f72ca23f936b6f3689c0c577a11a97895b4213bf
[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                     
7194                     alert('is android : ' + Roo.isAndroid);
7195                     alert('is ios : ' + Roo.isIOS);
7196
7197                     if(Roo.isAndroid){
7198                         alert('Is Android');
7199                         return Roo.get(document.documentElement);
7200                     }
7201
7202                     if(!Roo.isAndroid){
7203                         alert('not android');
7204                     }
7205
7206                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7207                 }
7208             }
7209             
7210             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7211         },
7212
7213         /**
7214          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7215          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7216          * @param {String} selector The simple selector to test
7217          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7218                 search as a number or element (defaults to 10 || document.body)
7219          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7220          */
7221         up : function(simpleSelector, maxDepth){
7222             return this.findParentNode(simpleSelector, maxDepth, true);
7223         },
7224
7225
7226
7227         /**
7228          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7229          * @param {String} selector The simple selector to test
7230          * @return {Boolean} True if this element matches the selector, else false
7231          */
7232         is : function(simpleSelector){
7233             return Roo.DomQuery.is(this.dom, simpleSelector);
7234         },
7235
7236         /**
7237          * Perform animation on this element.
7238          * @param {Object} args The YUI animation control args
7239          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7240          * @param {Function} onComplete (optional) Function to call when animation completes
7241          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7242          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7243          * @return {Roo.Element} this
7244          */
7245         animate : function(args, duration, onComplete, easing, animType){
7246             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7247             return this;
7248         },
7249
7250         /*
7251          * @private Internal animation call
7252          */
7253         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7254             animType = animType || 'run';
7255             opt = opt || {};
7256             var anim = Roo.lib.Anim[animType](
7257                 this.dom, args,
7258                 (opt.duration || defaultDur) || .35,
7259                 (opt.easing || defaultEase) || 'easeOut',
7260                 function(){
7261                     Roo.callback(cb, this);
7262                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7263                 },
7264                 this
7265             );
7266             opt.anim = anim;
7267             return anim;
7268         },
7269
7270         // private legacy anim prep
7271         preanim : function(a, i){
7272             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7273         },
7274
7275         /**
7276          * Removes worthless text nodes
7277          * @param {Boolean} forceReclean (optional) By default the element
7278          * keeps track if it has been cleaned already so
7279          * you can call this over and over. However, if you update the element and
7280          * need to force a reclean, you can pass true.
7281          */
7282         clean : function(forceReclean){
7283             if(this.isCleaned && forceReclean !== true){
7284                 return this;
7285             }
7286             var ns = /\S/;
7287             var d = this.dom, n = d.firstChild, ni = -1;
7288             while(n){
7289                 var nx = n.nextSibling;
7290                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7291                     d.removeChild(n);
7292                 }else{
7293                     n.nodeIndex = ++ni;
7294                 }
7295                 n = nx;
7296             }
7297             this.isCleaned = true;
7298             return this;
7299         },
7300
7301         // private
7302         calcOffsetsTo : function(el){
7303             el = Roo.get(el);
7304             var d = el.dom;
7305             var restorePos = false;
7306             if(el.getStyle('position') == 'static'){
7307                 el.position('relative');
7308                 restorePos = true;
7309             }
7310             var x = 0, y =0;
7311             var op = this.dom;
7312             while(op && op != d && op.tagName != 'HTML'){
7313                 x+= op.offsetLeft;
7314                 y+= op.offsetTop;
7315                 op = op.offsetParent;
7316             }
7317             if(restorePos){
7318                 el.position('static');
7319             }
7320             return [x, y];
7321         },
7322
7323         /**
7324          * Scrolls this element into view within the passed container.
7325          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7326          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7327          * @return {Roo.Element} this
7328          */
7329         scrollIntoView : function(container, hscroll){
7330             var c = Roo.getDom(container) || document.body;
7331             var el = this.dom;
7332
7333             var o = this.calcOffsetsTo(c),
7334                 l = o[0],
7335                 t = o[1],
7336                 b = t+el.offsetHeight,
7337                 r = l+el.offsetWidth;
7338
7339             var ch = c.clientHeight;
7340             var ct = parseInt(c.scrollTop, 10);
7341             var cl = parseInt(c.scrollLeft, 10);
7342             var cb = ct + ch;
7343             var cr = cl + c.clientWidth;
7344
7345             if(t < ct){
7346                 c.scrollTop = t;
7347             }else if(b > cb){
7348                 c.scrollTop = b-ch;
7349             }
7350
7351             if(hscroll !== false){
7352                 if(l < cl){
7353                     c.scrollLeft = l;
7354                 }else if(r > cr){
7355                     c.scrollLeft = r-c.clientWidth;
7356                 }
7357             }
7358             return this;
7359         },
7360
7361         // private
7362         scrollChildIntoView : function(child, hscroll){
7363             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7364         },
7365
7366         /**
7367          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7368          * the new height may not be available immediately.
7369          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7370          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7371          * @param {Function} onComplete (optional) Function to call when animation completes
7372          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7373          * @return {Roo.Element} this
7374          */
7375         autoHeight : function(animate, duration, onComplete, easing){
7376             var oldHeight = this.getHeight();
7377             this.clip();
7378             this.setHeight(1); // force clipping
7379             setTimeout(function(){
7380                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7381                 if(!animate){
7382                     this.setHeight(height);
7383                     this.unclip();
7384                     if(typeof onComplete == "function"){
7385                         onComplete();
7386                     }
7387                 }else{
7388                     this.setHeight(oldHeight); // restore original height
7389                     this.setHeight(height, animate, duration, function(){
7390                         this.unclip();
7391                         if(typeof onComplete == "function") { onComplete(); }
7392                     }.createDelegate(this), easing);
7393                 }
7394             }.createDelegate(this), 0);
7395             return this;
7396         },
7397
7398         /**
7399          * Returns true if this element is an ancestor of the passed element
7400          * @param {HTMLElement/String} el The element to check
7401          * @return {Boolean} True if this element is an ancestor of el, else false
7402          */
7403         contains : function(el){
7404             if(!el){return false;}
7405             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7406         },
7407
7408         /**
7409          * Checks whether the element is currently visible using both visibility and display properties.
7410          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7411          * @return {Boolean} True if the element is currently visible, else false
7412          */
7413         isVisible : function(deep) {
7414             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7415             if(deep !== true || !vis){
7416                 return vis;
7417             }
7418             var p = this.dom.parentNode;
7419             while(p && p.tagName.toLowerCase() != "body"){
7420                 if(!Roo.fly(p, '_isVisible').isVisible()){
7421                     return false;
7422                 }
7423                 p = p.parentNode;
7424             }
7425             return true;
7426         },
7427
7428         /**
7429          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7430          * @param {String} selector The CSS selector
7431          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7432          * @return {CompositeElement/CompositeElementLite} The composite element
7433          */
7434         select : function(selector, unique){
7435             return El.select(selector, unique, this.dom);
7436         },
7437
7438         /**
7439          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7440          * @param {String} selector The CSS selector
7441          * @return {Array} An array of the matched nodes
7442          */
7443         query : function(selector, unique){
7444             return Roo.DomQuery.select(selector, this.dom);
7445         },
7446
7447         /**
7448          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7449          * @param {String} selector The CSS selector
7450          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7451          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7452          */
7453         child : function(selector, returnDom){
7454             var n = Roo.DomQuery.selectNode(selector, this.dom);
7455             return returnDom ? n : Roo.get(n);
7456         },
7457
7458         /**
7459          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7460          * @param {String} selector The CSS selector
7461          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7462          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7463          */
7464         down : function(selector, returnDom){
7465             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7466             return returnDom ? n : Roo.get(n);
7467         },
7468
7469         /**
7470          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7471          * @param {String} group The group the DD object is member of
7472          * @param {Object} config The DD config object
7473          * @param {Object} overrides An object containing methods to override/implement on the DD object
7474          * @return {Roo.dd.DD} The DD object
7475          */
7476         initDD : function(group, config, overrides){
7477             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7478             return Roo.apply(dd, overrides);
7479         },
7480
7481         /**
7482          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7483          * @param {String} group The group the DDProxy object is member of
7484          * @param {Object} config The DDProxy config object
7485          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7486          * @return {Roo.dd.DDProxy} The DDProxy object
7487          */
7488         initDDProxy : function(group, config, overrides){
7489             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7490             return Roo.apply(dd, overrides);
7491         },
7492
7493         /**
7494          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7495          * @param {String} group The group the DDTarget object is member of
7496          * @param {Object} config The DDTarget config object
7497          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7498          * @return {Roo.dd.DDTarget} The DDTarget object
7499          */
7500         initDDTarget : function(group, config, overrides){
7501             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7502             return Roo.apply(dd, overrides);
7503         },
7504
7505         /**
7506          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7507          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7508          * @param {Boolean} visible Whether the element is visible
7509          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7510          * @return {Roo.Element} this
7511          */
7512          setVisible : function(visible, animate){
7513             if(!animate || !A){
7514                 if(this.visibilityMode == El.DISPLAY){
7515                     this.setDisplayed(visible);
7516                 }else{
7517                     this.fixDisplay();
7518                     this.dom.style.visibility = visible ? "visible" : "hidden";
7519                 }
7520             }else{
7521                 // closure for composites
7522                 var dom = this.dom;
7523                 var visMode = this.visibilityMode;
7524                 if(visible){
7525                     this.setOpacity(.01);
7526                     this.setVisible(true);
7527                 }
7528                 this.anim({opacity: { to: (visible?1:0) }},
7529                       this.preanim(arguments, 1),
7530                       null, .35, 'easeIn', function(){
7531                          if(!visible){
7532                              if(visMode == El.DISPLAY){
7533                                  dom.style.display = "none";
7534                              }else{
7535                                  dom.style.visibility = "hidden";
7536                              }
7537                              Roo.get(dom).setOpacity(1);
7538                          }
7539                      });
7540             }
7541             return this;
7542         },
7543
7544         /**
7545          * Returns true if display is not "none"
7546          * @return {Boolean}
7547          */
7548         isDisplayed : function() {
7549             return this.getStyle("display") != "none";
7550         },
7551
7552         /**
7553          * Toggles the element's visibility or display, depending on visibility mode.
7554          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7555          * @return {Roo.Element} this
7556          */
7557         toggle : function(animate){
7558             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7559             return this;
7560         },
7561
7562         /**
7563          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7564          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7565          * @return {Roo.Element} this
7566          */
7567         setDisplayed : function(value) {
7568             if(typeof value == "boolean"){
7569                value = value ? this.originalDisplay : "none";
7570             }
7571             this.setStyle("display", value);
7572             return this;
7573         },
7574
7575         /**
7576          * Tries to focus the element. Any exceptions are caught and ignored.
7577          * @return {Roo.Element} this
7578          */
7579         focus : function() {
7580             try{
7581                 this.dom.focus();
7582             }catch(e){}
7583             return this;
7584         },
7585
7586         /**
7587          * Tries to blur the element. Any exceptions are caught and ignored.
7588          * @return {Roo.Element} this
7589          */
7590         blur : function() {
7591             try{
7592                 this.dom.blur();
7593             }catch(e){}
7594             return this;
7595         },
7596
7597         /**
7598          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7599          * @param {String/Array} className The CSS class to add, or an array of classes
7600          * @return {Roo.Element} this
7601          */
7602         addClass : function(className){
7603             if(className instanceof Array){
7604                 for(var i = 0, len = className.length; i < len; i++) {
7605                     this.addClass(className[i]);
7606                 }
7607             }else{
7608                 if(className && !this.hasClass(className)){
7609                     this.dom.className = this.dom.className + " " + className;
7610                 }
7611             }
7612             return this;
7613         },
7614
7615         /**
7616          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7617          * @param {String/Array} className The CSS class to add, or an array of classes
7618          * @return {Roo.Element} this
7619          */
7620         radioClass : function(className){
7621             var siblings = this.dom.parentNode.childNodes;
7622             for(var i = 0; i < siblings.length; i++) {
7623                 var s = siblings[i];
7624                 if(s.nodeType == 1){
7625                     Roo.get(s).removeClass(className);
7626                 }
7627             }
7628             this.addClass(className);
7629             return this;
7630         },
7631
7632         /**
7633          * Removes one or more CSS classes from the element.
7634          * @param {String/Array} className The CSS class to remove, or an array of classes
7635          * @return {Roo.Element} this
7636          */
7637         removeClass : function(className){
7638             if(!className || !this.dom.className){
7639                 return this;
7640             }
7641             if(className instanceof Array){
7642                 for(var i = 0, len = className.length; i < len; i++) {
7643                     this.removeClass(className[i]);
7644                 }
7645             }else{
7646                 if(this.hasClass(className)){
7647                     var re = this.classReCache[className];
7648                     if (!re) {
7649                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7650                        this.classReCache[className] = re;
7651                     }
7652                     this.dom.className =
7653                         this.dom.className.replace(re, " ");
7654                 }
7655             }
7656             return this;
7657         },
7658
7659         // private
7660         classReCache: {},
7661
7662         /**
7663          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7664          * @param {String} className The CSS class to toggle
7665          * @return {Roo.Element} this
7666          */
7667         toggleClass : function(className){
7668             if(this.hasClass(className)){
7669                 this.removeClass(className);
7670             }else{
7671                 this.addClass(className);
7672             }
7673             return this;
7674         },
7675
7676         /**
7677          * Checks if the specified CSS class exists on this element's DOM node.
7678          * @param {String} className The CSS class to check for
7679          * @return {Boolean} True if the class exists, else false
7680          */
7681         hasClass : function(className){
7682             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7683         },
7684
7685         /**
7686          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7687          * @param {String} oldClassName The CSS class to replace
7688          * @param {String} newClassName The replacement CSS class
7689          * @return {Roo.Element} this
7690          */
7691         replaceClass : function(oldClassName, newClassName){
7692             this.removeClass(oldClassName);
7693             this.addClass(newClassName);
7694             return this;
7695         },
7696
7697         /**
7698          * Returns an object with properties matching the styles requested.
7699          * For example, el.getStyles('color', 'font-size', 'width') might return
7700          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7701          * @param {String} style1 A style name
7702          * @param {String} style2 A style name
7703          * @param {String} etc.
7704          * @return {Object} The style object
7705          */
7706         getStyles : function(){
7707             var a = arguments, len = a.length, r = {};
7708             for(var i = 0; i < len; i++){
7709                 r[a[i]] = this.getStyle(a[i]);
7710             }
7711             return r;
7712         },
7713
7714         /**
7715          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7716          * @param {String} property The style property whose value is returned.
7717          * @return {String} The current value of the style property for this element.
7718          */
7719         getStyle : function(){
7720             return view && view.getComputedStyle ?
7721                 function(prop){
7722                     var el = this.dom, v, cs, camel;
7723                     if(prop == 'float'){
7724                         prop = "cssFloat";
7725                     }
7726                     if(el.style && (v = el.style[prop])){
7727                         return v;
7728                     }
7729                     if(cs = view.getComputedStyle(el, "")){
7730                         if(!(camel = propCache[prop])){
7731                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7732                         }
7733                         return cs[camel];
7734                     }
7735                     return null;
7736                 } :
7737                 function(prop){
7738                     var el = this.dom, v, cs, camel;
7739                     if(prop == 'opacity'){
7740                         if(typeof el.style.filter == 'string'){
7741                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7742                             if(m){
7743                                 var fv = parseFloat(m[1]);
7744                                 if(!isNaN(fv)){
7745                                     return fv ? fv / 100 : 0;
7746                                 }
7747                             }
7748                         }
7749                         return 1;
7750                     }else if(prop == 'float'){
7751                         prop = "styleFloat";
7752                     }
7753                     if(!(camel = propCache[prop])){
7754                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7755                     }
7756                     if(v = el.style[camel]){
7757                         return v;
7758                     }
7759                     if(cs = el.currentStyle){
7760                         return cs[camel];
7761                     }
7762                     return null;
7763                 };
7764         }(),
7765
7766         /**
7767          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7768          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7769          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7770          * @return {Roo.Element} this
7771          */
7772         setStyle : function(prop, value){
7773             if(typeof prop == "string"){
7774                 
7775                 if (prop == 'float') {
7776                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7777                     return this;
7778                 }
7779                 
7780                 var camel;
7781                 if(!(camel = propCache[prop])){
7782                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7783                 }
7784                 
7785                 if(camel == 'opacity') {
7786                     this.setOpacity(value);
7787                 }else{
7788                     this.dom.style[camel] = value;
7789                 }
7790             }else{
7791                 for(var style in prop){
7792                     if(typeof prop[style] != "function"){
7793                        this.setStyle(style, prop[style]);
7794                     }
7795                 }
7796             }
7797             return this;
7798         },
7799
7800         /**
7801          * More flexible version of {@link #setStyle} for setting style properties.
7802          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7803          * a function which returns such a specification.
7804          * @return {Roo.Element} this
7805          */
7806         applyStyles : function(style){
7807             Roo.DomHelper.applyStyles(this.dom, style);
7808             return this;
7809         },
7810
7811         /**
7812           * 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).
7813           * @return {Number} The X position of the element
7814           */
7815         getX : function(){
7816             return D.getX(this.dom);
7817         },
7818
7819         /**
7820           * 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).
7821           * @return {Number} The Y position of the element
7822           */
7823         getY : function(){
7824             return D.getY(this.dom);
7825         },
7826
7827         /**
7828           * 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).
7829           * @return {Array} The XY position of the element
7830           */
7831         getXY : function(){
7832             return D.getXY(this.dom);
7833         },
7834
7835         /**
7836          * 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).
7837          * @param {Number} The X position of the element
7838          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7839          * @return {Roo.Element} this
7840          */
7841         setX : function(x, animate){
7842             if(!animate || !A){
7843                 D.setX(this.dom, x);
7844             }else{
7845                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7846             }
7847             return this;
7848         },
7849
7850         /**
7851          * 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).
7852          * @param {Number} The Y position of the element
7853          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7854          * @return {Roo.Element} this
7855          */
7856         setY : function(y, animate){
7857             if(!animate || !A){
7858                 D.setY(this.dom, y);
7859             }else{
7860                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7861             }
7862             return this;
7863         },
7864
7865         /**
7866          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7867          * @param {String} left The left CSS property value
7868          * @return {Roo.Element} this
7869          */
7870         setLeft : function(left){
7871             this.setStyle("left", this.addUnits(left));
7872             return this;
7873         },
7874
7875         /**
7876          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7877          * @param {String} top The top CSS property value
7878          * @return {Roo.Element} this
7879          */
7880         setTop : function(top){
7881             this.setStyle("top", this.addUnits(top));
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's CSS right style.
7887          * @param {String} right The right CSS property value
7888          * @return {Roo.Element} this
7889          */
7890         setRight : function(right){
7891             this.setStyle("right", this.addUnits(right));
7892             return this;
7893         },
7894
7895         /**
7896          * Sets the element's CSS bottom style.
7897          * @param {String} bottom The bottom CSS property value
7898          * @return {Roo.Element} this
7899          */
7900         setBottom : function(bottom){
7901             this.setStyle("bottom", this.addUnits(bottom));
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7907          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7908          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7909          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setXY : function(pos, animate){
7913             if(!animate || !A){
7914                 D.setXY(this.dom, pos);
7915             }else{
7916                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7917             }
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         setLocation : function(x, y, animate){
7930             this.setXY([x, y], this.preanim(arguments, 2));
7931             return this;
7932         },
7933
7934         /**
7935          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7936          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7937          * @param {Number} x X value for new position (coordinates are page-based)
7938          * @param {Number} y Y value for new position (coordinates are page-based)
7939          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7940          * @return {Roo.Element} this
7941          */
7942         moveTo : function(x, y, animate){
7943             this.setXY([x, y], this.preanim(arguments, 2));
7944             return this;
7945         },
7946
7947         /**
7948          * Returns the region of the given element.
7949          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7950          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7951          */
7952         getRegion : function(){
7953             return D.getRegion(this.dom);
7954         },
7955
7956         /**
7957          * Returns the offset height of the element
7958          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7959          * @return {Number} The element's height
7960          */
7961         getHeight : function(contentHeight){
7962             var h = this.dom.offsetHeight || 0;
7963             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7964         },
7965
7966         /**
7967          * Returns the offset width of the element
7968          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7969          * @return {Number} The element's width
7970          */
7971         getWidth : function(contentWidth){
7972             var w = this.dom.offsetWidth || 0;
7973             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7974         },
7975
7976         /**
7977          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7978          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7979          * if a height has not been set using CSS.
7980          * @return {Number}
7981          */
7982         getComputedHeight : function(){
7983             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7984             if(!h){
7985                 h = parseInt(this.getStyle('height'), 10) || 0;
7986                 if(!this.isBorderBox()){
7987                     h += this.getFrameWidth('tb');
7988                 }
7989             }
7990             return h;
7991         },
7992
7993         /**
7994          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7995          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7996          * if a width has not been set using CSS.
7997          * @return {Number}
7998          */
7999         getComputedWidth : function(){
8000             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8001             if(!w){
8002                 w = parseInt(this.getStyle('width'), 10) || 0;
8003                 if(!this.isBorderBox()){
8004                     w += this.getFrameWidth('lr');
8005                 }
8006             }
8007             return w;
8008         },
8009
8010         /**
8011          * Returns the size of the element.
8012          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8013          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8014          */
8015         getSize : function(contentSize){
8016             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8017         },
8018
8019         /**
8020          * Returns the width and height of the viewport.
8021          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8022          */
8023         getViewSize : function(){
8024             var d = this.dom, doc = document, aw = 0, ah = 0;
8025             if(d == doc || d == doc.body){
8026                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8027             }else{
8028                 return {
8029                     width : d.clientWidth,
8030                     height: d.clientHeight
8031                 };
8032             }
8033         },
8034
8035         /**
8036          * Returns the value of the "value" attribute
8037          * @param {Boolean} asNumber true to parse the value as a number
8038          * @return {String/Number}
8039          */
8040         getValue : function(asNumber){
8041             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8042         },
8043
8044         // private
8045         adjustWidth : function(width){
8046             if(typeof width == "number"){
8047                 if(this.autoBoxAdjust && !this.isBorderBox()){
8048                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8049                 }
8050                 if(width < 0){
8051                     width = 0;
8052                 }
8053             }
8054             return width;
8055         },
8056
8057         // private
8058         adjustHeight : function(height){
8059             if(typeof height == "number"){
8060                if(this.autoBoxAdjust && !this.isBorderBox()){
8061                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8062                }
8063                if(height < 0){
8064                    height = 0;
8065                }
8066             }
8067             return height;
8068         },
8069
8070         /**
8071          * Set the width of the element
8072          * @param {Number} width The new width
8073          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8074          * @return {Roo.Element} this
8075          */
8076         setWidth : function(width, animate){
8077             width = this.adjustWidth(width);
8078             if(!animate || !A){
8079                 this.dom.style.width = this.addUnits(width);
8080             }else{
8081                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8082             }
8083             return this;
8084         },
8085
8086         /**
8087          * Set the height of the element
8088          * @param {Number} height The new height
8089          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8090          * @return {Roo.Element} this
8091          */
8092          setHeight : function(height, animate){
8093             height = this.adjustHeight(height);
8094             if(!animate || !A){
8095                 this.dom.style.height = this.addUnits(height);
8096             }else{
8097                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8098             }
8099             return this;
8100         },
8101
8102         /**
8103          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8104          * @param {Number} width The new width
8105          * @param {Number} height The new height
8106          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8107          * @return {Roo.Element} this
8108          */
8109          setSize : function(width, height, animate){
8110             if(typeof width == "object"){ // in case of object from getSize()
8111                 height = width.height; width = width.width;
8112             }
8113             width = this.adjustWidth(width); height = this.adjustHeight(height);
8114             if(!animate || !A){
8115                 this.dom.style.width = this.addUnits(width);
8116                 this.dom.style.height = this.addUnits(height);
8117             }else{
8118                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8119             }
8120             return this;
8121         },
8122
8123         /**
8124          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8125          * @param {Number} x X value for new position (coordinates are page-based)
8126          * @param {Number} y Y value for new position (coordinates are page-based)
8127          * @param {Number} width The new width
8128          * @param {Number} height The new height
8129          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8130          * @return {Roo.Element} this
8131          */
8132         setBounds : function(x, y, width, height, animate){
8133             if(!animate || !A){
8134                 this.setSize(width, height);
8135                 this.setLocation(x, y);
8136             }else{
8137                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8138                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8139                               this.preanim(arguments, 4), 'motion');
8140             }
8141             return this;
8142         },
8143
8144         /**
8145          * 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.
8146          * @param {Roo.lib.Region} region The region to fill
8147          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8148          * @return {Roo.Element} this
8149          */
8150         setRegion : function(region, animate){
8151             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8152             return this;
8153         },
8154
8155         /**
8156          * Appends an event handler
8157          *
8158          * @param {String}   eventName     The type of event to append
8159          * @param {Function} fn        The method the event invokes
8160          * @param {Object} scope       (optional) The scope (this object) of the fn
8161          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8162          */
8163         addListener : function(eventName, fn, scope, options){
8164             if (this.dom) {
8165                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8166             }
8167         },
8168
8169         /**
8170          * Removes an event handler from this element
8171          * @param {String} eventName the type of event to remove
8172          * @param {Function} fn the method the event invokes
8173          * @return {Roo.Element} this
8174          */
8175         removeListener : function(eventName, fn){
8176             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8177             return this;
8178         },
8179
8180         /**
8181          * Removes all previous added listeners from this element
8182          * @return {Roo.Element} this
8183          */
8184         removeAllListeners : function(){
8185             E.purgeElement(this.dom);
8186             return this;
8187         },
8188
8189         relayEvent : function(eventName, observable){
8190             this.on(eventName, function(e){
8191                 observable.fireEvent(eventName, e);
8192             });
8193         },
8194
8195         /**
8196          * Set the opacity of the element
8197          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8198          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8199          * @return {Roo.Element} this
8200          */
8201          setOpacity : function(opacity, animate){
8202             if(!animate || !A){
8203                 var s = this.dom.style;
8204                 if(Roo.isIE){
8205                     s.zoom = 1;
8206                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8207                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8208                 }else{
8209                     s.opacity = opacity;
8210                 }
8211             }else{
8212                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8213             }
8214             return this;
8215         },
8216
8217         /**
8218          * Gets the left X coordinate
8219          * @param {Boolean} local True to get the local css position instead of page coordinate
8220          * @return {Number}
8221          */
8222         getLeft : function(local){
8223             if(!local){
8224                 return this.getX();
8225             }else{
8226                 return parseInt(this.getStyle("left"), 10) || 0;
8227             }
8228         },
8229
8230         /**
8231          * Gets the right X coordinate of the element (element X position + element width)
8232          * @param {Boolean} local True to get the local css position instead of page coordinate
8233          * @return {Number}
8234          */
8235         getRight : function(local){
8236             if(!local){
8237                 return this.getX() + this.getWidth();
8238             }else{
8239                 return (this.getLeft(true) + this.getWidth()) || 0;
8240             }
8241         },
8242
8243         /**
8244          * Gets the top Y coordinate
8245          * @param {Boolean} local True to get the local css position instead of page coordinate
8246          * @return {Number}
8247          */
8248         getTop : function(local) {
8249             if(!local){
8250                 return this.getY();
8251             }else{
8252                 return parseInt(this.getStyle("top"), 10) || 0;
8253             }
8254         },
8255
8256         /**
8257          * Gets the bottom Y coordinate of the element (element Y position + element height)
8258          * @param {Boolean} local True to get the local css position instead of page coordinate
8259          * @return {Number}
8260          */
8261         getBottom : function(local){
8262             if(!local){
8263                 return this.getY() + this.getHeight();
8264             }else{
8265                 return (this.getTop(true) + this.getHeight()) || 0;
8266             }
8267         },
8268
8269         /**
8270         * Initializes positioning on this element. If a desired position is not passed, it will make the
8271         * the element positioned relative IF it is not already positioned.
8272         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8273         * @param {Number} zIndex (optional) The zIndex to apply
8274         * @param {Number} x (optional) Set the page X position
8275         * @param {Number} y (optional) Set the page Y position
8276         */
8277         position : function(pos, zIndex, x, y){
8278             if(!pos){
8279                if(this.getStyle('position') == 'static'){
8280                    this.setStyle('position', 'relative');
8281                }
8282             }else{
8283                 this.setStyle("position", pos);
8284             }
8285             if(zIndex){
8286                 this.setStyle("z-index", zIndex);
8287             }
8288             if(x !== undefined && y !== undefined){
8289                 this.setXY([x, y]);
8290             }else if(x !== undefined){
8291                 this.setX(x);
8292             }else if(y !== undefined){
8293                 this.setY(y);
8294             }
8295         },
8296
8297         /**
8298         * Clear positioning back to the default when the document was loaded
8299         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8300         * @return {Roo.Element} this
8301          */
8302         clearPositioning : function(value){
8303             value = value ||'';
8304             this.setStyle({
8305                 "left": value,
8306                 "right": value,
8307                 "top": value,
8308                 "bottom": value,
8309                 "z-index": "",
8310                 "position" : "static"
8311             });
8312             return this;
8313         },
8314
8315         /**
8316         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8317         * snapshot before performing an update and then restoring the element.
8318         * @return {Object}
8319         */
8320         getPositioning : function(){
8321             var l = this.getStyle("left");
8322             var t = this.getStyle("top");
8323             return {
8324                 "position" : this.getStyle("position"),
8325                 "left" : l,
8326                 "right" : l ? "" : this.getStyle("right"),
8327                 "top" : t,
8328                 "bottom" : t ? "" : this.getStyle("bottom"),
8329                 "z-index" : this.getStyle("z-index")
8330             };
8331         },
8332
8333         /**
8334          * Gets the width of the border(s) for the specified side(s)
8335          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8336          * passing lr would get the border (l)eft width + the border (r)ight width.
8337          * @return {Number} The width of the sides passed added together
8338          */
8339         getBorderWidth : function(side){
8340             return this.addStyles(side, El.borders);
8341         },
8342
8343         /**
8344          * Gets the width of the padding(s) for the specified side(s)
8345          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8346          * passing lr would get the padding (l)eft + the padding (r)ight.
8347          * @return {Number} The padding of the sides passed added together
8348          */
8349         getPadding : function(side){
8350             return this.addStyles(side, El.paddings);
8351         },
8352
8353         /**
8354         * Set positioning with an object returned by getPositioning().
8355         * @param {Object} posCfg
8356         * @return {Roo.Element} this
8357          */
8358         setPositioning : function(pc){
8359             this.applyStyles(pc);
8360             if(pc.right == "auto"){
8361                 this.dom.style.right = "";
8362             }
8363             if(pc.bottom == "auto"){
8364                 this.dom.style.bottom = "";
8365             }
8366             return this;
8367         },
8368
8369         // private
8370         fixDisplay : function(){
8371             if(this.getStyle("display") == "none"){
8372                 this.setStyle("visibility", "hidden");
8373                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8374                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8375                     this.setStyle("display", "block");
8376                 }
8377             }
8378         },
8379
8380         /**
8381          * Quick set left and top adding default units
8382          * @param {String} left The left CSS property value
8383          * @param {String} top The top CSS property value
8384          * @return {Roo.Element} this
8385          */
8386          setLeftTop : function(left, top){
8387             this.dom.style.left = this.addUnits(left);
8388             this.dom.style.top = this.addUnits(top);
8389             return this;
8390         },
8391
8392         /**
8393          * Move this element relative to its current position.
8394          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8395          * @param {Number} distance How far to move the element in pixels
8396          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8397          * @return {Roo.Element} this
8398          */
8399          move : function(direction, distance, animate){
8400             var xy = this.getXY();
8401             direction = direction.toLowerCase();
8402             switch(direction){
8403                 case "l":
8404                 case "left":
8405                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8406                     break;
8407                case "r":
8408                case "right":
8409                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8410                     break;
8411                case "t":
8412                case "top":
8413                case "up":
8414                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8415                     break;
8416                case "b":
8417                case "bottom":
8418                case "down":
8419                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8420                     break;
8421             }
8422             return this;
8423         },
8424
8425         /**
8426          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8427          * @return {Roo.Element} this
8428          */
8429         clip : function(){
8430             if(!this.isClipped){
8431                this.isClipped = true;
8432                this.originalClip = {
8433                    "o": this.getStyle("overflow"),
8434                    "x": this.getStyle("overflow-x"),
8435                    "y": this.getStyle("overflow-y")
8436                };
8437                this.setStyle("overflow", "hidden");
8438                this.setStyle("overflow-x", "hidden");
8439                this.setStyle("overflow-y", "hidden");
8440             }
8441             return this;
8442         },
8443
8444         /**
8445          *  Return clipping (overflow) to original clipping before clip() was called
8446          * @return {Roo.Element} this
8447          */
8448         unclip : function(){
8449             if(this.isClipped){
8450                 this.isClipped = false;
8451                 var o = this.originalClip;
8452                 if(o.o){this.setStyle("overflow", o.o);}
8453                 if(o.x){this.setStyle("overflow-x", o.x);}
8454                 if(o.y){this.setStyle("overflow-y", o.y);}
8455             }
8456             return this;
8457         },
8458
8459
8460         /**
8461          * Gets the x,y coordinates specified by the anchor position on the element.
8462          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8463          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8464          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8465          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8466          * @return {Array} [x, y] An array containing the element's x and y coordinates
8467          */
8468         getAnchorXY : function(anchor, local, s){
8469             //Passing a different size is useful for pre-calculating anchors,
8470             //especially for anchored animations that change the el size.
8471
8472             var w, h, vp = false;
8473             if(!s){
8474                 var d = this.dom;
8475                 if(d == document.body || d == document){
8476                     vp = true;
8477                     w = D.getViewWidth(); h = D.getViewHeight();
8478                 }else{
8479                     w = this.getWidth(); h = this.getHeight();
8480                 }
8481             }else{
8482                 w = s.width;  h = s.height;
8483             }
8484             var x = 0, y = 0, r = Math.round;
8485             switch((anchor || "tl").toLowerCase()){
8486                 case "c":
8487                     x = r(w*.5);
8488                     y = r(h*.5);
8489                 break;
8490                 case "t":
8491                     x = r(w*.5);
8492                     y = 0;
8493                 break;
8494                 case "l":
8495                     x = 0;
8496                     y = r(h*.5);
8497                 break;
8498                 case "r":
8499                     x = w;
8500                     y = r(h*.5);
8501                 break;
8502                 case "b":
8503                     x = r(w*.5);
8504                     y = h;
8505                 break;
8506                 case "tl":
8507                     x = 0;
8508                     y = 0;
8509                 break;
8510                 case "bl":
8511                     x = 0;
8512                     y = h;
8513                 break;
8514                 case "br":
8515                     x = w;
8516                     y = h;
8517                 break;
8518                 case "tr":
8519                     x = w;
8520                     y = 0;
8521                 break;
8522             }
8523             if(local === true){
8524                 return [x, y];
8525             }
8526             if(vp){
8527                 var sc = this.getScroll();
8528                 return [x + sc.left, y + sc.top];
8529             }
8530             //Add the element's offset xy
8531             var o = this.getXY();
8532             return [x+o[0], y+o[1]];
8533         },
8534
8535         /**
8536          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8537          * supported position values.
8538          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8539          * @param {String} position The position to align to.
8540          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8541          * @return {Array} [x, y]
8542          */
8543         getAlignToXY : function(el, p, o){
8544             el = Roo.get(el);
8545             var d = this.dom;
8546             if(!el.dom){
8547                 throw "Element.alignTo with an element that doesn't exist";
8548             }
8549             var c = false; //constrain to viewport
8550             var p1 = "", p2 = "";
8551             o = o || [0,0];
8552
8553             if(!p){
8554                 p = "tl-bl";
8555             }else if(p == "?"){
8556                 p = "tl-bl?";
8557             }else if(p.indexOf("-") == -1){
8558                 p = "tl-" + p;
8559             }
8560             p = p.toLowerCase();
8561             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8562             if(!m){
8563                throw "Element.alignTo with an invalid alignment " + p;
8564             }
8565             p1 = m[1]; p2 = m[2]; c = !!m[3];
8566
8567             //Subtract the aligned el's internal xy from the target's offset xy
8568             //plus custom offset to get the aligned el's new offset xy
8569             var a1 = this.getAnchorXY(p1, true);
8570             var a2 = el.getAnchorXY(p2, false);
8571             var x = a2[0] - a1[0] + o[0];
8572             var y = a2[1] - a1[1] + o[1];
8573             if(c){
8574                 //constrain the aligned el to viewport if necessary
8575                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8576                 // 5px of margin for ie
8577                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8578
8579                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8580                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8581                 //otherwise swap the aligned el to the opposite border of the target.
8582                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8583                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8584                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8585                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8586
8587                var doc = document;
8588                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8589                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8590
8591                if((x+w) > dw + scrollX){
8592                     x = swapX ? r.left-w : dw+scrollX-w;
8593                 }
8594                if(x < scrollX){
8595                    x = swapX ? r.right : scrollX;
8596                }
8597                if((y+h) > dh + scrollY){
8598                     y = swapY ? r.top-h : dh+scrollY-h;
8599                 }
8600                if (y < scrollY){
8601                    y = swapY ? r.bottom : scrollY;
8602                }
8603             }
8604             return [x,y];
8605         },
8606
8607         // private
8608         getConstrainToXY : function(){
8609             var os = {top:0, left:0, bottom:0, right: 0};
8610
8611             return function(el, local, offsets, proposedXY){
8612                 el = Roo.get(el);
8613                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8614
8615                 var vw, vh, vx = 0, vy = 0;
8616                 if(el.dom == document.body || el.dom == document){
8617                     vw = Roo.lib.Dom.getViewWidth();
8618                     vh = Roo.lib.Dom.getViewHeight();
8619                 }else{
8620                     vw = el.dom.clientWidth;
8621                     vh = el.dom.clientHeight;
8622                     if(!local){
8623                         var vxy = el.getXY();
8624                         vx = vxy[0];
8625                         vy = vxy[1];
8626                     }
8627                 }
8628
8629                 var s = el.getScroll();
8630
8631                 vx += offsets.left + s.left;
8632                 vy += offsets.top + s.top;
8633
8634                 vw -= offsets.right;
8635                 vh -= offsets.bottom;
8636
8637                 var vr = vx+vw;
8638                 var vb = vy+vh;
8639
8640                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8641                 var x = xy[0], y = xy[1];
8642                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8643
8644                 // only move it if it needs it
8645                 var moved = false;
8646
8647                 // first validate right/bottom
8648                 if((x + w) > vr){
8649                     x = vr - w;
8650                     moved = true;
8651                 }
8652                 if((y + h) > vb){
8653                     y = vb - h;
8654                     moved = true;
8655                 }
8656                 // then make sure top/left isn't negative
8657                 if(x < vx){
8658                     x = vx;
8659                     moved = true;
8660                 }
8661                 if(y < vy){
8662                     y = vy;
8663                     moved = true;
8664                 }
8665                 return moved ? [x, y] : false;
8666             };
8667         }(),
8668
8669         // private
8670         adjustForConstraints : function(xy, parent, offsets){
8671             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8672         },
8673
8674         /**
8675          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8676          * document it aligns it to the viewport.
8677          * The position parameter is optional, and can be specified in any one of the following formats:
8678          * <ul>
8679          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8680          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8681          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8682          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8683          *   <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
8684          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8685          * </ul>
8686          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8687          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8688          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8689          * that specified in order to enforce the viewport constraints.
8690          * Following are all of the supported anchor positions:
8691     <pre>
8692     Value  Description
8693     -----  -----------------------------
8694     tl     The top left corner (default)
8695     t      The center of the top edge
8696     tr     The top right corner
8697     l      The center of the left edge
8698     c      In the center of the element
8699     r      The center of the right edge
8700     bl     The bottom left corner
8701     b      The center of the bottom edge
8702     br     The bottom right corner
8703     </pre>
8704     Example Usage:
8705     <pre><code>
8706     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8707     el.alignTo("other-el");
8708
8709     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8710     el.alignTo("other-el", "tr?");
8711
8712     // align the bottom right corner of el with the center left edge of other-el
8713     el.alignTo("other-el", "br-l?");
8714
8715     // align the center of el with the bottom left corner of other-el and
8716     // adjust the x position by -6 pixels (and the y position by 0)
8717     el.alignTo("other-el", "c-bl", [-6, 0]);
8718     </code></pre>
8719          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8720          * @param {String} position The position to align to.
8721          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8722          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8723          * @return {Roo.Element} this
8724          */
8725         alignTo : function(element, position, offsets, animate){
8726             var xy = this.getAlignToXY(element, position, offsets);
8727             this.setXY(xy, this.preanim(arguments, 3));
8728             return this;
8729         },
8730
8731         /**
8732          * Anchors an element to another element and realigns it when the window is resized.
8733          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8734          * @param {String} position The position to align to.
8735          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8736          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8737          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8738          * is a number, it is used as the buffer delay (defaults to 50ms).
8739          * @param {Function} callback The function to call after the animation finishes
8740          * @return {Roo.Element} this
8741          */
8742         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8743             var action = function(){
8744                 this.alignTo(el, alignment, offsets, animate);
8745                 Roo.callback(callback, this);
8746             };
8747             Roo.EventManager.onWindowResize(action, this);
8748             var tm = typeof monitorScroll;
8749             if(tm != 'undefined'){
8750                 Roo.EventManager.on(window, 'scroll', action, this,
8751                     {buffer: tm == 'number' ? monitorScroll : 50});
8752             }
8753             action.call(this); // align immediately
8754             return this;
8755         },
8756         /**
8757          * Clears any opacity settings from this element. Required in some cases for IE.
8758          * @return {Roo.Element} this
8759          */
8760         clearOpacity : function(){
8761             if (window.ActiveXObject) {
8762                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8763                     this.dom.style.filter = "";
8764                 }
8765             } else {
8766                 this.dom.style.opacity = "";
8767                 this.dom.style["-moz-opacity"] = "";
8768                 this.dom.style["-khtml-opacity"] = "";
8769             }
8770             return this;
8771         },
8772
8773         /**
8774          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8775          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8776          * @return {Roo.Element} this
8777          */
8778         hide : function(animate){
8779             this.setVisible(false, this.preanim(arguments, 0));
8780             return this;
8781         },
8782
8783         /**
8784         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8785         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8786          * @return {Roo.Element} this
8787          */
8788         show : function(animate){
8789             this.setVisible(true, this.preanim(arguments, 0));
8790             return this;
8791         },
8792
8793         /**
8794          * @private Test if size has a unit, otherwise appends the default
8795          */
8796         addUnits : function(size){
8797             return Roo.Element.addUnits(size, this.defaultUnit);
8798         },
8799
8800         /**
8801          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8802          * @return {Roo.Element} this
8803          */
8804         beginMeasure : function(){
8805             var el = this.dom;
8806             if(el.offsetWidth || el.offsetHeight){
8807                 return this; // offsets work already
8808             }
8809             var changed = [];
8810             var p = this.dom, b = document.body; // start with this element
8811             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8812                 var pe = Roo.get(p);
8813                 if(pe.getStyle('display') == 'none'){
8814                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8815                     p.style.visibility = "hidden";
8816                     p.style.display = "block";
8817                 }
8818                 p = p.parentNode;
8819             }
8820             this._measureChanged = changed;
8821             return this;
8822
8823         },
8824
8825         /**
8826          * Restores displays to before beginMeasure was called
8827          * @return {Roo.Element} this
8828          */
8829         endMeasure : function(){
8830             var changed = this._measureChanged;
8831             if(changed){
8832                 for(var i = 0, len = changed.length; i < len; i++) {
8833                     var r = changed[i];
8834                     r.el.style.visibility = r.visibility;
8835                     r.el.style.display = "none";
8836                 }
8837                 this._measureChanged = null;
8838             }
8839             return this;
8840         },
8841
8842         /**
8843         * Update the innerHTML of this element, optionally searching for and processing scripts
8844         * @param {String} html The new HTML
8845         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8846         * @param {Function} callback For async script loading you can be noticed when the update completes
8847         * @return {Roo.Element} this
8848          */
8849         update : function(html, loadScripts, callback){
8850             if(typeof html == "undefined"){
8851                 html = "";
8852             }
8853             if(loadScripts !== true){
8854                 this.dom.innerHTML = html;
8855                 if(typeof callback == "function"){
8856                     callback();
8857                 }
8858                 return this;
8859             }
8860             var id = Roo.id();
8861             var dom = this.dom;
8862
8863             html += '<span id="' + id + '"></span>';
8864
8865             E.onAvailable(id, function(){
8866                 var hd = document.getElementsByTagName("head")[0];
8867                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8868                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8869                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8870
8871                 var match;
8872                 while(match = re.exec(html)){
8873                     var attrs = match[1];
8874                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8875                     if(srcMatch && srcMatch[2]){
8876                        var s = document.createElement("script");
8877                        s.src = srcMatch[2];
8878                        var typeMatch = attrs.match(typeRe);
8879                        if(typeMatch && typeMatch[2]){
8880                            s.type = typeMatch[2];
8881                        }
8882                        hd.appendChild(s);
8883                     }else if(match[2] && match[2].length > 0){
8884                         if(window.execScript) {
8885                            window.execScript(match[2]);
8886                         } else {
8887                             /**
8888                              * eval:var:id
8889                              * eval:var:dom
8890                              * eval:var:html
8891                              * 
8892                              */
8893                            window.eval(match[2]);
8894                         }
8895                     }
8896                 }
8897                 var el = document.getElementById(id);
8898                 if(el){el.parentNode.removeChild(el);}
8899                 if(typeof callback == "function"){
8900                     callback();
8901                 }
8902             });
8903             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8904             return this;
8905         },
8906
8907         /**
8908          * Direct access to the UpdateManager update() method (takes the same parameters).
8909          * @param {String/Function} url The url for this request or a function to call to get the url
8910          * @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}
8911          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8912          * @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.
8913          * @return {Roo.Element} this
8914          */
8915         load : function(){
8916             var um = this.getUpdateManager();
8917             um.update.apply(um, arguments);
8918             return this;
8919         },
8920
8921         /**
8922         * Gets this element's UpdateManager
8923         * @return {Roo.UpdateManager} The UpdateManager
8924         */
8925         getUpdateManager : function(){
8926             if(!this.updateManager){
8927                 this.updateManager = new Roo.UpdateManager(this);
8928             }
8929             return this.updateManager;
8930         },
8931
8932         /**
8933          * Disables text selection for this element (normalized across browsers)
8934          * @return {Roo.Element} this
8935          */
8936         unselectable : function(){
8937             this.dom.unselectable = "on";
8938             this.swallowEvent("selectstart", true);
8939             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8940             this.addClass("x-unselectable");
8941             return this;
8942         },
8943
8944         /**
8945         * Calculates the x, y to center this element on the screen
8946         * @return {Array} The x, y values [x, y]
8947         */
8948         getCenterXY : function(){
8949             return this.getAlignToXY(document, 'c-c');
8950         },
8951
8952         /**
8953         * Centers the Element in either the viewport, or another Element.
8954         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8955         */
8956         center : function(centerIn){
8957             this.alignTo(centerIn || document, 'c-c');
8958             return this;
8959         },
8960
8961         /**
8962          * Tests various css rules/browsers to determine if this element uses a border box
8963          * @return {Boolean}
8964          */
8965         isBorderBox : function(){
8966             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8967         },
8968
8969         /**
8970          * Return a box {x, y, width, height} that can be used to set another elements
8971          * size/location to match this element.
8972          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8973          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8974          * @return {Object} box An object in the format {x, y, width, height}
8975          */
8976         getBox : function(contentBox, local){
8977             var xy;
8978             if(!local){
8979                 xy = this.getXY();
8980             }else{
8981                 var left = parseInt(this.getStyle("left"), 10) || 0;
8982                 var top = parseInt(this.getStyle("top"), 10) || 0;
8983                 xy = [left, top];
8984             }
8985             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8986             if(!contentBox){
8987                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8988             }else{
8989                 var l = this.getBorderWidth("l")+this.getPadding("l");
8990                 var r = this.getBorderWidth("r")+this.getPadding("r");
8991                 var t = this.getBorderWidth("t")+this.getPadding("t");
8992                 var b = this.getBorderWidth("b")+this.getPadding("b");
8993                 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)};
8994             }
8995             bx.right = bx.x + bx.width;
8996             bx.bottom = bx.y + bx.height;
8997             return bx;
8998         },
8999
9000         /**
9001          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9002          for more information about the sides.
9003          * @param {String} sides
9004          * @return {Number}
9005          */
9006         getFrameWidth : function(sides, onlyContentBox){
9007             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9008         },
9009
9010         /**
9011          * 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.
9012          * @param {Object} box The box to fill {x, y, width, height}
9013          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9014          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9015          * @return {Roo.Element} this
9016          */
9017         setBox : function(box, adjust, animate){
9018             var w = box.width, h = box.height;
9019             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9020                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9021                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9022             }
9023             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9024             return this;
9025         },
9026
9027         /**
9028          * Forces the browser to repaint this element
9029          * @return {Roo.Element} this
9030          */
9031          repaint : function(){
9032             var dom = this.dom;
9033             this.addClass("x-repaint");
9034             setTimeout(function(){
9035                 Roo.get(dom).removeClass("x-repaint");
9036             }, 1);
9037             return this;
9038         },
9039
9040         /**
9041          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9042          * then it returns the calculated width of the sides (see getPadding)
9043          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9044          * @return {Object/Number}
9045          */
9046         getMargins : function(side){
9047             if(!side){
9048                 return {
9049                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9050                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9051                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9052                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9053                 };
9054             }else{
9055                 return this.addStyles(side, El.margins);
9056              }
9057         },
9058
9059         // private
9060         addStyles : function(sides, styles){
9061             var val = 0, v, w;
9062             for(var i = 0, len = sides.length; i < len; i++){
9063                 v = this.getStyle(styles[sides.charAt(i)]);
9064                 if(v){
9065                      w = parseInt(v, 10);
9066                      if(w){ val += w; }
9067                 }
9068             }
9069             return val;
9070         },
9071
9072         /**
9073          * Creates a proxy element of this element
9074          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9075          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9076          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9077          * @return {Roo.Element} The new proxy element
9078          */
9079         createProxy : function(config, renderTo, matchBox){
9080             if(renderTo){
9081                 renderTo = Roo.getDom(renderTo);
9082             }else{
9083                 renderTo = document.body;
9084             }
9085             config = typeof config == "object" ?
9086                 config : {tag : "div", cls: config};
9087             var proxy = Roo.DomHelper.append(renderTo, config, true);
9088             if(matchBox){
9089                proxy.setBox(this.getBox());
9090             }
9091             return proxy;
9092         },
9093
9094         /**
9095          * Puts a mask over this element to disable user interaction. Requires core.css.
9096          * This method can only be applied to elements which accept child nodes.
9097          * @param {String} msg (optional) A message to display in the mask
9098          * @param {String} msgCls (optional) A css class to apply to the msg element
9099          * @return {Element} The mask  element
9100          */
9101         mask : function(msg, msgCls)
9102         {
9103             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9104                 this.setStyle("position", "relative");
9105             }
9106             if(!this._mask){
9107                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9108             }
9109             this.addClass("x-masked");
9110             this._mask.setDisplayed(true);
9111             
9112             // we wander
9113             var z = 0;
9114             var dom = this.dom;
9115             while (dom && dom.style) {
9116                 if (!isNaN(parseInt(dom.style.zIndex))) {
9117                     z = Math.max(z, parseInt(dom.style.zIndex));
9118                 }
9119                 dom = dom.parentNode;
9120             }
9121             // if we are masking the body - then it hides everything..
9122             if (this.dom == document.body) {
9123                 z = 1000000;
9124                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9125                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9126             }
9127            
9128             if(typeof msg == 'string'){
9129                 if(!this._maskMsg){
9130                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9131                 }
9132                 var mm = this._maskMsg;
9133                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9134                 if (mm.dom.firstChild) { // weird IE issue?
9135                     mm.dom.firstChild.innerHTML = msg;
9136                 }
9137                 mm.setDisplayed(true);
9138                 mm.center(this);
9139                 mm.setStyle('z-index', z + 102);
9140             }
9141             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9142                 this._mask.setHeight(this.getHeight());
9143             }
9144             this._mask.setStyle('z-index', z + 100);
9145             
9146             return this._mask;
9147         },
9148
9149         /**
9150          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9151          * it is cached for reuse.
9152          */
9153         unmask : function(removeEl){
9154             if(this._mask){
9155                 if(removeEl === true){
9156                     this._mask.remove();
9157                     delete this._mask;
9158                     if(this._maskMsg){
9159                         this._maskMsg.remove();
9160                         delete this._maskMsg;
9161                     }
9162                 }else{
9163                     this._mask.setDisplayed(false);
9164                     if(this._maskMsg){
9165                         this._maskMsg.setDisplayed(false);
9166                     }
9167                 }
9168             }
9169             this.removeClass("x-masked");
9170         },
9171
9172         /**
9173          * Returns true if this element is masked
9174          * @return {Boolean}
9175          */
9176         isMasked : function(){
9177             return this._mask && this._mask.isVisible();
9178         },
9179
9180         /**
9181          * Creates an iframe shim for this element to keep selects and other windowed objects from
9182          * showing through.
9183          * @return {Roo.Element} The new shim element
9184          */
9185         createShim : function(){
9186             var el = document.createElement('iframe');
9187             el.frameBorder = 'no';
9188             el.className = 'roo-shim';
9189             if(Roo.isIE && Roo.isSecure){
9190                 el.src = Roo.SSL_SECURE_URL;
9191             }
9192             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9193             shim.autoBoxAdjust = false;
9194             return shim;
9195         },
9196
9197         /**
9198          * Removes this element from the DOM and deletes it from the cache
9199          */
9200         remove : function(){
9201             if(this.dom.parentNode){
9202                 this.dom.parentNode.removeChild(this.dom);
9203             }
9204             delete El.cache[this.dom.id];
9205         },
9206
9207         /**
9208          * Sets up event handlers to add and remove a css class when the mouse is over this element
9209          * @param {String} className
9210          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9211          * mouseout events for children elements
9212          * @return {Roo.Element} this
9213          */
9214         addClassOnOver : function(className, preventFlicker){
9215             this.on("mouseover", function(){
9216                 Roo.fly(this, '_internal').addClass(className);
9217             }, this.dom);
9218             var removeFn = function(e){
9219                 if(preventFlicker !== true || !e.within(this, true)){
9220                     Roo.fly(this, '_internal').removeClass(className);
9221                 }
9222             };
9223             this.on("mouseout", removeFn, this.dom);
9224             return this;
9225         },
9226
9227         /**
9228          * Sets up event handlers to add and remove a css class when this element has the focus
9229          * @param {String} className
9230          * @return {Roo.Element} this
9231          */
9232         addClassOnFocus : function(className){
9233             this.on("focus", function(){
9234                 Roo.fly(this, '_internal').addClass(className);
9235             }, this.dom);
9236             this.on("blur", function(){
9237                 Roo.fly(this, '_internal').removeClass(className);
9238             }, this.dom);
9239             return this;
9240         },
9241         /**
9242          * 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)
9243          * @param {String} className
9244          * @return {Roo.Element} this
9245          */
9246         addClassOnClick : function(className){
9247             var dom = this.dom;
9248             this.on("mousedown", function(){
9249                 Roo.fly(dom, '_internal').addClass(className);
9250                 var d = Roo.get(document);
9251                 var fn = function(){
9252                     Roo.fly(dom, '_internal').removeClass(className);
9253                     d.removeListener("mouseup", fn);
9254                 };
9255                 d.on("mouseup", fn);
9256             });
9257             return this;
9258         },
9259
9260         /**
9261          * Stops the specified event from bubbling and optionally prevents the default action
9262          * @param {String} eventName
9263          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9264          * @return {Roo.Element} this
9265          */
9266         swallowEvent : function(eventName, preventDefault){
9267             var fn = function(e){
9268                 e.stopPropagation();
9269                 if(preventDefault){
9270                     e.preventDefault();
9271                 }
9272             };
9273             if(eventName instanceof Array){
9274                 for(var i = 0, len = eventName.length; i < len; i++){
9275                      this.on(eventName[i], fn);
9276                 }
9277                 return this;
9278             }
9279             this.on(eventName, fn);
9280             return this;
9281         },
9282
9283         /**
9284          * @private
9285          */
9286       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9287
9288         /**
9289          * Sizes this element to its parent element's dimensions performing
9290          * neccessary box adjustments.
9291          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9292          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9293          * @return {Roo.Element} this
9294          */
9295         fitToParent : function(monitorResize, targetParent) {
9296           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9297           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9298           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9299             return;
9300           }
9301           var p = Roo.get(targetParent || this.dom.parentNode);
9302           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9303           if (monitorResize === true) {
9304             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9305             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9306           }
9307           return this;
9308         },
9309
9310         /**
9311          * Gets the next sibling, skipping text nodes
9312          * @return {HTMLElement} The next sibling or null
9313          */
9314         getNextSibling : function(){
9315             var n = this.dom.nextSibling;
9316             while(n && n.nodeType != 1){
9317                 n = n.nextSibling;
9318             }
9319             return n;
9320         },
9321
9322         /**
9323          * Gets the previous sibling, skipping text nodes
9324          * @return {HTMLElement} The previous sibling or null
9325          */
9326         getPrevSibling : function(){
9327             var n = this.dom.previousSibling;
9328             while(n && n.nodeType != 1){
9329                 n = n.previousSibling;
9330             }
9331             return n;
9332         },
9333
9334
9335         /**
9336          * Appends the passed element(s) to this element
9337          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9338          * @return {Roo.Element} this
9339          */
9340         appendChild: function(el){
9341             el = Roo.get(el);
9342             el.appendTo(this);
9343             return this;
9344         },
9345
9346         /**
9347          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9348          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9349          * automatically generated with the specified attributes.
9350          * @param {HTMLElement} insertBefore (optional) a child element of this element
9351          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9352          * @return {Roo.Element} The new child element
9353          */
9354         createChild: function(config, insertBefore, returnDom){
9355             config = config || {tag:'div'};
9356             if(insertBefore){
9357                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9358             }
9359             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9360         },
9361
9362         /**
9363          * Appends this element to the passed element
9364          * @param {String/HTMLElement/Element} el The new parent element
9365          * @return {Roo.Element} this
9366          */
9367         appendTo: function(el){
9368             el = Roo.getDom(el);
9369             el.appendChild(this.dom);
9370             return this;
9371         },
9372
9373         /**
9374          * Inserts this element before the passed element in the DOM
9375          * @param {String/HTMLElement/Element} el The element to insert before
9376          * @return {Roo.Element} this
9377          */
9378         insertBefore: function(el){
9379             el = Roo.getDom(el);
9380             el.parentNode.insertBefore(this.dom, el);
9381             return this;
9382         },
9383
9384         /**
9385          * Inserts this element after the passed element in the DOM
9386          * @param {String/HTMLElement/Element} el The element to insert after
9387          * @return {Roo.Element} this
9388          */
9389         insertAfter: function(el){
9390             el = Roo.getDom(el);
9391             el.parentNode.insertBefore(this.dom, el.nextSibling);
9392             return this;
9393         },
9394
9395         /**
9396          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9397          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9398          * @return {Roo.Element} The new child
9399          */
9400         insertFirst: function(el, returnDom){
9401             el = el || {};
9402             if(typeof el == 'object' && !el.nodeType){ // dh config
9403                 return this.createChild(el, this.dom.firstChild, returnDom);
9404             }else{
9405                 el = Roo.getDom(el);
9406                 this.dom.insertBefore(el, this.dom.firstChild);
9407                 return !returnDom ? Roo.get(el) : el;
9408             }
9409         },
9410
9411         /**
9412          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9413          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9414          * @param {String} where (optional) 'before' or 'after' defaults to before
9415          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9416          * @return {Roo.Element} the inserted Element
9417          */
9418         insertSibling: function(el, where, returnDom){
9419             where = where ? where.toLowerCase() : 'before';
9420             el = el || {};
9421             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9422
9423             if(typeof el == 'object' && !el.nodeType){ // dh config
9424                 if(where == 'after' && !this.dom.nextSibling){
9425                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9426                 }else{
9427                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9428                 }
9429
9430             }else{
9431                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9432                             where == 'before' ? this.dom : this.dom.nextSibling);
9433                 if(!returnDom){
9434                     rt = Roo.get(rt);
9435                 }
9436             }
9437             return rt;
9438         },
9439
9440         /**
9441          * Creates and wraps this element with another element
9442          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9443          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9444          * @return {HTMLElement/Element} The newly created wrapper element
9445          */
9446         wrap: function(config, returnDom){
9447             if(!config){
9448                 config = {tag: "div"};
9449             }
9450             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9451             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9452             return newEl;
9453         },
9454
9455         /**
9456          * Replaces the passed element with this element
9457          * @param {String/HTMLElement/Element} el The element to replace
9458          * @return {Roo.Element} this
9459          */
9460         replace: function(el){
9461             el = Roo.get(el);
9462             this.insertBefore(el);
9463             el.remove();
9464             return this;
9465         },
9466
9467         /**
9468          * Inserts an html fragment into this element
9469          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9470          * @param {String} html The HTML fragment
9471          * @param {Boolean} returnEl True to return an Roo.Element
9472          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9473          */
9474         insertHtml : function(where, html, returnEl){
9475             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9476             return returnEl ? Roo.get(el) : el;
9477         },
9478
9479         /**
9480          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9481          * @param {Object} o The object with the attributes
9482          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9483          * @return {Roo.Element} this
9484          */
9485         set : function(o, useSet){
9486             var el = this.dom;
9487             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9488             for(var attr in o){
9489                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9490                 if(attr=="cls"){
9491                     el.className = o["cls"];
9492                 }else{
9493                     if(useSet) {
9494                         el.setAttribute(attr, o[attr]);
9495                     } else {
9496                         el[attr] = o[attr];
9497                     }
9498                 }
9499             }
9500             if(o.style){
9501                 Roo.DomHelper.applyStyles(el, o.style);
9502             }
9503             return this;
9504         },
9505
9506         /**
9507          * Convenience method for constructing a KeyMap
9508          * @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:
9509          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9510          * @param {Function} fn The function to call
9511          * @param {Object} scope (optional) The scope of the function
9512          * @return {Roo.KeyMap} The KeyMap created
9513          */
9514         addKeyListener : function(key, fn, scope){
9515             var config;
9516             if(typeof key != "object" || key instanceof Array){
9517                 config = {
9518                     key: key,
9519                     fn: fn,
9520                     scope: scope
9521                 };
9522             }else{
9523                 config = {
9524                     key : key.key,
9525                     shift : key.shift,
9526                     ctrl : key.ctrl,
9527                     alt : key.alt,
9528                     fn: fn,
9529                     scope: scope
9530                 };
9531             }
9532             return new Roo.KeyMap(this, config);
9533         },
9534
9535         /**
9536          * Creates a KeyMap for this element
9537          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9538          * @return {Roo.KeyMap} The KeyMap created
9539          */
9540         addKeyMap : function(config){
9541             return new Roo.KeyMap(this, config);
9542         },
9543
9544         /**
9545          * Returns true if this element is scrollable.
9546          * @return {Boolean}
9547          */
9548          isScrollable : function(){
9549             var dom = this.dom;
9550             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9551         },
9552
9553         /**
9554          * 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().
9555          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9556          * @param {Number} value The new scroll value
9557          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9558          * @return {Element} this
9559          */
9560
9561         scrollTo : function(side, value, animate){
9562             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9563             if(!animate || !A){
9564                 this.dom[prop] = value;
9565             }else{
9566                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9567                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9568             }
9569             return this;
9570         },
9571
9572         /**
9573          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9574          * within this element's scrollable range.
9575          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9576          * @param {Number} distance How far to scroll the element in pixels
9577          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9578          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9579          * was scrolled as far as it could go.
9580          */
9581          scroll : function(direction, distance, animate){
9582              if(!this.isScrollable()){
9583                  return;
9584              }
9585              var el = this.dom;
9586              var l = el.scrollLeft, t = el.scrollTop;
9587              var w = el.scrollWidth, h = el.scrollHeight;
9588              var cw = el.clientWidth, ch = el.clientHeight;
9589              direction = direction.toLowerCase();
9590              var scrolled = false;
9591              var a = this.preanim(arguments, 2);
9592              switch(direction){
9593                  case "l":
9594                  case "left":
9595                      if(w - l > cw){
9596                          var v = Math.min(l + distance, w-cw);
9597                          this.scrollTo("left", v, a);
9598                          scrolled = true;
9599                      }
9600                      break;
9601                 case "r":
9602                 case "right":
9603                      if(l > 0){
9604                          var v = Math.max(l - distance, 0);
9605                          this.scrollTo("left", v, a);
9606                          scrolled = true;
9607                      }
9608                      break;
9609                 case "t":
9610                 case "top":
9611                 case "up":
9612                      if(t > 0){
9613                          var v = Math.max(t - distance, 0);
9614                          this.scrollTo("top", v, a);
9615                          scrolled = true;
9616                      }
9617                      break;
9618                 case "b":
9619                 case "bottom":
9620                 case "down":
9621                      if(h - t > ch){
9622                          var v = Math.min(t + distance, h-ch);
9623                          this.scrollTo("top", v, a);
9624                          scrolled = true;
9625                      }
9626                      break;
9627              }
9628              return scrolled;
9629         },
9630
9631         /**
9632          * Translates the passed page coordinates into left/top css values for this element
9633          * @param {Number/Array} x The page x or an array containing [x, y]
9634          * @param {Number} y The page y
9635          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9636          */
9637         translatePoints : function(x, y){
9638             if(typeof x == 'object' || x instanceof Array){
9639                 y = x[1]; x = x[0];
9640             }
9641             var p = this.getStyle('position');
9642             var o = this.getXY();
9643
9644             var l = parseInt(this.getStyle('left'), 10);
9645             var t = parseInt(this.getStyle('top'), 10);
9646
9647             if(isNaN(l)){
9648                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9649             }
9650             if(isNaN(t)){
9651                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9652             }
9653
9654             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9655         },
9656
9657         /**
9658          * Returns the current scroll position of the element.
9659          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9660          */
9661         getScroll : function(){
9662             var d = this.dom, doc = document;
9663             if(d == doc || d == doc.body){
9664                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9665                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9666                 return {left: l, top: t};
9667             }else{
9668                 return {left: d.scrollLeft, top: d.scrollTop};
9669             }
9670         },
9671
9672         /**
9673          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9674          * are convert to standard 6 digit hex color.
9675          * @param {String} attr The css attribute
9676          * @param {String} defaultValue The default value to use when a valid color isn't found
9677          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9678          * YUI color anims.
9679          */
9680         getColor : function(attr, defaultValue, prefix){
9681             var v = this.getStyle(attr);
9682             if(!v || v == "transparent" || v == "inherit") {
9683                 return defaultValue;
9684             }
9685             var color = typeof prefix == "undefined" ? "#" : prefix;
9686             if(v.substr(0, 4) == "rgb("){
9687                 var rvs = v.slice(4, v.length -1).split(",");
9688                 for(var i = 0; i < 3; i++){
9689                     var h = parseInt(rvs[i]).toString(16);
9690                     if(h < 16){
9691                         h = "0" + h;
9692                     }
9693                     color += h;
9694                 }
9695             } else {
9696                 if(v.substr(0, 1) == "#"){
9697                     if(v.length == 4) {
9698                         for(var i = 1; i < 4; i++){
9699                             var c = v.charAt(i);
9700                             color +=  c + c;
9701                         }
9702                     }else if(v.length == 7){
9703                         color += v.substr(1);
9704                     }
9705                 }
9706             }
9707             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9708         },
9709
9710         /**
9711          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9712          * gradient background, rounded corners and a 4-way shadow.
9713          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9714          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9715          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9716          * @return {Roo.Element} this
9717          */
9718         boxWrap : function(cls){
9719             cls = cls || 'x-box';
9720             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9721             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9722             return el;
9723         },
9724
9725         /**
9726          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9727          * @param {String} namespace The namespace in which to look for the attribute
9728          * @param {String} name The attribute name
9729          * @return {String} The attribute value
9730          */
9731         getAttributeNS : Roo.isIE ? function(ns, name){
9732             var d = this.dom;
9733             var type = typeof d[ns+":"+name];
9734             if(type != 'undefined' && type != 'unknown'){
9735                 return d[ns+":"+name];
9736             }
9737             return d[name];
9738         } : function(ns, name){
9739             var d = this.dom;
9740             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9741         },
9742         
9743         
9744         /**
9745          * Sets or Returns the value the dom attribute value
9746          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9747          * @param {String} value (optional) The value to set the attribute to
9748          * @return {String} The attribute value
9749          */
9750         attr : function(name){
9751             if (arguments.length > 1) {
9752                 this.dom.setAttribute(name, arguments[1]);
9753                 return arguments[1];
9754             }
9755             if (typeof(name) == 'object') {
9756                 for(var i in name) {
9757                     this.attr(i, name[i]);
9758                 }
9759                 return name;
9760             }
9761             
9762             
9763             if (!this.dom.hasAttribute(name)) {
9764                 return undefined;
9765             }
9766             return this.dom.getAttribute(name);
9767         }
9768         
9769         
9770         
9771     };
9772
9773     var ep = El.prototype;
9774
9775     /**
9776      * Appends an event handler (Shorthand for addListener)
9777      * @param {String}   eventName     The type of event to append
9778      * @param {Function} fn        The method the event invokes
9779      * @param {Object} scope       (optional) The scope (this object) of the fn
9780      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9781      * @method
9782      */
9783     ep.on = ep.addListener;
9784         // backwards compat
9785     ep.mon = ep.addListener;
9786
9787     /**
9788      * Removes an event handler from this element (shorthand for removeListener)
9789      * @param {String} eventName the type of event to remove
9790      * @param {Function} fn the method the event invokes
9791      * @return {Roo.Element} this
9792      * @method
9793      */
9794     ep.un = ep.removeListener;
9795
9796     /**
9797      * true to automatically adjust width and height settings for box-model issues (default to true)
9798      */
9799     ep.autoBoxAdjust = true;
9800
9801     // private
9802     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9803
9804     // private
9805     El.addUnits = function(v, defaultUnit){
9806         if(v === "" || v == "auto"){
9807             return v;
9808         }
9809         if(v === undefined){
9810             return '';
9811         }
9812         if(typeof v == "number" || !El.unitPattern.test(v)){
9813             return v + (defaultUnit || 'px');
9814         }
9815         return v;
9816     };
9817
9818     // special markup used throughout Roo when box wrapping elements
9819     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>';
9820     /**
9821      * Visibility mode constant - Use visibility to hide element
9822      * @static
9823      * @type Number
9824      */
9825     El.VISIBILITY = 1;
9826     /**
9827      * Visibility mode constant - Use display to hide element
9828      * @static
9829      * @type Number
9830      */
9831     El.DISPLAY = 2;
9832
9833     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9834     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9835     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9836
9837
9838
9839     /**
9840      * @private
9841      */
9842     El.cache = {};
9843
9844     var docEl;
9845
9846     /**
9847      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9848      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9849      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9850      * @return {Element} The Element object
9851      * @static
9852      */
9853     El.get = function(el){
9854         var ex, elm, id;
9855         if(!el){ return null; }
9856         if(typeof el == "string"){ // element id
9857             if(!(elm = document.getElementById(el))){
9858                 return null;
9859             }
9860             if(ex = El.cache[el]){
9861                 ex.dom = elm;
9862             }else{
9863                 ex = El.cache[el] = new El(elm);
9864             }
9865             return ex;
9866         }else if(el.tagName){ // dom element
9867             if(!(id = el.id)){
9868                 id = Roo.id(el);
9869             }
9870             if(ex = El.cache[id]){
9871                 ex.dom = el;
9872             }else{
9873                 ex = El.cache[id] = new El(el);
9874             }
9875             return ex;
9876         }else if(el instanceof El){
9877             if(el != docEl){
9878                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9879                                                               // catch case where it hasn't been appended
9880                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9881             }
9882             return el;
9883         }else if(el.isComposite){
9884             return el;
9885         }else if(el instanceof Array){
9886             return El.select(el);
9887         }else if(el == document){
9888             // create a bogus element object representing the document object
9889             if(!docEl){
9890                 var f = function(){};
9891                 f.prototype = El.prototype;
9892                 docEl = new f();
9893                 docEl.dom = document;
9894             }
9895             return docEl;
9896         }
9897         return null;
9898     };
9899
9900     // private
9901     El.uncache = function(el){
9902         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9903             if(a[i]){
9904                 delete El.cache[a[i].id || a[i]];
9905             }
9906         }
9907     };
9908
9909     // private
9910     // Garbage collection - uncache elements/purge listeners on orphaned elements
9911     // so we don't hold a reference and cause the browser to retain them
9912     El.garbageCollect = function(){
9913         if(!Roo.enableGarbageCollector){
9914             clearInterval(El.collectorThread);
9915             return;
9916         }
9917         for(var eid in El.cache){
9918             var el = El.cache[eid], d = el.dom;
9919             // -------------------------------------------------------
9920             // Determining what is garbage:
9921             // -------------------------------------------------------
9922             // !d
9923             // dom node is null, definitely garbage
9924             // -------------------------------------------------------
9925             // !d.parentNode
9926             // no parentNode == direct orphan, definitely garbage
9927             // -------------------------------------------------------
9928             // !d.offsetParent && !document.getElementById(eid)
9929             // display none elements have no offsetParent so we will
9930             // also try to look it up by it's id. However, check
9931             // offsetParent first so we don't do unneeded lookups.
9932             // This enables collection of elements that are not orphans
9933             // directly, but somewhere up the line they have an orphan
9934             // parent.
9935             // -------------------------------------------------------
9936             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9937                 delete El.cache[eid];
9938                 if(d && Roo.enableListenerCollection){
9939                     E.purgeElement(d);
9940                 }
9941             }
9942         }
9943     }
9944     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9945
9946
9947     // dom is optional
9948     El.Flyweight = function(dom){
9949         this.dom = dom;
9950     };
9951     El.Flyweight.prototype = El.prototype;
9952
9953     El._flyweights = {};
9954     /**
9955      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9956      * the dom node can be overwritten by other code.
9957      * @param {String/HTMLElement} el The dom node or id
9958      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9959      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9960      * @static
9961      * @return {Element} The shared Element object
9962      */
9963     El.fly = function(el, named){
9964         named = named || '_global';
9965         el = Roo.getDom(el);
9966         if(!el){
9967             return null;
9968         }
9969         if(!El._flyweights[named]){
9970             El._flyweights[named] = new El.Flyweight();
9971         }
9972         El._flyweights[named].dom = el;
9973         return El._flyweights[named];
9974     };
9975
9976     /**
9977      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9978      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9979      * Shorthand of {@link Roo.Element#get}
9980      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9981      * @return {Element} The Element object
9982      * @member Roo
9983      * @method get
9984      */
9985     Roo.get = El.get;
9986     /**
9987      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9988      * the dom node can be overwritten by other code.
9989      * Shorthand of {@link Roo.Element#fly}
9990      * @param {String/HTMLElement} el The dom node or id
9991      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9992      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9993      * @static
9994      * @return {Element} The shared Element object
9995      * @member Roo
9996      * @method fly
9997      */
9998     Roo.fly = El.fly;
9999
10000     // speedy lookup for elements never to box adjust
10001     var noBoxAdjust = Roo.isStrict ? {
10002         select:1
10003     } : {
10004         input:1, select:1, textarea:1
10005     };
10006     if(Roo.isIE || Roo.isGecko){
10007         noBoxAdjust['button'] = 1;
10008     }
10009
10010
10011     Roo.EventManager.on(window, 'unload', function(){
10012         delete El.cache;
10013         delete El._flyweights;
10014     });
10015 })();
10016
10017
10018
10019
10020 if(Roo.DomQuery){
10021     Roo.Element.selectorFunction = Roo.DomQuery.select;
10022 }
10023
10024 Roo.Element.select = function(selector, unique, root){
10025     var els;
10026     if(typeof selector == "string"){
10027         els = Roo.Element.selectorFunction(selector, root);
10028     }else if(selector.length !== undefined){
10029         els = selector;
10030     }else{
10031         throw "Invalid selector";
10032     }
10033     if(unique === true){
10034         return new Roo.CompositeElement(els);
10035     }else{
10036         return new Roo.CompositeElementLite(els);
10037     }
10038 };
10039 /**
10040  * Selects elements based on the passed CSS selector to enable working on them as 1.
10041  * @param {String/Array} selector The CSS selector or an array of elements
10042  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10043  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10044  * @return {CompositeElementLite/CompositeElement}
10045  * @member Roo
10046  * @method select
10047  */
10048 Roo.select = Roo.Element.select;
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062
10063 /*
10064  * Based on:
10065  * Ext JS Library 1.1.1
10066  * Copyright(c) 2006-2007, Ext JS, LLC.
10067  *
10068  * Originally Released Under LGPL - original licence link has changed is not relivant.
10069  *
10070  * Fork - LGPL
10071  * <script type="text/javascript">
10072  */
10073
10074
10075
10076 //Notifies Element that fx methods are available
10077 Roo.enableFx = true;
10078
10079 /**
10080  * @class Roo.Fx
10081  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10082  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10083  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10084  * Element effects to work.</p><br/>
10085  *
10086  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10087  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10088  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10089  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10090  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10091  * expected results and should be done with care.</p><br/>
10092  *
10093  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10094  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10095 <pre>
10096 Value  Description
10097 -----  -----------------------------
10098 tl     The top left corner
10099 t      The center of the top edge
10100 tr     The top right corner
10101 l      The center of the left edge
10102 r      The center of the right edge
10103 bl     The bottom left corner
10104 b      The center of the bottom edge
10105 br     The bottom right corner
10106 </pre>
10107  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10108  * below are common options that can be passed to any Fx method.</b>
10109  * @cfg {Function} callback A function called when the effect is finished
10110  * @cfg {Object} scope The scope of the effect function
10111  * @cfg {String} easing A valid Easing value for the effect
10112  * @cfg {String} afterCls A css class to apply after the effect
10113  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10114  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10115  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10116  * effects that end with the element being visually hidden, ignored otherwise)
10117  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10118  * a function which returns such a specification that will be applied to the Element after the effect finishes
10119  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10120  * @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
10121  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10122  */
10123 Roo.Fx = {
10124         /**
10125          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10126          * origin for the slide effect.  This function automatically handles wrapping the element with
10127          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10128          * Usage:
10129          *<pre><code>
10130 // default: slide the element in from the top
10131 el.slideIn();
10132
10133 // custom: slide the element in from the right with a 2-second duration
10134 el.slideIn('r', { duration: 2 });
10135
10136 // common config options shown with default values
10137 el.slideIn('t', {
10138     easing: 'easeOut',
10139     duration: .5
10140 });
10141 </code></pre>
10142          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10143          * @param {Object} options (optional) Object literal with any of the Fx config options
10144          * @return {Roo.Element} The Element
10145          */
10146     slideIn : function(anchor, o){
10147         var el = this.getFxEl();
10148         o = o || {};
10149
10150         el.queueFx(o, function(){
10151
10152             anchor = anchor || "t";
10153
10154             // fix display to visibility
10155             this.fixDisplay();
10156
10157             // restore values after effect
10158             var r = this.getFxRestore();
10159             var b = this.getBox();
10160             // fixed size for slide
10161             this.setSize(b);
10162
10163             // wrap if needed
10164             var wrap = this.fxWrap(r.pos, o, "hidden");
10165
10166             var st = this.dom.style;
10167             st.visibility = "visible";
10168             st.position = "absolute";
10169
10170             // clear out temp styles after slide and unwrap
10171             var after = function(){
10172                 el.fxUnwrap(wrap, r.pos, o);
10173                 st.width = r.width;
10174                 st.height = r.height;
10175                 el.afterFx(o);
10176             };
10177             // time to calc the positions
10178             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10179
10180             switch(anchor.toLowerCase()){
10181                 case "t":
10182                     wrap.setSize(b.width, 0);
10183                     st.left = st.bottom = "0";
10184                     a = {height: bh};
10185                 break;
10186                 case "l":
10187                     wrap.setSize(0, b.height);
10188                     st.right = st.top = "0";
10189                     a = {width: bw};
10190                 break;
10191                 case "r":
10192                     wrap.setSize(0, b.height);
10193                     wrap.setX(b.right);
10194                     st.left = st.top = "0";
10195                     a = {width: bw, points: pt};
10196                 break;
10197                 case "b":
10198                     wrap.setSize(b.width, 0);
10199                     wrap.setY(b.bottom);
10200                     st.left = st.top = "0";
10201                     a = {height: bh, points: pt};
10202                 break;
10203                 case "tl":
10204                     wrap.setSize(0, 0);
10205                     st.right = st.bottom = "0";
10206                     a = {width: bw, height: bh};
10207                 break;
10208                 case "bl":
10209                     wrap.setSize(0, 0);
10210                     wrap.setY(b.y+b.height);
10211                     st.right = st.top = "0";
10212                     a = {width: bw, height: bh, points: pt};
10213                 break;
10214                 case "br":
10215                     wrap.setSize(0, 0);
10216                     wrap.setXY([b.right, b.bottom]);
10217                     st.left = st.top = "0";
10218                     a = {width: bw, height: bh, points: pt};
10219                 break;
10220                 case "tr":
10221                     wrap.setSize(0, 0);
10222                     wrap.setX(b.x+b.width);
10223                     st.left = st.bottom = "0";
10224                     a = {width: bw, height: bh, points: pt};
10225                 break;
10226             }
10227             this.dom.style.visibility = "visible";
10228             wrap.show();
10229
10230             arguments.callee.anim = wrap.fxanim(a,
10231                 o,
10232                 'motion',
10233                 .5,
10234                 'easeOut', after);
10235         });
10236         return this;
10237     },
10238     
10239         /**
10240          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10241          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10242          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10243          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10244          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10245          * Usage:
10246          *<pre><code>
10247 // default: slide the element out to the top
10248 el.slideOut();
10249
10250 // custom: slide the element out to the right with a 2-second duration
10251 el.slideOut('r', { duration: 2 });
10252
10253 // common config options shown with default values
10254 el.slideOut('t', {
10255     easing: 'easeOut',
10256     duration: .5,
10257     remove: false,
10258     useDisplay: false
10259 });
10260 </code></pre>
10261          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10262          * @param {Object} options (optional) Object literal with any of the Fx config options
10263          * @return {Roo.Element} The Element
10264          */
10265     slideOut : function(anchor, o){
10266         var el = this.getFxEl();
10267         o = o || {};
10268
10269         el.queueFx(o, function(){
10270
10271             anchor = anchor || "t";
10272
10273             // restore values after effect
10274             var r = this.getFxRestore();
10275             
10276             var b = this.getBox();
10277             // fixed size for slide
10278             this.setSize(b);
10279
10280             // wrap if needed
10281             var wrap = this.fxWrap(r.pos, o, "visible");
10282
10283             var st = this.dom.style;
10284             st.visibility = "visible";
10285             st.position = "absolute";
10286
10287             wrap.setSize(b);
10288
10289             var after = function(){
10290                 if(o.useDisplay){
10291                     el.setDisplayed(false);
10292                 }else{
10293                     el.hide();
10294                 }
10295
10296                 el.fxUnwrap(wrap, r.pos, o);
10297
10298                 st.width = r.width;
10299                 st.height = r.height;
10300
10301                 el.afterFx(o);
10302             };
10303
10304             var a, zero = {to: 0};
10305             switch(anchor.toLowerCase()){
10306                 case "t":
10307                     st.left = st.bottom = "0";
10308                     a = {height: zero};
10309                 break;
10310                 case "l":
10311                     st.right = st.top = "0";
10312                     a = {width: zero};
10313                 break;
10314                 case "r":
10315                     st.left = st.top = "0";
10316                     a = {width: zero, points: {to:[b.right, b.y]}};
10317                 break;
10318                 case "b":
10319                     st.left = st.top = "0";
10320                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10321                 break;
10322                 case "tl":
10323                     st.right = st.bottom = "0";
10324                     a = {width: zero, height: zero};
10325                 break;
10326                 case "bl":
10327                     st.right = st.top = "0";
10328                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10329                 break;
10330                 case "br":
10331                     st.left = st.top = "0";
10332                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10333                 break;
10334                 case "tr":
10335                     st.left = st.bottom = "0";
10336                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10337                 break;
10338             }
10339
10340             arguments.callee.anim = wrap.fxanim(a,
10341                 o,
10342                 'motion',
10343                 .5,
10344                 "easeOut", after);
10345         });
10346         return this;
10347     },
10348
10349         /**
10350          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10351          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10352          * The element must be removed from the DOM using the 'remove' config option if desired.
10353          * Usage:
10354          *<pre><code>
10355 // default
10356 el.puff();
10357
10358 // common config options shown with default values
10359 el.puff({
10360     easing: 'easeOut',
10361     duration: .5,
10362     remove: false,
10363     useDisplay: false
10364 });
10365 </code></pre>
10366          * @param {Object} options (optional) Object literal with any of the Fx config options
10367          * @return {Roo.Element} The Element
10368          */
10369     puff : function(o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             this.clearOpacity();
10375             this.show();
10376
10377             // restore values after effect
10378             var r = this.getFxRestore();
10379             var st = this.dom.style;
10380
10381             var after = function(){
10382                 if(o.useDisplay){
10383                     el.setDisplayed(false);
10384                 }else{
10385                     el.hide();
10386                 }
10387
10388                 el.clearOpacity();
10389
10390                 el.setPositioning(r.pos);
10391                 st.width = r.width;
10392                 st.height = r.height;
10393                 st.fontSize = '';
10394                 el.afterFx(o);
10395             };
10396
10397             var width = this.getWidth();
10398             var height = this.getHeight();
10399
10400             arguments.callee.anim = this.fxanim({
10401                     width : {to: this.adjustWidth(width * 2)},
10402                     height : {to: this.adjustHeight(height * 2)},
10403                     points : {by: [-(width * .5), -(height * .5)]},
10404                     opacity : {to: 0},
10405                     fontSize: {to:200, unit: "%"}
10406                 },
10407                 o,
10408                 'motion',
10409                 .5,
10410                 "easeOut", after);
10411         });
10412         return this;
10413     },
10414
10415         /**
10416          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10417          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10418          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10419          * Usage:
10420          *<pre><code>
10421 // default
10422 el.switchOff();
10423
10424 // all config options shown with default values
10425 el.switchOff({
10426     easing: 'easeIn',
10427     duration: .3,
10428     remove: false,
10429     useDisplay: false
10430 });
10431 </code></pre>
10432          * @param {Object} options (optional) Object literal with any of the Fx config options
10433          * @return {Roo.Element} The Element
10434          */
10435     switchOff : function(o){
10436         var el = this.getFxEl();
10437         o = o || {};
10438
10439         el.queueFx(o, function(){
10440             this.clearOpacity();
10441             this.clip();
10442
10443             // restore values after effect
10444             var r = this.getFxRestore();
10445             var st = this.dom.style;
10446
10447             var after = function(){
10448                 if(o.useDisplay){
10449                     el.setDisplayed(false);
10450                 }else{
10451                     el.hide();
10452                 }
10453
10454                 el.clearOpacity();
10455                 el.setPositioning(r.pos);
10456                 st.width = r.width;
10457                 st.height = r.height;
10458
10459                 el.afterFx(o);
10460             };
10461
10462             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10463                 this.clearOpacity();
10464                 (function(){
10465                     this.fxanim({
10466                         height:{to:1},
10467                         points:{by:[0, this.getHeight() * .5]}
10468                     }, o, 'motion', 0.3, 'easeIn', after);
10469                 }).defer(100, this);
10470             });
10471         });
10472         return this;
10473     },
10474
10475     /**
10476      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10477      * changed using the "attr" config option) and then fading back to the original color. If no original
10478      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10479      * Usage:
10480 <pre><code>
10481 // default: highlight background to yellow
10482 el.highlight();
10483
10484 // custom: highlight foreground text to blue for 2 seconds
10485 el.highlight("0000ff", { attr: 'color', duration: 2 });
10486
10487 // common config options shown with default values
10488 el.highlight("ffff9c", {
10489     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10490     endColor: (current color) or "ffffff",
10491     easing: 'easeIn',
10492     duration: 1
10493 });
10494 </code></pre>
10495      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10496      * @param {Object} options (optional) Object literal with any of the Fx config options
10497      * @return {Roo.Element} The Element
10498      */ 
10499     highlight : function(color, o){
10500         var el = this.getFxEl();
10501         o = o || {};
10502
10503         el.queueFx(o, function(){
10504             color = color || "ffff9c";
10505             attr = o.attr || "backgroundColor";
10506
10507             this.clearOpacity();
10508             this.show();
10509
10510             var origColor = this.getColor(attr);
10511             var restoreColor = this.dom.style[attr];
10512             endColor = (o.endColor || origColor) || "ffffff";
10513
10514             var after = function(){
10515                 el.dom.style[attr] = restoreColor;
10516                 el.afterFx(o);
10517             };
10518
10519             var a = {};
10520             a[attr] = {from: color, to: endColor};
10521             arguments.callee.anim = this.fxanim(a,
10522                 o,
10523                 'color',
10524                 1,
10525                 'easeIn', after);
10526         });
10527         return this;
10528     },
10529
10530    /**
10531     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10532     * Usage:
10533 <pre><code>
10534 // default: a single light blue ripple
10535 el.frame();
10536
10537 // custom: 3 red ripples lasting 3 seconds total
10538 el.frame("ff0000", 3, { duration: 3 });
10539
10540 // common config options shown with default values
10541 el.frame("C3DAF9", 1, {
10542     duration: 1 //duration of entire animation (not each individual ripple)
10543     // Note: Easing is not configurable and will be ignored if included
10544 });
10545 </code></pre>
10546     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10547     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10548     * @param {Object} options (optional) Object literal with any of the Fx config options
10549     * @return {Roo.Element} The Element
10550     */
10551     frame : function(color, count, o){
10552         var el = this.getFxEl();
10553         o = o || {};
10554
10555         el.queueFx(o, function(){
10556             color = color || "#C3DAF9";
10557             if(color.length == 6){
10558                 color = "#" + color;
10559             }
10560             count = count || 1;
10561             duration = o.duration || 1;
10562             this.show();
10563
10564             var b = this.getBox();
10565             var animFn = function(){
10566                 var proxy = this.createProxy({
10567
10568                      style:{
10569                         visbility:"hidden",
10570                         position:"absolute",
10571                         "z-index":"35000", // yee haw
10572                         border:"0px solid " + color
10573                      }
10574                   });
10575                 var scale = Roo.isBorderBox ? 2 : 1;
10576                 proxy.animate({
10577                     top:{from:b.y, to:b.y - 20},
10578                     left:{from:b.x, to:b.x - 20},
10579                     borderWidth:{from:0, to:10},
10580                     opacity:{from:1, to:0},
10581                     height:{from:b.height, to:(b.height + (20*scale))},
10582                     width:{from:b.width, to:(b.width + (20*scale))}
10583                 }, duration, function(){
10584                     proxy.remove();
10585                 });
10586                 if(--count > 0){
10587                      animFn.defer((duration/2)*1000, this);
10588                 }else{
10589                     el.afterFx(o);
10590                 }
10591             };
10592             animFn.call(this);
10593         });
10594         return this;
10595     },
10596
10597    /**
10598     * Creates a pause before any subsequent queued effects begin.  If there are
10599     * no effects queued after the pause it will have no effect.
10600     * Usage:
10601 <pre><code>
10602 el.pause(1);
10603 </code></pre>
10604     * @param {Number} seconds The length of time to pause (in seconds)
10605     * @return {Roo.Element} The Element
10606     */
10607     pause : function(seconds){
10608         var el = this.getFxEl();
10609         var o = {};
10610
10611         el.queueFx(o, function(){
10612             setTimeout(function(){
10613                 el.afterFx(o);
10614             }, seconds * 1000);
10615         });
10616         return this;
10617     },
10618
10619    /**
10620     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10621     * using the "endOpacity" config option.
10622     * Usage:
10623 <pre><code>
10624 // default: fade in from opacity 0 to 100%
10625 el.fadeIn();
10626
10627 // custom: fade in from opacity 0 to 75% over 2 seconds
10628 el.fadeIn({ endOpacity: .75, duration: 2});
10629
10630 // common config options shown with default values
10631 el.fadeIn({
10632     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10633     easing: 'easeOut',
10634     duration: .5
10635 });
10636 </code></pre>
10637     * @param {Object} options (optional) Object literal with any of the Fx config options
10638     * @return {Roo.Element} The Element
10639     */
10640     fadeIn : function(o){
10641         var el = this.getFxEl();
10642         o = o || {};
10643         el.queueFx(o, function(){
10644             this.setOpacity(0);
10645             this.fixDisplay();
10646             this.dom.style.visibility = 'visible';
10647             var to = o.endOpacity || 1;
10648             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10649                 o, null, .5, "easeOut", function(){
10650                 if(to == 1){
10651                     this.clearOpacity();
10652                 }
10653                 el.afterFx(o);
10654             });
10655         });
10656         return this;
10657     },
10658
10659    /**
10660     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10661     * using the "endOpacity" config option.
10662     * Usage:
10663 <pre><code>
10664 // default: fade out from the element's current opacity to 0
10665 el.fadeOut();
10666
10667 // custom: fade out from the element's current opacity to 25% over 2 seconds
10668 el.fadeOut({ endOpacity: .25, duration: 2});
10669
10670 // common config options shown with default values
10671 el.fadeOut({
10672     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10673     easing: 'easeOut',
10674     duration: .5
10675     remove: false,
10676     useDisplay: false
10677 });
10678 </code></pre>
10679     * @param {Object} options (optional) Object literal with any of the Fx config options
10680     * @return {Roo.Element} The Element
10681     */
10682     fadeOut : function(o){
10683         var el = this.getFxEl();
10684         o = o || {};
10685         el.queueFx(o, function(){
10686             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10687                 o, null, .5, "easeOut", function(){
10688                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10689                      this.dom.style.display = "none";
10690                 }else{
10691                      this.dom.style.visibility = "hidden";
10692                 }
10693                 this.clearOpacity();
10694                 el.afterFx(o);
10695             });
10696         });
10697         return this;
10698     },
10699
10700    /**
10701     * Animates the transition of an element's dimensions from a starting height/width
10702     * to an ending height/width.
10703     * Usage:
10704 <pre><code>
10705 // change height and width to 100x100 pixels
10706 el.scale(100, 100);
10707
10708 // common config options shown with default values.  The height and width will default to
10709 // the element's existing values if passed as null.
10710 el.scale(
10711     [element's width],
10712     [element's height], {
10713     easing: 'easeOut',
10714     duration: .35
10715 });
10716 </code></pre>
10717     * @param {Number} width  The new width (pass undefined to keep the original width)
10718     * @param {Number} height  The new height (pass undefined to keep the original height)
10719     * @param {Object} options (optional) Object literal with any of the Fx config options
10720     * @return {Roo.Element} The Element
10721     */
10722     scale : function(w, h, o){
10723         this.shift(Roo.apply({}, o, {
10724             width: w,
10725             height: h
10726         }));
10727         return this;
10728     },
10729
10730    /**
10731     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10732     * Any of these properties not specified in the config object will not be changed.  This effect 
10733     * requires that at least one new dimension, position or opacity setting must be passed in on
10734     * the config object in order for the function to have any effect.
10735     * Usage:
10736 <pre><code>
10737 // slide the element horizontally to x position 200 while changing the height and opacity
10738 el.shift({ x: 200, height: 50, opacity: .8 });
10739
10740 // common config options shown with default values.
10741 el.shift({
10742     width: [element's width],
10743     height: [element's height],
10744     x: [element's x position],
10745     y: [element's y position],
10746     opacity: [element's opacity],
10747     easing: 'easeOut',
10748     duration: .35
10749 });
10750 </code></pre>
10751     * @param {Object} options  Object literal with any of the Fx config options
10752     * @return {Roo.Element} The Element
10753     */
10754     shift : function(o){
10755         var el = this.getFxEl();
10756         o = o || {};
10757         el.queueFx(o, function(){
10758             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10759             if(w !== undefined){
10760                 a.width = {to: this.adjustWidth(w)};
10761             }
10762             if(h !== undefined){
10763                 a.height = {to: this.adjustHeight(h)};
10764             }
10765             if(x !== undefined || y !== undefined){
10766                 a.points = {to: [
10767                     x !== undefined ? x : this.getX(),
10768                     y !== undefined ? y : this.getY()
10769                 ]};
10770             }
10771             if(op !== undefined){
10772                 a.opacity = {to: op};
10773             }
10774             if(o.xy !== undefined){
10775                 a.points = {to: o.xy};
10776             }
10777             arguments.callee.anim = this.fxanim(a,
10778                 o, 'motion', .35, "easeOut", function(){
10779                 el.afterFx(o);
10780             });
10781         });
10782         return this;
10783     },
10784
10785         /**
10786          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10787          * ending point of the effect.
10788          * Usage:
10789          *<pre><code>
10790 // default: slide the element downward while fading out
10791 el.ghost();
10792
10793 // custom: slide the element out to the right with a 2-second duration
10794 el.ghost('r', { duration: 2 });
10795
10796 // common config options shown with default values
10797 el.ghost('b', {
10798     easing: 'easeOut',
10799     duration: .5
10800     remove: false,
10801     useDisplay: false
10802 });
10803 </code></pre>
10804          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10805          * @param {Object} options (optional) Object literal with any of the Fx config options
10806          * @return {Roo.Element} The Element
10807          */
10808     ghost : function(anchor, o){
10809         var el = this.getFxEl();
10810         o = o || {};
10811
10812         el.queueFx(o, function(){
10813             anchor = anchor || "b";
10814
10815             // restore values after effect
10816             var r = this.getFxRestore();
10817             var w = this.getWidth(),
10818                 h = this.getHeight();
10819
10820             var st = this.dom.style;
10821
10822             var after = function(){
10823                 if(o.useDisplay){
10824                     el.setDisplayed(false);
10825                 }else{
10826                     el.hide();
10827                 }
10828
10829                 el.clearOpacity();
10830                 el.setPositioning(r.pos);
10831                 st.width = r.width;
10832                 st.height = r.height;
10833
10834                 el.afterFx(o);
10835             };
10836
10837             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10838             switch(anchor.toLowerCase()){
10839                 case "t":
10840                     pt.by = [0, -h];
10841                 break;
10842                 case "l":
10843                     pt.by = [-w, 0];
10844                 break;
10845                 case "r":
10846                     pt.by = [w, 0];
10847                 break;
10848                 case "b":
10849                     pt.by = [0, h];
10850                 break;
10851                 case "tl":
10852                     pt.by = [-w, -h];
10853                 break;
10854                 case "bl":
10855                     pt.by = [-w, h];
10856                 break;
10857                 case "br":
10858                     pt.by = [w, h];
10859                 break;
10860                 case "tr":
10861                     pt.by = [w, -h];
10862                 break;
10863             }
10864
10865             arguments.callee.anim = this.fxanim(a,
10866                 o,
10867                 'motion',
10868                 .5,
10869                 "easeOut", after);
10870         });
10871         return this;
10872     },
10873
10874         /**
10875          * Ensures that all effects queued after syncFx is called on the element are
10876          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10877          * @return {Roo.Element} The Element
10878          */
10879     syncFx : function(){
10880         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10881             block : false,
10882             concurrent : true,
10883             stopFx : false
10884         });
10885         return this;
10886     },
10887
10888         /**
10889          * Ensures that all effects queued after sequenceFx is called on the element are
10890          * run in sequence.  This is the opposite of {@link #syncFx}.
10891          * @return {Roo.Element} The Element
10892          */
10893     sequenceFx : function(){
10894         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10895             block : false,
10896             concurrent : false,
10897             stopFx : false
10898         });
10899         return this;
10900     },
10901
10902         /* @private */
10903     nextFx : function(){
10904         var ef = this.fxQueue[0];
10905         if(ef){
10906             ef.call(this);
10907         }
10908     },
10909
10910         /**
10911          * Returns true if the element has any effects actively running or queued, else returns false.
10912          * @return {Boolean} True if element has active effects, else false
10913          */
10914     hasActiveFx : function(){
10915         return this.fxQueue && this.fxQueue[0];
10916     },
10917
10918         /**
10919          * Stops any running effects and clears the element's internal effects queue if it contains
10920          * any additional effects that haven't started yet.
10921          * @return {Roo.Element} The Element
10922          */
10923     stopFx : function(){
10924         if(this.hasActiveFx()){
10925             var cur = this.fxQueue[0];
10926             if(cur && cur.anim && cur.anim.isAnimated()){
10927                 this.fxQueue = [cur]; // clear out others
10928                 cur.anim.stop(true);
10929             }
10930         }
10931         return this;
10932     },
10933
10934         /* @private */
10935     beforeFx : function(o){
10936         if(this.hasActiveFx() && !o.concurrent){
10937            if(o.stopFx){
10938                this.stopFx();
10939                return true;
10940            }
10941            return false;
10942         }
10943         return true;
10944     },
10945
10946         /**
10947          * Returns true if the element is currently blocking so that no other effect can be queued
10948          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10949          * used to ensure that an effect initiated by a user action runs to completion prior to the
10950          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10951          * @return {Boolean} True if blocking, else false
10952          */
10953     hasFxBlock : function(){
10954         var q = this.fxQueue;
10955         return q && q[0] && q[0].block;
10956     },
10957
10958         /* @private */
10959     queueFx : function(o, fn){
10960         if(!this.fxQueue){
10961             this.fxQueue = [];
10962         }
10963         if(!this.hasFxBlock()){
10964             Roo.applyIf(o, this.fxDefaults);
10965             if(!o.concurrent){
10966                 var run = this.beforeFx(o);
10967                 fn.block = o.block;
10968                 this.fxQueue.push(fn);
10969                 if(run){
10970                     this.nextFx();
10971                 }
10972             }else{
10973                 fn.call(this);
10974             }
10975         }
10976         return this;
10977     },
10978
10979         /* @private */
10980     fxWrap : function(pos, o, vis){
10981         var wrap;
10982         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10983             var wrapXY;
10984             if(o.fixPosition){
10985                 wrapXY = this.getXY();
10986             }
10987             var div = document.createElement("div");
10988             div.style.visibility = vis;
10989             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10990             wrap.setPositioning(pos);
10991             if(wrap.getStyle("position") == "static"){
10992                 wrap.position("relative");
10993             }
10994             this.clearPositioning('auto');
10995             wrap.clip();
10996             wrap.dom.appendChild(this.dom);
10997             if(wrapXY){
10998                 wrap.setXY(wrapXY);
10999             }
11000         }
11001         return wrap;
11002     },
11003
11004         /* @private */
11005     fxUnwrap : function(wrap, pos, o){
11006         this.clearPositioning();
11007         this.setPositioning(pos);
11008         if(!o.wrap){
11009             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11010             wrap.remove();
11011         }
11012     },
11013
11014         /* @private */
11015     getFxRestore : function(){
11016         var st = this.dom.style;
11017         return {pos: this.getPositioning(), width: st.width, height : st.height};
11018     },
11019
11020         /* @private */
11021     afterFx : function(o){
11022         if(o.afterStyle){
11023             this.applyStyles(o.afterStyle);
11024         }
11025         if(o.afterCls){
11026             this.addClass(o.afterCls);
11027         }
11028         if(o.remove === true){
11029             this.remove();
11030         }
11031         Roo.callback(o.callback, o.scope, [this]);
11032         if(!o.concurrent){
11033             this.fxQueue.shift();
11034             this.nextFx();
11035         }
11036     },
11037
11038         /* @private */
11039     getFxEl : function(){ // support for composite element fx
11040         return Roo.get(this.dom);
11041     },
11042
11043         /* @private */
11044     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11045         animType = animType || 'run';
11046         opt = opt || {};
11047         var anim = Roo.lib.Anim[animType](
11048             this.dom, args,
11049             (opt.duration || defaultDur) || .35,
11050             (opt.easing || defaultEase) || 'easeOut',
11051             function(){
11052                 Roo.callback(cb, this);
11053             },
11054             this
11055         );
11056         opt.anim = anim;
11057         return anim;
11058     }
11059 };
11060
11061 // backwords compat
11062 Roo.Fx.resize = Roo.Fx.scale;
11063
11064 //When included, Roo.Fx is automatically applied to Element so that all basic
11065 //effects are available directly via the Element API
11066 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11067  * Based on:
11068  * Ext JS Library 1.1.1
11069  * Copyright(c) 2006-2007, Ext JS, LLC.
11070  *
11071  * Originally Released Under LGPL - original licence link has changed is not relivant.
11072  *
11073  * Fork - LGPL
11074  * <script type="text/javascript">
11075  */
11076
11077
11078 /**
11079  * @class Roo.CompositeElement
11080  * Standard composite class. Creates a Roo.Element for every element in the collection.
11081  * <br><br>
11082  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11083  * actions will be performed on all the elements in this collection.</b>
11084  * <br><br>
11085  * All methods return <i>this</i> and can be chained.
11086  <pre><code>
11087  var els = Roo.select("#some-el div.some-class", true);
11088  // or select directly from an existing element
11089  var el = Roo.get('some-el');
11090  el.select('div.some-class', true);
11091
11092  els.setWidth(100); // all elements become 100 width
11093  els.hide(true); // all elements fade out and hide
11094  // or
11095  els.setWidth(100).hide(true);
11096  </code></pre>
11097  */
11098 Roo.CompositeElement = function(els){
11099     this.elements = [];
11100     this.addElements(els);
11101 };
11102 Roo.CompositeElement.prototype = {
11103     isComposite: true,
11104     addElements : function(els){
11105         if(!els) {
11106             return this;
11107         }
11108         if(typeof els == "string"){
11109             els = Roo.Element.selectorFunction(els);
11110         }
11111         var yels = this.elements;
11112         var index = yels.length-1;
11113         for(var i = 0, len = els.length; i < len; i++) {
11114                 yels[++index] = Roo.get(els[i]);
11115         }
11116         return this;
11117     },
11118
11119     /**
11120     * Clears this composite and adds the elements returned by the passed selector.
11121     * @param {String/Array} els A string CSS selector, an array of elements or an element
11122     * @return {CompositeElement} this
11123     */
11124     fill : function(els){
11125         this.elements = [];
11126         this.add(els);
11127         return this;
11128     },
11129
11130     /**
11131     * Filters this composite to only elements that match the passed selector.
11132     * @param {String} selector A string CSS selector
11133     * @param {Boolean} inverse return inverse filter (not matches)
11134     * @return {CompositeElement} this
11135     */
11136     filter : function(selector, inverse){
11137         var els = [];
11138         inverse = inverse || false;
11139         this.each(function(el){
11140             var match = inverse ? !el.is(selector) : el.is(selector);
11141             if(match){
11142                 els[els.length] = el.dom;
11143             }
11144         });
11145         this.fill(els);
11146         return this;
11147     },
11148
11149     invoke : function(fn, args){
11150         var els = this.elements;
11151         for(var i = 0, len = els.length; i < len; i++) {
11152                 Roo.Element.prototype[fn].apply(els[i], args);
11153         }
11154         return this;
11155     },
11156     /**
11157     * Adds elements to this composite.
11158     * @param {String/Array} els A string CSS selector, an array of elements or an element
11159     * @return {CompositeElement} this
11160     */
11161     add : function(els){
11162         if(typeof els == "string"){
11163             this.addElements(Roo.Element.selectorFunction(els));
11164         }else if(els.length !== undefined){
11165             this.addElements(els);
11166         }else{
11167             this.addElements([els]);
11168         }
11169         return this;
11170     },
11171     /**
11172     * Calls the passed function passing (el, this, index) for each element in this composite.
11173     * @param {Function} fn The function to call
11174     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11175     * @return {CompositeElement} this
11176     */
11177     each : function(fn, scope){
11178         var els = this.elements;
11179         for(var i = 0, len = els.length; i < len; i++){
11180             if(fn.call(scope || els[i], els[i], this, i) === false) {
11181                 break;
11182             }
11183         }
11184         return this;
11185     },
11186
11187     /**
11188      * Returns the Element object at the specified index
11189      * @param {Number} index
11190      * @return {Roo.Element}
11191      */
11192     item : function(index){
11193         return this.elements[index] || null;
11194     },
11195
11196     /**
11197      * Returns the first Element
11198      * @return {Roo.Element}
11199      */
11200     first : function(){
11201         return this.item(0);
11202     },
11203
11204     /**
11205      * Returns the last Element
11206      * @return {Roo.Element}
11207      */
11208     last : function(){
11209         return this.item(this.elements.length-1);
11210     },
11211
11212     /**
11213      * Returns the number of elements in this composite
11214      * @return Number
11215      */
11216     getCount : function(){
11217         return this.elements.length;
11218     },
11219
11220     /**
11221      * Returns true if this composite contains the passed element
11222      * @return Boolean
11223      */
11224     contains : function(el){
11225         return this.indexOf(el) !== -1;
11226     },
11227
11228     /**
11229      * Returns true if this composite contains the passed element
11230      * @return Boolean
11231      */
11232     indexOf : function(el){
11233         return this.elements.indexOf(Roo.get(el));
11234     },
11235
11236
11237     /**
11238     * Removes the specified element(s).
11239     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11240     * or an array of any of those.
11241     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11242     * @return {CompositeElement} this
11243     */
11244     removeElement : function(el, removeDom){
11245         if(el instanceof Array){
11246             for(var i = 0, len = el.length; i < len; i++){
11247                 this.removeElement(el[i]);
11248             }
11249             return this;
11250         }
11251         var index = typeof el == 'number' ? el : this.indexOf(el);
11252         if(index !== -1){
11253             if(removeDom){
11254                 var d = this.elements[index];
11255                 if(d.dom){
11256                     d.remove();
11257                 }else{
11258                     d.parentNode.removeChild(d);
11259                 }
11260             }
11261             this.elements.splice(index, 1);
11262         }
11263         return this;
11264     },
11265
11266     /**
11267     * Replaces the specified element with the passed element.
11268     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11269     * to replace.
11270     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11271     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11272     * @return {CompositeElement} this
11273     */
11274     replaceElement : function(el, replacement, domReplace){
11275         var index = typeof el == 'number' ? el : this.indexOf(el);
11276         if(index !== -1){
11277             if(domReplace){
11278                 this.elements[index].replaceWith(replacement);
11279             }else{
11280                 this.elements.splice(index, 1, Roo.get(replacement))
11281             }
11282         }
11283         return this;
11284     },
11285
11286     /**
11287      * Removes all elements.
11288      */
11289     clear : function(){
11290         this.elements = [];
11291     }
11292 };
11293 (function(){
11294     Roo.CompositeElement.createCall = function(proto, fnName){
11295         if(!proto[fnName]){
11296             proto[fnName] = function(){
11297                 return this.invoke(fnName, arguments);
11298             };
11299         }
11300     };
11301     for(var fnName in Roo.Element.prototype){
11302         if(typeof Roo.Element.prototype[fnName] == "function"){
11303             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11304         }
11305     };
11306 })();
11307 /*
11308  * Based on:
11309  * Ext JS Library 1.1.1
11310  * Copyright(c) 2006-2007, Ext JS, LLC.
11311  *
11312  * Originally Released Under LGPL - original licence link has changed is not relivant.
11313  *
11314  * Fork - LGPL
11315  * <script type="text/javascript">
11316  */
11317
11318 /**
11319  * @class Roo.CompositeElementLite
11320  * @extends Roo.CompositeElement
11321  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11322  <pre><code>
11323  var els = Roo.select("#some-el div.some-class");
11324  // or select directly from an existing element
11325  var el = Roo.get('some-el');
11326  el.select('div.some-class');
11327
11328  els.setWidth(100); // all elements become 100 width
11329  els.hide(true); // all elements fade out and hide
11330  // or
11331  els.setWidth(100).hide(true);
11332  </code></pre><br><br>
11333  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11334  * actions will be performed on all the elements in this collection.</b>
11335  */
11336 Roo.CompositeElementLite = function(els){
11337     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11338     this.el = new Roo.Element.Flyweight();
11339 };
11340 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11341     addElements : function(els){
11342         if(els){
11343             if(els instanceof Array){
11344                 this.elements = this.elements.concat(els);
11345             }else{
11346                 var yels = this.elements;
11347                 var index = yels.length-1;
11348                 for(var i = 0, len = els.length; i < len; i++) {
11349                     yels[++index] = els[i];
11350                 }
11351             }
11352         }
11353         return this;
11354     },
11355     invoke : function(fn, args){
11356         var els = this.elements;
11357         var el = this.el;
11358         for(var i = 0, len = els.length; i < len; i++) {
11359             el.dom = els[i];
11360                 Roo.Element.prototype[fn].apply(el, args);
11361         }
11362         return this;
11363     },
11364     /**
11365      * Returns a flyweight Element of the dom element object at the specified index
11366      * @param {Number} index
11367      * @return {Roo.Element}
11368      */
11369     item : function(index){
11370         if(!this.elements[index]){
11371             return null;
11372         }
11373         this.el.dom = this.elements[index];
11374         return this.el;
11375     },
11376
11377     // fixes scope with flyweight
11378     addListener : function(eventName, handler, scope, opt){
11379         var els = this.elements;
11380         for(var i = 0, len = els.length; i < len; i++) {
11381             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11382         }
11383         return this;
11384     },
11385
11386     /**
11387     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11388     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11389     * a reference to the dom node, use el.dom.</b>
11390     * @param {Function} fn The function to call
11391     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11392     * @return {CompositeElement} this
11393     */
11394     each : function(fn, scope){
11395         var els = this.elements;
11396         var el = this.el;
11397         for(var i = 0, len = els.length; i < len; i++){
11398             el.dom = els[i];
11399                 if(fn.call(scope || el, el, this, i) === false){
11400                 break;
11401             }
11402         }
11403         return this;
11404     },
11405
11406     indexOf : function(el){
11407         return this.elements.indexOf(Roo.getDom(el));
11408     },
11409
11410     replaceElement : function(el, replacement, domReplace){
11411         var index = typeof el == 'number' ? el : this.indexOf(el);
11412         if(index !== -1){
11413             replacement = Roo.getDom(replacement);
11414             if(domReplace){
11415                 var d = this.elements[index];
11416                 d.parentNode.insertBefore(replacement, d);
11417                 d.parentNode.removeChild(d);
11418             }
11419             this.elements.splice(index, 1, replacement);
11420         }
11421         return this;
11422     }
11423 });
11424 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11425
11426 /*
11427  * Based on:
11428  * Ext JS Library 1.1.1
11429  * Copyright(c) 2006-2007, Ext JS, LLC.
11430  *
11431  * Originally Released Under LGPL - original licence link has changed is not relivant.
11432  *
11433  * Fork - LGPL
11434  * <script type="text/javascript">
11435  */
11436
11437  
11438
11439 /**
11440  * @class Roo.data.Connection
11441  * @extends Roo.util.Observable
11442  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11443  * either to a configured URL, or to a URL specified at request time.<br><br>
11444  * <p>
11445  * Requests made by this class are asynchronous, and will return immediately. No data from
11446  * the server will be available to the statement immediately following the {@link #request} call.
11447  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11448  * <p>
11449  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11450  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11451  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11452  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11453  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11454  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11455  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11456  * standard DOM methods.
11457  * @constructor
11458  * @param {Object} config a configuration object.
11459  */
11460 Roo.data.Connection = function(config){
11461     Roo.apply(this, config);
11462     this.addEvents({
11463         /**
11464          * @event beforerequest
11465          * Fires before a network request is made to retrieve a data object.
11466          * @param {Connection} conn This Connection object.
11467          * @param {Object} options The options config object passed to the {@link #request} method.
11468          */
11469         "beforerequest" : true,
11470         /**
11471          * @event requestcomplete
11472          * Fires if the request was successfully completed.
11473          * @param {Connection} conn This Connection object.
11474          * @param {Object} response The XHR object containing the response data.
11475          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11476          * @param {Object} options The options config object passed to the {@link #request} method.
11477          */
11478         "requestcomplete" : true,
11479         /**
11480          * @event requestexception
11481          * Fires if an error HTTP status was returned from the server.
11482          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11483          * @param {Connection} conn This Connection object.
11484          * @param {Object} response The XHR object containing the response data.
11485          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11486          * @param {Object} options The options config object passed to the {@link #request} method.
11487          */
11488         "requestexception" : true
11489     });
11490     Roo.data.Connection.superclass.constructor.call(this);
11491 };
11492
11493 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11494     /**
11495      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11496      */
11497     /**
11498      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11499      * extra parameters to each request made by this object. (defaults to undefined)
11500      */
11501     /**
11502      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11503      *  to each request made by this object. (defaults to undefined)
11504      */
11505     /**
11506      * @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)
11507      */
11508     /**
11509      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11510      */
11511     timeout : 30000,
11512     /**
11513      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11514      * @type Boolean
11515      */
11516     autoAbort:false,
11517
11518     /**
11519      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11520      * @type Boolean
11521      */
11522     disableCaching: true,
11523
11524     /**
11525      * Sends an HTTP request to a remote server.
11526      * @param {Object} options An object which may contain the following properties:<ul>
11527      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11528      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11529      * request, a url encoded string or a function to call to get either.</li>
11530      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11531      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11532      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11533      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11534      * <li>options {Object} The parameter to the request call.</li>
11535      * <li>success {Boolean} True if the request succeeded.</li>
11536      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11537      * </ul></li>
11538      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11539      * The callback is passed the following parameters:<ul>
11540      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11541      * <li>options {Object} The parameter to the request call.</li>
11542      * </ul></li>
11543      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11544      * The callback is passed the following parameters:<ul>
11545      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11546      * <li>options {Object} The parameter to the request call.</li>
11547      * </ul></li>
11548      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11549      * for the callback function. Defaults to the browser window.</li>
11550      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11551      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11552      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11553      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11554      * params for the post data. Any params will be appended to the URL.</li>
11555      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11556      * </ul>
11557      * @return {Number} transactionId
11558      */
11559     request : function(o){
11560         if(this.fireEvent("beforerequest", this, o) !== false){
11561             var p = o.params;
11562
11563             if(typeof p == "function"){
11564                 p = p.call(o.scope||window, o);
11565             }
11566             if(typeof p == "object"){
11567                 p = Roo.urlEncode(o.params);
11568             }
11569             if(this.extraParams){
11570                 var extras = Roo.urlEncode(this.extraParams);
11571                 p = p ? (p + '&' + extras) : extras;
11572             }
11573
11574             var url = o.url || this.url;
11575             if(typeof url == 'function'){
11576                 url = url.call(o.scope||window, o);
11577             }
11578
11579             if(o.form){
11580                 var form = Roo.getDom(o.form);
11581                 url = url || form.action;
11582
11583                 var enctype = form.getAttribute("enctype");
11584                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11585                     return this.doFormUpload(o, p, url);
11586                 }
11587                 var f = Roo.lib.Ajax.serializeForm(form);
11588                 p = p ? (p + '&' + f) : f;
11589             }
11590
11591             var hs = o.headers;
11592             if(this.defaultHeaders){
11593                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11594                 if(!o.headers){
11595                     o.headers = hs;
11596                 }
11597             }
11598
11599             var cb = {
11600                 success: this.handleResponse,
11601                 failure: this.handleFailure,
11602                 scope: this,
11603                 argument: {options: o},
11604                 timeout : o.timeout || this.timeout
11605             };
11606
11607             var method = o.method||this.method||(p ? "POST" : "GET");
11608
11609             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11610                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11611             }
11612
11613             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11614                 if(o.autoAbort){
11615                     this.abort();
11616                 }
11617             }else if(this.autoAbort !== false){
11618                 this.abort();
11619             }
11620
11621             if((method == 'GET' && p) || o.xmlData){
11622                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11623                 p = '';
11624             }
11625             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11626             return this.transId;
11627         }else{
11628             Roo.callback(o.callback, o.scope, [o, null, null]);
11629             return null;
11630         }
11631     },
11632
11633     /**
11634      * Determine whether this object has a request outstanding.
11635      * @param {Number} transactionId (Optional) defaults to the last transaction
11636      * @return {Boolean} True if there is an outstanding request.
11637      */
11638     isLoading : function(transId){
11639         if(transId){
11640             return Roo.lib.Ajax.isCallInProgress(transId);
11641         }else{
11642             return this.transId ? true : false;
11643         }
11644     },
11645
11646     /**
11647      * Aborts any outstanding request.
11648      * @param {Number} transactionId (Optional) defaults to the last transaction
11649      */
11650     abort : function(transId){
11651         if(transId || this.isLoading()){
11652             Roo.lib.Ajax.abort(transId || this.transId);
11653         }
11654     },
11655
11656     // private
11657     handleResponse : function(response){
11658         this.transId = false;
11659         var options = response.argument.options;
11660         response.argument = options ? options.argument : null;
11661         this.fireEvent("requestcomplete", this, response, options);
11662         Roo.callback(options.success, options.scope, [response, options]);
11663         Roo.callback(options.callback, options.scope, [options, true, response]);
11664     },
11665
11666     // private
11667     handleFailure : function(response, e){
11668         this.transId = false;
11669         var options = response.argument.options;
11670         response.argument = options ? options.argument : null;
11671         this.fireEvent("requestexception", this, response, options, e);
11672         Roo.callback(options.failure, options.scope, [response, options]);
11673         Roo.callback(options.callback, options.scope, [options, false, response]);
11674     },
11675
11676     // private
11677     doFormUpload : function(o, ps, url){
11678         var id = Roo.id();
11679         var frame = document.createElement('iframe');
11680         frame.id = id;
11681         frame.name = id;
11682         frame.className = 'x-hidden';
11683         if(Roo.isIE){
11684             frame.src = Roo.SSL_SECURE_URL;
11685         }
11686         document.body.appendChild(frame);
11687
11688         if(Roo.isIE){
11689            document.frames[id].name = id;
11690         }
11691
11692         var form = Roo.getDom(o.form);
11693         form.target = id;
11694         form.method = 'POST';
11695         form.enctype = form.encoding = 'multipart/form-data';
11696         if(url){
11697             form.action = url;
11698         }
11699
11700         var hiddens, hd;
11701         if(ps){ // add dynamic params
11702             hiddens = [];
11703             ps = Roo.urlDecode(ps, false);
11704             for(var k in ps){
11705                 if(ps.hasOwnProperty(k)){
11706                     hd = document.createElement('input');
11707                     hd.type = 'hidden';
11708                     hd.name = k;
11709                     hd.value = ps[k];
11710                     form.appendChild(hd);
11711                     hiddens.push(hd);
11712                 }
11713             }
11714         }
11715
11716         function cb(){
11717             var r = {  // bogus response object
11718                 responseText : '',
11719                 responseXML : null
11720             };
11721
11722             r.argument = o ? o.argument : null;
11723
11724             try { //
11725                 var doc;
11726                 if(Roo.isIE){
11727                     doc = frame.contentWindow.document;
11728                 }else {
11729                     doc = (frame.contentDocument || window.frames[id].document);
11730                 }
11731                 if(doc && doc.body){
11732                     r.responseText = doc.body.innerHTML;
11733                 }
11734                 if(doc && doc.XMLDocument){
11735                     r.responseXML = doc.XMLDocument;
11736                 }else {
11737                     r.responseXML = doc;
11738                 }
11739             }
11740             catch(e) {
11741                 // ignore
11742             }
11743
11744             Roo.EventManager.removeListener(frame, 'load', cb, this);
11745
11746             this.fireEvent("requestcomplete", this, r, o);
11747             Roo.callback(o.success, o.scope, [r, o]);
11748             Roo.callback(o.callback, o.scope, [o, true, r]);
11749
11750             setTimeout(function(){document.body.removeChild(frame);}, 100);
11751         }
11752
11753         Roo.EventManager.on(frame, 'load', cb, this);
11754         form.submit();
11755
11756         if(hiddens){ // remove dynamic params
11757             for(var i = 0, len = hiddens.length; i < len; i++){
11758                 form.removeChild(hiddens[i]);
11759             }
11760         }
11761     }
11762 });
11763 /*
11764  * Based on:
11765  * Ext JS Library 1.1.1
11766  * Copyright(c) 2006-2007, Ext JS, LLC.
11767  *
11768  * Originally Released Under LGPL - original licence link has changed is not relivant.
11769  *
11770  * Fork - LGPL
11771  * <script type="text/javascript">
11772  */
11773  
11774 /**
11775  * Global Ajax request class.
11776  * 
11777  * @class Roo.Ajax
11778  * @extends Roo.data.Connection
11779  * @static
11780  * 
11781  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11782  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11783  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11784  * @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)
11785  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11786  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11787  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11788  */
11789 Roo.Ajax = new Roo.data.Connection({
11790     // fix up the docs
11791     /**
11792      * @scope Roo.Ajax
11793      * @type {Boolear} 
11794      */
11795     autoAbort : false,
11796
11797     /**
11798      * Serialize the passed form into a url encoded string
11799      * @scope Roo.Ajax
11800      * @param {String/HTMLElement} form
11801      * @return {String}
11802      */
11803     serializeForm : function(form){
11804         return Roo.lib.Ajax.serializeForm(form);
11805     }
11806 });/*
11807  * Based on:
11808  * Ext JS Library 1.1.1
11809  * Copyright(c) 2006-2007, Ext JS, LLC.
11810  *
11811  * Originally Released Under LGPL - original licence link has changed is not relivant.
11812  *
11813  * Fork - LGPL
11814  * <script type="text/javascript">
11815  */
11816
11817  
11818 /**
11819  * @class Roo.UpdateManager
11820  * @extends Roo.util.Observable
11821  * Provides AJAX-style update for Element object.<br><br>
11822  * Usage:<br>
11823  * <pre><code>
11824  * // Get it from a Roo.Element object
11825  * var el = Roo.get("foo");
11826  * var mgr = el.getUpdateManager();
11827  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11828  * ...
11829  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11830  * <br>
11831  * // or directly (returns the same UpdateManager instance)
11832  * var mgr = new Roo.UpdateManager("myElementId");
11833  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11834  * mgr.on("update", myFcnNeedsToKnow);
11835  * <br>
11836    // short handed call directly from the element object
11837    Roo.get("foo").load({
11838         url: "bar.php",
11839         scripts:true,
11840         params: "for=bar",
11841         text: "Loading Foo..."
11842    });
11843  * </code></pre>
11844  * @constructor
11845  * Create new UpdateManager directly.
11846  * @param {String/HTMLElement/Roo.Element} el The element to update
11847  * @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).
11848  */
11849 Roo.UpdateManager = function(el, forceNew){
11850     el = Roo.get(el);
11851     if(!forceNew && el.updateManager){
11852         return el.updateManager;
11853     }
11854     /**
11855      * The Element object
11856      * @type Roo.Element
11857      */
11858     this.el = el;
11859     /**
11860      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11861      * @type String
11862      */
11863     this.defaultUrl = null;
11864
11865     this.addEvents({
11866         /**
11867          * @event beforeupdate
11868          * Fired before an update is made, return false from your handler and the update is cancelled.
11869          * @param {Roo.Element} el
11870          * @param {String/Object/Function} url
11871          * @param {String/Object} params
11872          */
11873         "beforeupdate": true,
11874         /**
11875          * @event update
11876          * Fired after successful update is made.
11877          * @param {Roo.Element} el
11878          * @param {Object} oResponseObject The response Object
11879          */
11880         "update": true,
11881         /**
11882          * @event failure
11883          * Fired on update failure.
11884          * @param {Roo.Element} el
11885          * @param {Object} oResponseObject The response Object
11886          */
11887         "failure": true
11888     });
11889     var d = Roo.UpdateManager.defaults;
11890     /**
11891      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11892      * @type String
11893      */
11894     this.sslBlankUrl = d.sslBlankUrl;
11895     /**
11896      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11897      * @type Boolean
11898      */
11899     this.disableCaching = d.disableCaching;
11900     /**
11901      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11902      * @type String
11903      */
11904     this.indicatorText = d.indicatorText;
11905     /**
11906      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11907      * @type String
11908      */
11909     this.showLoadIndicator = d.showLoadIndicator;
11910     /**
11911      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11912      * @type Number
11913      */
11914     this.timeout = d.timeout;
11915
11916     /**
11917      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11918      * @type Boolean
11919      */
11920     this.loadScripts = d.loadScripts;
11921
11922     /**
11923      * Transaction object of current executing transaction
11924      */
11925     this.transaction = null;
11926
11927     /**
11928      * @private
11929      */
11930     this.autoRefreshProcId = null;
11931     /**
11932      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11933      * @type Function
11934      */
11935     this.refreshDelegate = this.refresh.createDelegate(this);
11936     /**
11937      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11938      * @type Function
11939      */
11940     this.updateDelegate = this.update.createDelegate(this);
11941     /**
11942      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11943      * @type Function
11944      */
11945     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11946     /**
11947      * @private
11948      */
11949     this.successDelegate = this.processSuccess.createDelegate(this);
11950     /**
11951      * @private
11952      */
11953     this.failureDelegate = this.processFailure.createDelegate(this);
11954
11955     if(!this.renderer){
11956      /**
11957       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11958       */
11959     this.renderer = new Roo.UpdateManager.BasicRenderer();
11960     }
11961     
11962     Roo.UpdateManager.superclass.constructor.call(this);
11963 };
11964
11965 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11966     /**
11967      * Get the Element this UpdateManager is bound to
11968      * @return {Roo.Element} The element
11969      */
11970     getEl : function(){
11971         return this.el;
11972     },
11973     /**
11974      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11975      * @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:
11976 <pre><code>
11977 um.update({<br/>
11978     url: "your-url.php",<br/>
11979     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11980     callback: yourFunction,<br/>
11981     scope: yourObject, //(optional scope)  <br/>
11982     discardUrl: false, <br/>
11983     nocache: false,<br/>
11984     text: "Loading...",<br/>
11985     timeout: 30,<br/>
11986     scripts: false<br/>
11987 });
11988 </code></pre>
11989      * The only required property is url. The optional properties nocache, text and scripts
11990      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11991      * @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}
11992      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11993      * @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.
11994      */
11995     update : function(url, params, callback, discardUrl){
11996         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11997             var method = this.method,
11998                 cfg;
11999             if(typeof url == "object"){ // must be config object
12000                 cfg = url;
12001                 url = cfg.url;
12002                 params = params || cfg.params;
12003                 callback = callback || cfg.callback;
12004                 discardUrl = discardUrl || cfg.discardUrl;
12005                 if(callback && cfg.scope){
12006                     callback = callback.createDelegate(cfg.scope);
12007                 }
12008                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12009                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12010                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12011                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12012                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12013             }
12014             this.showLoading();
12015             if(!discardUrl){
12016                 this.defaultUrl = url;
12017             }
12018             if(typeof url == "function"){
12019                 url = url.call(this);
12020             }
12021
12022             method = method || (params ? "POST" : "GET");
12023             if(method == "GET"){
12024                 url = this.prepareUrl(url);
12025             }
12026
12027             var o = Roo.apply(cfg ||{}, {
12028                 url : url,
12029                 params: params,
12030                 success: this.successDelegate,
12031                 failure: this.failureDelegate,
12032                 callback: undefined,
12033                 timeout: (this.timeout*1000),
12034                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12035             });
12036             Roo.log("updated manager called with timeout of " + o.timeout);
12037             this.transaction = Roo.Ajax.request(o);
12038         }
12039     },
12040
12041     /**
12042      * 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.
12043      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12044      * @param {String/HTMLElement} form The form Id or form element
12045      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12046      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12047      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12048      */
12049     formUpdate : function(form, url, reset, callback){
12050         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12051             if(typeof url == "function"){
12052                 url = url.call(this);
12053             }
12054             form = Roo.getDom(form);
12055             this.transaction = Roo.Ajax.request({
12056                 form: form,
12057                 url:url,
12058                 success: this.successDelegate,
12059                 failure: this.failureDelegate,
12060                 timeout: (this.timeout*1000),
12061                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12062             });
12063             this.showLoading.defer(1, this);
12064         }
12065     },
12066
12067     /**
12068      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12069      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12070      */
12071     refresh : function(callback){
12072         if(this.defaultUrl == null){
12073             return;
12074         }
12075         this.update(this.defaultUrl, null, callback, true);
12076     },
12077
12078     /**
12079      * Set this element to auto refresh.
12080      * @param {Number} interval How often to update (in seconds).
12081      * @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)
12082      * @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}
12083      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12084      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12085      */
12086     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12087         if(refreshNow){
12088             this.update(url || this.defaultUrl, params, callback, true);
12089         }
12090         if(this.autoRefreshProcId){
12091             clearInterval(this.autoRefreshProcId);
12092         }
12093         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12094     },
12095
12096     /**
12097      * Stop auto refresh on this element.
12098      */
12099      stopAutoRefresh : function(){
12100         if(this.autoRefreshProcId){
12101             clearInterval(this.autoRefreshProcId);
12102             delete this.autoRefreshProcId;
12103         }
12104     },
12105
12106     isAutoRefreshing : function(){
12107        return this.autoRefreshProcId ? true : false;
12108     },
12109     /**
12110      * Called to update the element to "Loading" state. Override to perform custom action.
12111      */
12112     showLoading : function(){
12113         if(this.showLoadIndicator){
12114             this.el.update(this.indicatorText);
12115         }
12116     },
12117
12118     /**
12119      * Adds unique parameter to query string if disableCaching = true
12120      * @private
12121      */
12122     prepareUrl : function(url){
12123         if(this.disableCaching){
12124             var append = "_dc=" + (new Date().getTime());
12125             if(url.indexOf("?") !== -1){
12126                 url += "&" + append;
12127             }else{
12128                 url += "?" + append;
12129             }
12130         }
12131         return url;
12132     },
12133
12134     /**
12135      * @private
12136      */
12137     processSuccess : function(response){
12138         this.transaction = null;
12139         if(response.argument.form && response.argument.reset){
12140             try{ // put in try/catch since some older FF releases had problems with this
12141                 response.argument.form.reset();
12142             }catch(e){}
12143         }
12144         if(this.loadScripts){
12145             this.renderer.render(this.el, response, this,
12146                 this.updateComplete.createDelegate(this, [response]));
12147         }else{
12148             this.renderer.render(this.el, response, this);
12149             this.updateComplete(response);
12150         }
12151     },
12152
12153     updateComplete : function(response){
12154         this.fireEvent("update", this.el, response);
12155         if(typeof response.argument.callback == "function"){
12156             response.argument.callback(this.el, true, response);
12157         }
12158     },
12159
12160     /**
12161      * @private
12162      */
12163     processFailure : function(response){
12164         this.transaction = null;
12165         this.fireEvent("failure", this.el, response);
12166         if(typeof response.argument.callback == "function"){
12167             response.argument.callback(this.el, false, response);
12168         }
12169     },
12170
12171     /**
12172      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12173      * @param {Object} renderer The object implementing the render() method
12174      */
12175     setRenderer : function(renderer){
12176         this.renderer = renderer;
12177     },
12178
12179     getRenderer : function(){
12180        return this.renderer;
12181     },
12182
12183     /**
12184      * Set the defaultUrl used for updates
12185      * @param {String/Function} defaultUrl The url or a function to call to get the url
12186      */
12187     setDefaultUrl : function(defaultUrl){
12188         this.defaultUrl = defaultUrl;
12189     },
12190
12191     /**
12192      * Aborts the executing transaction
12193      */
12194     abort : function(){
12195         if(this.transaction){
12196             Roo.Ajax.abort(this.transaction);
12197         }
12198     },
12199
12200     /**
12201      * Returns true if an update is in progress
12202      * @return {Boolean}
12203      */
12204     isUpdating : function(){
12205         if(this.transaction){
12206             return Roo.Ajax.isLoading(this.transaction);
12207         }
12208         return false;
12209     }
12210 });
12211
12212 /**
12213  * @class Roo.UpdateManager.defaults
12214  * @static (not really - but it helps the doc tool)
12215  * The defaults collection enables customizing the default properties of UpdateManager
12216  */
12217    Roo.UpdateManager.defaults = {
12218        /**
12219          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12220          * @type Number
12221          */
12222          timeout : 30,
12223
12224          /**
12225          * True to process scripts by default (Defaults to false).
12226          * @type Boolean
12227          */
12228         loadScripts : false,
12229
12230         /**
12231         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12232         * @type String
12233         */
12234         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12235         /**
12236          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12237          * @type Boolean
12238          */
12239         disableCaching : false,
12240         /**
12241          * Whether to show indicatorText when loading (Defaults to true).
12242          * @type Boolean
12243          */
12244         showLoadIndicator : true,
12245         /**
12246          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12247          * @type String
12248          */
12249         indicatorText : '<div class="loading-indicator">Loading...</div>'
12250    };
12251
12252 /**
12253  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12254  *Usage:
12255  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12256  * @param {String/HTMLElement/Roo.Element} el The element to update
12257  * @param {String} url The url
12258  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12259  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12260  * @static
12261  * @deprecated
12262  * @member Roo.UpdateManager
12263  */
12264 Roo.UpdateManager.updateElement = function(el, url, params, options){
12265     var um = Roo.get(el, true).getUpdateManager();
12266     Roo.apply(um, options);
12267     um.update(url, params, options ? options.callback : null);
12268 };
12269 // alias for backwards compat
12270 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12271 /**
12272  * @class Roo.UpdateManager.BasicRenderer
12273  * Default Content renderer. Updates the elements innerHTML with the responseText.
12274  */
12275 Roo.UpdateManager.BasicRenderer = function(){};
12276
12277 Roo.UpdateManager.BasicRenderer.prototype = {
12278     /**
12279      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12280      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12281      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12282      * @param {Roo.Element} el The element being rendered
12283      * @param {Object} response The YUI Connect response object
12284      * @param {UpdateManager} updateManager The calling update manager
12285      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12286      */
12287      render : function(el, response, updateManager, callback){
12288         el.update(response.responseText, updateManager.loadScripts, callback);
12289     }
12290 };
12291 /*
12292  * Based on:
12293  * Roo JS
12294  * (c)) Alan Knowles
12295  * Licence : LGPL
12296  */
12297
12298
12299 /**
12300  * @class Roo.DomTemplate
12301  * @extends Roo.Template
12302  * An effort at a dom based template engine..
12303  *
12304  * Similar to XTemplate, except it uses dom parsing to create the template..
12305  *
12306  * Supported features:
12307  *
12308  *  Tags:
12309
12310 <pre><code>
12311       {a_variable} - output encoded.
12312       {a_variable.format:("Y-m-d")} - call a method on the variable
12313       {a_variable:raw} - unencoded output
12314       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12315       {a_variable:this.method_on_template(...)} - call a method on the template object.
12316  
12317 </code></pre>
12318  *  The tpl tag:
12319 <pre><code>
12320         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12321         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12322         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12323         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12324   
12325 </code></pre>
12326  *      
12327  */
12328 Roo.DomTemplate = function()
12329 {
12330      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12331      if (this.html) {
12332         this.compile();
12333      }
12334 };
12335
12336
12337 Roo.extend(Roo.DomTemplate, Roo.Template, {
12338     /**
12339      * id counter for sub templates.
12340      */
12341     id : 0,
12342     /**
12343      * flag to indicate if dom parser is inside a pre,
12344      * it will strip whitespace if not.
12345      */
12346     inPre : false,
12347     
12348     /**
12349      * The various sub templates
12350      */
12351     tpls : false,
12352     
12353     
12354     
12355     /**
12356      *
12357      * basic tag replacing syntax
12358      * WORD:WORD()
12359      *
12360      * // you can fake an object call by doing this
12361      *  x.t:(test,tesT) 
12362      * 
12363      */
12364     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12365     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12366     
12367     iterChild : function (node, method) {
12368         
12369         var oldPre = this.inPre;
12370         if (node.tagName == 'PRE') {
12371             this.inPre = true;
12372         }
12373         for( var i = 0; i < node.childNodes.length; i++) {
12374             method.call(this, node.childNodes[i]);
12375         }
12376         this.inPre = oldPre;
12377     },
12378     
12379     
12380     
12381     /**
12382      * compile the template
12383      *
12384      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12385      *
12386      */
12387     compile: function()
12388     {
12389         var s = this.html;
12390         
12391         // covert the html into DOM...
12392         var doc = false;
12393         var div =false;
12394         try {
12395             doc = document.implementation.createHTMLDocument("");
12396             doc.documentElement.innerHTML =   this.html  ;
12397             div = doc.documentElement;
12398         } catch (e) {
12399             // old IE... - nasty -- it causes all sorts of issues.. with
12400             // images getting pulled from server..
12401             div = document.createElement('div');
12402             div.innerHTML = this.html;
12403         }
12404         //doc.documentElement.innerHTML = htmlBody
12405          
12406         
12407         
12408         this.tpls = [];
12409         var _t = this;
12410         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12411         
12412         var tpls = this.tpls;
12413         
12414         // create a top level template from the snippet..
12415         
12416         //Roo.log(div.innerHTML);
12417         
12418         var tpl = {
12419             uid : 'master',
12420             id : this.id++,
12421             attr : false,
12422             value : false,
12423             body : div.innerHTML,
12424             
12425             forCall : false,
12426             execCall : false,
12427             dom : div,
12428             isTop : true
12429             
12430         };
12431         tpls.unshift(tpl);
12432         
12433         
12434         // compile them...
12435         this.tpls = [];
12436         Roo.each(tpls, function(tp){
12437             this.compileTpl(tp);
12438             this.tpls[tp.id] = tp;
12439         }, this);
12440         
12441         this.master = tpls[0];
12442         return this;
12443         
12444         
12445     },
12446     
12447     compileNode : function(node, istop) {
12448         // test for
12449         //Roo.log(node);
12450         
12451         
12452         // skip anything not a tag..
12453         if (node.nodeType != 1) {
12454             if (node.nodeType == 3 && !this.inPre) {
12455                 // reduce white space..
12456                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12457                 
12458             }
12459             return;
12460         }
12461         
12462         var tpl = {
12463             uid : false,
12464             id : false,
12465             attr : false,
12466             value : false,
12467             body : '',
12468             
12469             forCall : false,
12470             execCall : false,
12471             dom : false,
12472             isTop : istop
12473             
12474             
12475         };
12476         
12477         
12478         switch(true) {
12479             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12480             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12481             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12482             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12483             // no default..
12484         }
12485         
12486         
12487         if (!tpl.attr) {
12488             // just itterate children..
12489             this.iterChild(node,this.compileNode);
12490             return;
12491         }
12492         tpl.uid = this.id++;
12493         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12494         node.removeAttribute('roo-'+ tpl.attr);
12495         if (tpl.attr != 'name') {
12496             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12497             node.parentNode.replaceChild(placeholder,  node);
12498         } else {
12499             
12500             var placeholder =  document.createElement('span');
12501             placeholder.className = 'roo-tpl-' + tpl.value;
12502             node.parentNode.replaceChild(placeholder,  node);
12503         }
12504         
12505         // parent now sees '{domtplXXXX}
12506         this.iterChild(node,this.compileNode);
12507         
12508         // we should now have node body...
12509         var div = document.createElement('div');
12510         div.appendChild(node);
12511         tpl.dom = node;
12512         // this has the unfortunate side effect of converting tagged attributes
12513         // eg. href="{...}" into %7C...%7D
12514         // this has been fixed by searching for those combo's although it's a bit hacky..
12515         
12516         
12517         tpl.body = div.innerHTML;
12518         
12519         
12520          
12521         tpl.id = tpl.uid;
12522         switch(tpl.attr) {
12523             case 'for' :
12524                 switch (tpl.value) {
12525                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12526                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12527                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12528                 }
12529                 break;
12530             
12531             case 'exec':
12532                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12533                 break;
12534             
12535             case 'if':     
12536                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12537                 break;
12538             
12539             case 'name':
12540                 tpl.id  = tpl.value; // replace non characters???
12541                 break;
12542             
12543         }
12544         
12545         
12546         this.tpls.push(tpl);
12547         
12548         
12549         
12550     },
12551     
12552     
12553     
12554     
12555     /**
12556      * Compile a segment of the template into a 'sub-template'
12557      *
12558      * 
12559      * 
12560      *
12561      */
12562     compileTpl : function(tpl)
12563     {
12564         var fm = Roo.util.Format;
12565         var useF = this.disableFormats !== true;
12566         
12567         var sep = Roo.isGecko ? "+\n" : ",\n";
12568         
12569         var undef = function(str) {
12570             Roo.debug && Roo.log("Property not found :"  + str);
12571             return '';
12572         };
12573           
12574         //Roo.log(tpl.body);
12575         
12576         
12577         
12578         var fn = function(m, lbrace, name, format, args)
12579         {
12580             //Roo.log("ARGS");
12581             //Roo.log(arguments);
12582             args = args ? args.replace(/\\'/g,"'") : args;
12583             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12584             if (typeof(format) == 'undefined') {
12585                 format =  'htmlEncode'; 
12586             }
12587             if (format == 'raw' ) {
12588                 format = false;
12589             }
12590             
12591             if(name.substr(0, 6) == 'domtpl'){
12592                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12593             }
12594             
12595             // build an array of options to determine if value is undefined..
12596             
12597             // basically get 'xxxx.yyyy' then do
12598             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12599             //    (function () { Roo.log("Property not found"); return ''; })() :
12600             //    ......
12601             
12602             var udef_ar = [];
12603             var lookfor = '';
12604             Roo.each(name.split('.'), function(st) {
12605                 lookfor += (lookfor.length ? '.': '') + st;
12606                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12607             });
12608             
12609             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12610             
12611             
12612             if(format && useF){
12613                 
12614                 args = args ? ',' + args : "";
12615                  
12616                 if(format.substr(0, 5) != "this."){
12617                     format = "fm." + format + '(';
12618                 }else{
12619                     format = 'this.call("'+ format.substr(5) + '", ';
12620                     args = ", values";
12621                 }
12622                 
12623                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12624             }
12625              
12626             if (args && args.length) {
12627                 // called with xxyx.yuu:(test,test)
12628                 // change to ()
12629                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12630             }
12631             // raw.. - :raw modifier..
12632             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12633             
12634         };
12635         var body;
12636         // branched to use + in gecko and [].join() in others
12637         if(Roo.isGecko){
12638             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12639                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12640                     "';};};";
12641         }else{
12642             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12643             body.push(tpl.body.replace(/(\r\n|\n)/g,
12644                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12645             body.push("'].join('');};};");
12646             body = body.join('');
12647         }
12648         
12649         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12650        
12651         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12652         eval(body);
12653         
12654         return this;
12655     },
12656      
12657     /**
12658      * same as applyTemplate, except it's done to one of the subTemplates
12659      * when using named templates, you can do:
12660      *
12661      * var str = pl.applySubTemplate('your-name', values);
12662      *
12663      * 
12664      * @param {Number} id of the template
12665      * @param {Object} values to apply to template
12666      * @param {Object} parent (normaly the instance of this object)
12667      */
12668     applySubTemplate : function(id, values, parent)
12669     {
12670         
12671         
12672         var t = this.tpls[id];
12673         
12674         
12675         try { 
12676             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12677                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12678                 return '';
12679             }
12680         } catch(e) {
12681             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12682             Roo.log(values);
12683           
12684             return '';
12685         }
12686         try { 
12687             
12688             if(t.execCall && t.execCall.call(this, values, parent)){
12689                 return '';
12690             }
12691         } catch(e) {
12692             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12693             Roo.log(values);
12694             return '';
12695         }
12696         
12697         try {
12698             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12699             parent = t.target ? values : parent;
12700             if(t.forCall && vs instanceof Array){
12701                 var buf = [];
12702                 for(var i = 0, len = vs.length; i < len; i++){
12703                     try {
12704                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12705                     } catch (e) {
12706                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12707                         Roo.log(e.body);
12708                         //Roo.log(t.compiled);
12709                         Roo.log(vs[i]);
12710                     }   
12711                 }
12712                 return buf.join('');
12713             }
12714         } catch (e) {
12715             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12716             Roo.log(values);
12717             return '';
12718         }
12719         try {
12720             return t.compiled.call(this, vs, parent);
12721         } catch (e) {
12722             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12723             Roo.log(e.body);
12724             //Roo.log(t.compiled);
12725             Roo.log(values);
12726             return '';
12727         }
12728     },
12729
12730    
12731
12732     applyTemplate : function(values){
12733         return this.master.compiled.call(this, values, {});
12734         //var s = this.subs;
12735     },
12736
12737     apply : function(){
12738         return this.applyTemplate.apply(this, arguments);
12739     }
12740
12741  });
12742
12743 Roo.DomTemplate.from = function(el){
12744     el = Roo.getDom(el);
12745     return new Roo.Domtemplate(el.value || el.innerHTML);
12746 };/*
12747  * Based on:
12748  * Ext JS Library 1.1.1
12749  * Copyright(c) 2006-2007, Ext JS, LLC.
12750  *
12751  * Originally Released Under LGPL - original licence link has changed is not relivant.
12752  *
12753  * Fork - LGPL
12754  * <script type="text/javascript">
12755  */
12756
12757 /**
12758  * @class Roo.util.DelayedTask
12759  * Provides a convenient method of performing setTimeout where a new
12760  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12761  * You can use this class to buffer
12762  * the keypress events for a certain number of milliseconds, and perform only if they stop
12763  * for that amount of time.
12764  * @constructor The parameters to this constructor serve as defaults and are not required.
12765  * @param {Function} fn (optional) The default function to timeout
12766  * @param {Object} scope (optional) The default scope of that timeout
12767  * @param {Array} args (optional) The default Array of arguments
12768  */
12769 Roo.util.DelayedTask = function(fn, scope, args){
12770     var id = null, d, t;
12771
12772     var call = function(){
12773         var now = new Date().getTime();
12774         if(now - t >= d){
12775             clearInterval(id);
12776             id = null;
12777             fn.apply(scope, args || []);
12778         }
12779     };
12780     /**
12781      * Cancels any pending timeout and queues a new one
12782      * @param {Number} delay The milliseconds to delay
12783      * @param {Function} newFn (optional) Overrides function passed to constructor
12784      * @param {Object} newScope (optional) Overrides scope passed to constructor
12785      * @param {Array} newArgs (optional) Overrides args passed to constructor
12786      */
12787     this.delay = function(delay, newFn, newScope, newArgs){
12788         if(id && delay != d){
12789             this.cancel();
12790         }
12791         d = delay;
12792         t = new Date().getTime();
12793         fn = newFn || fn;
12794         scope = newScope || scope;
12795         args = newArgs || args;
12796         if(!id){
12797             id = setInterval(call, d);
12798         }
12799     };
12800
12801     /**
12802      * Cancel the last queued timeout
12803      */
12804     this.cancel = function(){
12805         if(id){
12806             clearInterval(id);
12807             id = null;
12808         }
12809     };
12810 };/*
12811  * Based on:
12812  * Ext JS Library 1.1.1
12813  * Copyright(c) 2006-2007, Ext JS, LLC.
12814  *
12815  * Originally Released Under LGPL - original licence link has changed is not relivant.
12816  *
12817  * Fork - LGPL
12818  * <script type="text/javascript">
12819  */
12820  
12821  
12822 Roo.util.TaskRunner = function(interval){
12823     interval = interval || 10;
12824     var tasks = [], removeQueue = [];
12825     var id = 0;
12826     var running = false;
12827
12828     var stopThread = function(){
12829         running = false;
12830         clearInterval(id);
12831         id = 0;
12832     };
12833
12834     var startThread = function(){
12835         if(!running){
12836             running = true;
12837             id = setInterval(runTasks, interval);
12838         }
12839     };
12840
12841     var removeTask = function(task){
12842         removeQueue.push(task);
12843         if(task.onStop){
12844             task.onStop();
12845         }
12846     };
12847
12848     var runTasks = function(){
12849         if(removeQueue.length > 0){
12850             for(var i = 0, len = removeQueue.length; i < len; i++){
12851                 tasks.remove(removeQueue[i]);
12852             }
12853             removeQueue = [];
12854             if(tasks.length < 1){
12855                 stopThread();
12856                 return;
12857             }
12858         }
12859         var now = new Date().getTime();
12860         for(var i = 0, len = tasks.length; i < len; ++i){
12861             var t = tasks[i];
12862             var itime = now - t.taskRunTime;
12863             if(t.interval <= itime){
12864                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12865                 t.taskRunTime = now;
12866                 if(rt === false || t.taskRunCount === t.repeat){
12867                     removeTask(t);
12868                     return;
12869                 }
12870             }
12871             if(t.duration && t.duration <= (now - t.taskStartTime)){
12872                 removeTask(t);
12873             }
12874         }
12875     };
12876
12877     /**
12878      * Queues a new task.
12879      * @param {Object} task
12880      */
12881     this.start = function(task){
12882         tasks.push(task);
12883         task.taskStartTime = new Date().getTime();
12884         task.taskRunTime = 0;
12885         task.taskRunCount = 0;
12886         startThread();
12887         return task;
12888     };
12889
12890     this.stop = function(task){
12891         removeTask(task);
12892         return task;
12893     };
12894
12895     this.stopAll = function(){
12896         stopThread();
12897         for(var i = 0, len = tasks.length; i < len; i++){
12898             if(tasks[i].onStop){
12899                 tasks[i].onStop();
12900             }
12901         }
12902         tasks = [];
12903         removeQueue = [];
12904     };
12905 };
12906
12907 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12908  * Based on:
12909  * Ext JS Library 1.1.1
12910  * Copyright(c) 2006-2007, Ext JS, LLC.
12911  *
12912  * Originally Released Under LGPL - original licence link has changed is not relivant.
12913  *
12914  * Fork - LGPL
12915  * <script type="text/javascript">
12916  */
12917
12918  
12919 /**
12920  * @class Roo.util.MixedCollection
12921  * @extends Roo.util.Observable
12922  * A Collection class that maintains both numeric indexes and keys and exposes events.
12923  * @constructor
12924  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12925  * collection (defaults to false)
12926  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12927  * and return the key value for that item.  This is used when available to look up the key on items that
12928  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12929  * equivalent to providing an implementation for the {@link #getKey} method.
12930  */
12931 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12932     this.items = [];
12933     this.map = {};
12934     this.keys = [];
12935     this.length = 0;
12936     this.addEvents({
12937         /**
12938          * @event clear
12939          * Fires when the collection is cleared.
12940          */
12941         "clear" : true,
12942         /**
12943          * @event add
12944          * Fires when an item is added to the collection.
12945          * @param {Number} index The index at which the item was added.
12946          * @param {Object} o The item added.
12947          * @param {String} key The key associated with the added item.
12948          */
12949         "add" : true,
12950         /**
12951          * @event replace
12952          * Fires when an item is replaced in the collection.
12953          * @param {String} key he key associated with the new added.
12954          * @param {Object} old The item being replaced.
12955          * @param {Object} new The new item.
12956          */
12957         "replace" : true,
12958         /**
12959          * @event remove
12960          * Fires when an item is removed from the collection.
12961          * @param {Object} o The item being removed.
12962          * @param {String} key (optional) The key associated with the removed item.
12963          */
12964         "remove" : true,
12965         "sort" : true
12966     });
12967     this.allowFunctions = allowFunctions === true;
12968     if(keyFn){
12969         this.getKey = keyFn;
12970     }
12971     Roo.util.MixedCollection.superclass.constructor.call(this);
12972 };
12973
12974 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12975     allowFunctions : false,
12976     
12977 /**
12978  * Adds an item to the collection.
12979  * @param {String} key The key to associate with the item
12980  * @param {Object} o The item to add.
12981  * @return {Object} The item added.
12982  */
12983     add : function(key, o){
12984         if(arguments.length == 1){
12985             o = arguments[0];
12986             key = this.getKey(o);
12987         }
12988         if(typeof key == "undefined" || key === null){
12989             this.length++;
12990             this.items.push(o);
12991             this.keys.push(null);
12992         }else{
12993             var old = this.map[key];
12994             if(old){
12995                 return this.replace(key, o);
12996             }
12997             this.length++;
12998             this.items.push(o);
12999             this.map[key] = o;
13000             this.keys.push(key);
13001         }
13002         this.fireEvent("add", this.length-1, o, key);
13003         return o;
13004     },
13005        
13006 /**
13007   * MixedCollection has a generic way to fetch keys if you implement getKey.
13008 <pre><code>
13009 // normal way
13010 var mc = new Roo.util.MixedCollection();
13011 mc.add(someEl.dom.id, someEl);
13012 mc.add(otherEl.dom.id, otherEl);
13013 //and so on
13014
13015 // using getKey
13016 var mc = new Roo.util.MixedCollection();
13017 mc.getKey = function(el){
13018    return el.dom.id;
13019 };
13020 mc.add(someEl);
13021 mc.add(otherEl);
13022
13023 // or via the constructor
13024 var mc = new Roo.util.MixedCollection(false, function(el){
13025    return el.dom.id;
13026 });
13027 mc.add(someEl);
13028 mc.add(otherEl);
13029 </code></pre>
13030  * @param o {Object} The item for which to find the key.
13031  * @return {Object} The key for the passed item.
13032  */
13033     getKey : function(o){
13034          return o.id; 
13035     },
13036    
13037 /**
13038  * Replaces an item in the collection.
13039  * @param {String} key The key associated with the item to replace, or the item to replace.
13040  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13041  * @return {Object}  The new item.
13042  */
13043     replace : function(key, o){
13044         if(arguments.length == 1){
13045             o = arguments[0];
13046             key = this.getKey(o);
13047         }
13048         var old = this.item(key);
13049         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13050              return this.add(key, o);
13051         }
13052         var index = this.indexOfKey(key);
13053         this.items[index] = o;
13054         this.map[key] = o;
13055         this.fireEvent("replace", key, old, o);
13056         return o;
13057     },
13058    
13059 /**
13060  * Adds all elements of an Array or an Object to the collection.
13061  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13062  * an Array of values, each of which are added to the collection.
13063  */
13064     addAll : function(objs){
13065         if(arguments.length > 1 || objs instanceof Array){
13066             var args = arguments.length > 1 ? arguments : objs;
13067             for(var i = 0, len = args.length; i < len; i++){
13068                 this.add(args[i]);
13069             }
13070         }else{
13071             for(var key in objs){
13072                 if(this.allowFunctions || typeof objs[key] != "function"){
13073                     this.add(key, objs[key]);
13074                 }
13075             }
13076         }
13077     },
13078    
13079 /**
13080  * Executes the specified function once for every item in the collection, passing each
13081  * item as the first and only parameter. returning false from the function will stop the iteration.
13082  * @param {Function} fn The function to execute for each item.
13083  * @param {Object} scope (optional) The scope in which to execute the function.
13084  */
13085     each : function(fn, scope){
13086         var items = [].concat(this.items); // each safe for removal
13087         for(var i = 0, len = items.length; i < len; i++){
13088             if(fn.call(scope || items[i], items[i], i, len) === false){
13089                 break;
13090             }
13091         }
13092     },
13093    
13094 /**
13095  * Executes the specified function once for every key in the collection, passing each
13096  * key, and its associated item as the first two parameters.
13097  * @param {Function} fn The function to execute for each item.
13098  * @param {Object} scope (optional) The scope in which to execute the function.
13099  */
13100     eachKey : function(fn, scope){
13101         for(var i = 0, len = this.keys.length; i < len; i++){
13102             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13103         }
13104     },
13105    
13106 /**
13107  * Returns the first item in the collection which elicits a true return value from the
13108  * passed selection function.
13109  * @param {Function} fn The selection function to execute for each item.
13110  * @param {Object} scope (optional) The scope in which to execute the function.
13111  * @return {Object} The first item in the collection which returned true from the selection function.
13112  */
13113     find : function(fn, scope){
13114         for(var i = 0, len = this.items.length; i < len; i++){
13115             if(fn.call(scope || window, this.items[i], this.keys[i])){
13116                 return this.items[i];
13117             }
13118         }
13119         return null;
13120     },
13121    
13122 /**
13123  * Inserts an item at the specified index in the collection.
13124  * @param {Number} index The index to insert the item at.
13125  * @param {String} key The key to associate with the new item, or the item itself.
13126  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13127  * @return {Object} The item inserted.
13128  */
13129     insert : function(index, key, o){
13130         if(arguments.length == 2){
13131             o = arguments[1];
13132             key = this.getKey(o);
13133         }
13134         if(index >= this.length){
13135             return this.add(key, o);
13136         }
13137         this.length++;
13138         this.items.splice(index, 0, o);
13139         if(typeof key != "undefined" && key != null){
13140             this.map[key] = o;
13141         }
13142         this.keys.splice(index, 0, key);
13143         this.fireEvent("add", index, o, key);
13144         return o;
13145     },
13146    
13147 /**
13148  * Removed an item from the collection.
13149  * @param {Object} o The item to remove.
13150  * @return {Object} The item removed.
13151  */
13152     remove : function(o){
13153         return this.removeAt(this.indexOf(o));
13154     },
13155    
13156 /**
13157  * Remove an item from a specified index in the collection.
13158  * @param {Number} index The index within the collection of the item to remove.
13159  */
13160     removeAt : function(index){
13161         if(index < this.length && index >= 0){
13162             this.length--;
13163             var o = this.items[index];
13164             this.items.splice(index, 1);
13165             var key = this.keys[index];
13166             if(typeof key != "undefined"){
13167                 delete this.map[key];
13168             }
13169             this.keys.splice(index, 1);
13170             this.fireEvent("remove", o, key);
13171         }
13172     },
13173    
13174 /**
13175  * Removed an item associated with the passed key fom the collection.
13176  * @param {String} key The key of the item to remove.
13177  */
13178     removeKey : function(key){
13179         return this.removeAt(this.indexOfKey(key));
13180     },
13181    
13182 /**
13183  * Returns the number of items in the collection.
13184  * @return {Number} the number of items in the collection.
13185  */
13186     getCount : function(){
13187         return this.length; 
13188     },
13189    
13190 /**
13191  * Returns index within the collection of the passed Object.
13192  * @param {Object} o The item to find the index of.
13193  * @return {Number} index of the item.
13194  */
13195     indexOf : function(o){
13196         if(!this.items.indexOf){
13197             for(var i = 0, len = this.items.length; i < len; i++){
13198                 if(this.items[i] == o) {
13199                     return i;
13200                 }
13201             }
13202             return -1;
13203         }else{
13204             return this.items.indexOf(o);
13205         }
13206     },
13207    
13208 /**
13209  * Returns index within the collection of the passed key.
13210  * @param {String} key The key to find the index of.
13211  * @return {Number} index of the key.
13212  */
13213     indexOfKey : function(key){
13214         if(!this.keys.indexOf){
13215             for(var i = 0, len = this.keys.length; i < len; i++){
13216                 if(this.keys[i] == key) {
13217                     return i;
13218                 }
13219             }
13220             return -1;
13221         }else{
13222             return this.keys.indexOf(key);
13223         }
13224     },
13225    
13226 /**
13227  * Returns the item associated with the passed key OR index. Key has priority over index.
13228  * @param {String/Number} key The key or index of the item.
13229  * @return {Object} The item associated with the passed key.
13230  */
13231     item : function(key){
13232         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13233         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13234     },
13235     
13236 /**
13237  * Returns the item at the specified index.
13238  * @param {Number} index The index of the item.
13239  * @return {Object}
13240  */
13241     itemAt : function(index){
13242         return this.items[index];
13243     },
13244     
13245 /**
13246  * Returns the item associated with the passed key.
13247  * @param {String/Number} key The key of the item.
13248  * @return {Object} The item associated with the passed key.
13249  */
13250     key : function(key){
13251         return this.map[key];
13252     },
13253    
13254 /**
13255  * Returns true if the collection contains the passed Object as an item.
13256  * @param {Object} o  The Object to look for in the collection.
13257  * @return {Boolean} True if the collection contains the Object as an item.
13258  */
13259     contains : function(o){
13260         return this.indexOf(o) != -1;
13261     },
13262    
13263 /**
13264  * Returns true if the collection contains the passed Object as a key.
13265  * @param {String} key The key to look for in the collection.
13266  * @return {Boolean} True if the collection contains the Object as a key.
13267  */
13268     containsKey : function(key){
13269         return typeof this.map[key] != "undefined";
13270     },
13271    
13272 /**
13273  * Removes all items from the collection.
13274  */
13275     clear : function(){
13276         this.length = 0;
13277         this.items = [];
13278         this.keys = [];
13279         this.map = {};
13280         this.fireEvent("clear");
13281     },
13282    
13283 /**
13284  * Returns the first item in the collection.
13285  * @return {Object} the first item in the collection..
13286  */
13287     first : function(){
13288         return this.items[0]; 
13289     },
13290    
13291 /**
13292  * Returns the last item in the collection.
13293  * @return {Object} the last item in the collection..
13294  */
13295     last : function(){
13296         return this.items[this.length-1];   
13297     },
13298     
13299     _sort : function(property, dir, fn){
13300         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13301         fn = fn || function(a, b){
13302             return a-b;
13303         };
13304         var c = [], k = this.keys, items = this.items;
13305         for(var i = 0, len = items.length; i < len; i++){
13306             c[c.length] = {key: k[i], value: items[i], index: i};
13307         }
13308         c.sort(function(a, b){
13309             var v = fn(a[property], b[property]) * dsc;
13310             if(v == 0){
13311                 v = (a.index < b.index ? -1 : 1);
13312             }
13313             return v;
13314         });
13315         for(var i = 0, len = c.length; i < len; i++){
13316             items[i] = c[i].value;
13317             k[i] = c[i].key;
13318         }
13319         this.fireEvent("sort", this);
13320     },
13321     
13322     /**
13323      * Sorts this collection with the passed comparison function
13324      * @param {String} direction (optional) "ASC" or "DESC"
13325      * @param {Function} fn (optional) comparison function
13326      */
13327     sort : function(dir, fn){
13328         this._sort("value", dir, fn);
13329     },
13330     
13331     /**
13332      * Sorts this collection by keys
13333      * @param {String} direction (optional) "ASC" or "DESC"
13334      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13335      */
13336     keySort : function(dir, fn){
13337         this._sort("key", dir, fn || function(a, b){
13338             return String(a).toUpperCase()-String(b).toUpperCase();
13339         });
13340     },
13341     
13342     /**
13343      * Returns a range of items in this collection
13344      * @param {Number} startIndex (optional) defaults to 0
13345      * @param {Number} endIndex (optional) default to the last item
13346      * @return {Array} An array of items
13347      */
13348     getRange : function(start, end){
13349         var items = this.items;
13350         if(items.length < 1){
13351             return [];
13352         }
13353         start = start || 0;
13354         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13355         var r = [];
13356         if(start <= end){
13357             for(var i = start; i <= end; i++) {
13358                     r[r.length] = items[i];
13359             }
13360         }else{
13361             for(var i = start; i >= end; i--) {
13362                     r[r.length] = items[i];
13363             }
13364         }
13365         return r;
13366     },
13367         
13368     /**
13369      * Filter the <i>objects</i> in this collection by a specific property. 
13370      * Returns a new collection that has been filtered.
13371      * @param {String} property A property on your objects
13372      * @param {String/RegExp} value Either string that the property values 
13373      * should start with or a RegExp to test against the property
13374      * @return {MixedCollection} The new filtered collection
13375      */
13376     filter : function(property, value){
13377         if(!value.exec){ // not a regex
13378             value = String(value);
13379             if(value.length == 0){
13380                 return this.clone();
13381             }
13382             value = new RegExp("^" + Roo.escapeRe(value), "i");
13383         }
13384         return this.filterBy(function(o){
13385             return o && value.test(o[property]);
13386         });
13387         },
13388     
13389     /**
13390      * Filter by a function. * Returns a new collection that has been filtered.
13391      * The passed function will be called with each 
13392      * object in the collection. If the function returns true, the value is included 
13393      * otherwise it is filtered.
13394      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13395      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13396      * @return {MixedCollection} The new filtered collection
13397      */
13398     filterBy : function(fn, scope){
13399         var r = new Roo.util.MixedCollection();
13400         r.getKey = this.getKey;
13401         var k = this.keys, it = this.items;
13402         for(var i = 0, len = it.length; i < len; i++){
13403             if(fn.call(scope||this, it[i], k[i])){
13404                                 r.add(k[i], it[i]);
13405                         }
13406         }
13407         return r;
13408     },
13409     
13410     /**
13411      * Creates a duplicate of this collection
13412      * @return {MixedCollection}
13413      */
13414     clone : function(){
13415         var r = new Roo.util.MixedCollection();
13416         var k = this.keys, it = this.items;
13417         for(var i = 0, len = it.length; i < len; i++){
13418             r.add(k[i], it[i]);
13419         }
13420         r.getKey = this.getKey;
13421         return r;
13422     }
13423 });
13424 /**
13425  * Returns the item associated with the passed key or index.
13426  * @method
13427  * @param {String/Number} key The key or index of the item.
13428  * @return {Object} The item associated with the passed key.
13429  */
13430 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13431  * Based on:
13432  * Ext JS Library 1.1.1
13433  * Copyright(c) 2006-2007, Ext JS, LLC.
13434  *
13435  * Originally Released Under LGPL - original licence link has changed is not relivant.
13436  *
13437  * Fork - LGPL
13438  * <script type="text/javascript">
13439  */
13440 /**
13441  * @class Roo.util.JSON
13442  * Modified version of Douglas Crockford"s json.js that doesn"t
13443  * mess with the Object prototype 
13444  * http://www.json.org/js.html
13445  * @singleton
13446  */
13447 Roo.util.JSON = new (function(){
13448     var useHasOwn = {}.hasOwnProperty ? true : false;
13449     
13450     // crashes Safari in some instances
13451     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13452     
13453     var pad = function(n) {
13454         return n < 10 ? "0" + n : n;
13455     };
13456     
13457     var m = {
13458         "\b": '\\b',
13459         "\t": '\\t',
13460         "\n": '\\n',
13461         "\f": '\\f',
13462         "\r": '\\r',
13463         '"' : '\\"',
13464         "\\": '\\\\'
13465     };
13466
13467     var encodeString = function(s){
13468         if (/["\\\x00-\x1f]/.test(s)) {
13469             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13470                 var c = m[b];
13471                 if(c){
13472                     return c;
13473                 }
13474                 c = b.charCodeAt();
13475                 return "\\u00" +
13476                     Math.floor(c / 16).toString(16) +
13477                     (c % 16).toString(16);
13478             }) + '"';
13479         }
13480         return '"' + s + '"';
13481     };
13482     
13483     var encodeArray = function(o){
13484         var a = ["["], b, i, l = o.length, v;
13485             for (i = 0; i < l; i += 1) {
13486                 v = o[i];
13487                 switch (typeof v) {
13488                     case "undefined":
13489                     case "function":
13490                     case "unknown":
13491                         break;
13492                     default:
13493                         if (b) {
13494                             a.push(',');
13495                         }
13496                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13497                         b = true;
13498                 }
13499             }
13500             a.push("]");
13501             return a.join("");
13502     };
13503     
13504     var encodeDate = function(o){
13505         return '"' + o.getFullYear() + "-" +
13506                 pad(o.getMonth() + 1) + "-" +
13507                 pad(o.getDate()) + "T" +
13508                 pad(o.getHours()) + ":" +
13509                 pad(o.getMinutes()) + ":" +
13510                 pad(o.getSeconds()) + '"';
13511     };
13512     
13513     /**
13514      * Encodes an Object, Array or other value
13515      * @param {Mixed} o The variable to encode
13516      * @return {String} The JSON string
13517      */
13518     this.encode = function(o)
13519     {
13520         // should this be extended to fully wrap stringify..
13521         
13522         if(typeof o == "undefined" || o === null){
13523             return "null";
13524         }else if(o instanceof Array){
13525             return encodeArray(o);
13526         }else if(o instanceof Date){
13527             return encodeDate(o);
13528         }else if(typeof o == "string"){
13529             return encodeString(o);
13530         }else if(typeof o == "number"){
13531             return isFinite(o) ? String(o) : "null";
13532         }else if(typeof o == "boolean"){
13533             return String(o);
13534         }else {
13535             var a = ["{"], b, i, v;
13536             for (i in o) {
13537                 if(!useHasOwn || o.hasOwnProperty(i)) {
13538                     v = o[i];
13539                     switch (typeof v) {
13540                     case "undefined":
13541                     case "function":
13542                     case "unknown":
13543                         break;
13544                     default:
13545                         if(b){
13546                             a.push(',');
13547                         }
13548                         a.push(this.encode(i), ":",
13549                                 v === null ? "null" : this.encode(v));
13550                         b = true;
13551                     }
13552                 }
13553             }
13554             a.push("}");
13555             return a.join("");
13556         }
13557     };
13558     
13559     /**
13560      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13561      * @param {String} json The JSON string
13562      * @return {Object} The resulting object
13563      */
13564     this.decode = function(json){
13565         
13566         return  /** eval:var:json */ eval("(" + json + ')');
13567     };
13568 })();
13569 /** 
13570  * Shorthand for {@link Roo.util.JSON#encode}
13571  * @member Roo encode 
13572  * @method */
13573 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13574 /** 
13575  * Shorthand for {@link Roo.util.JSON#decode}
13576  * @member Roo decode 
13577  * @method */
13578 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13579 /*
13580  * Based on:
13581  * Ext JS Library 1.1.1
13582  * Copyright(c) 2006-2007, Ext JS, LLC.
13583  *
13584  * Originally Released Under LGPL - original licence link has changed is not relivant.
13585  *
13586  * Fork - LGPL
13587  * <script type="text/javascript">
13588  */
13589  
13590 /**
13591  * @class Roo.util.Format
13592  * Reusable data formatting functions
13593  * @singleton
13594  */
13595 Roo.util.Format = function(){
13596     var trimRe = /^\s+|\s+$/g;
13597     return {
13598         /**
13599          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13600          * @param {String} value The string to truncate
13601          * @param {Number} length The maximum length to allow before truncating
13602          * @return {String} The converted text
13603          */
13604         ellipsis : function(value, len){
13605             if(value && value.length > len){
13606                 return value.substr(0, len-3)+"...";
13607             }
13608             return value;
13609         },
13610
13611         /**
13612          * Checks a reference and converts it to empty string if it is undefined
13613          * @param {Mixed} value Reference to check
13614          * @return {Mixed} Empty string if converted, otherwise the original value
13615          */
13616         undef : function(value){
13617             return typeof value != "undefined" ? value : "";
13618         },
13619
13620         /**
13621          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13622          * @param {String} value The string to encode
13623          * @return {String} The encoded text
13624          */
13625         htmlEncode : function(value){
13626             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13627         },
13628
13629         /**
13630          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13631          * @param {String} value The string to decode
13632          * @return {String} The decoded text
13633          */
13634         htmlDecode : function(value){
13635             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13636         },
13637
13638         /**
13639          * Trims any whitespace from either side of a string
13640          * @param {String} value The text to trim
13641          * @return {String} The trimmed text
13642          */
13643         trim : function(value){
13644             return String(value).replace(trimRe, "");
13645         },
13646
13647         /**
13648          * Returns a substring from within an original string
13649          * @param {String} value The original text
13650          * @param {Number} start The start index of the substring
13651          * @param {Number} length The length of the substring
13652          * @return {String} The substring
13653          */
13654         substr : function(value, start, length){
13655             return String(value).substr(start, length);
13656         },
13657
13658         /**
13659          * Converts a string to all lower case letters
13660          * @param {String} value The text to convert
13661          * @return {String} The converted text
13662          */
13663         lowercase : function(value){
13664             return String(value).toLowerCase();
13665         },
13666
13667         /**
13668          * Converts a string to all upper case letters
13669          * @param {String} value The text to convert
13670          * @return {String} The converted text
13671          */
13672         uppercase : function(value){
13673             return String(value).toUpperCase();
13674         },
13675
13676         /**
13677          * Converts the first character only of a string to upper case
13678          * @param {String} value The text to convert
13679          * @return {String} The converted text
13680          */
13681         capitalize : function(value){
13682             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13683         },
13684
13685         // private
13686         call : function(value, fn){
13687             if(arguments.length > 2){
13688                 var args = Array.prototype.slice.call(arguments, 2);
13689                 args.unshift(value);
13690                  
13691                 return /** eval:var:value */  eval(fn).apply(window, args);
13692             }else{
13693                 /** eval:var:value */
13694                 return /** eval:var:value */ eval(fn).call(window, value);
13695             }
13696         },
13697
13698        
13699         /**
13700          * safer version of Math.toFixed..??/
13701          * @param {Number/String} value The numeric value to format
13702          * @param {Number/String} value Decimal places 
13703          * @return {String} The formatted currency string
13704          */
13705         toFixed : function(v, n)
13706         {
13707             // why not use to fixed - precision is buggered???
13708             if (!n) {
13709                 return Math.round(v-0);
13710             }
13711             var fact = Math.pow(10,n+1);
13712             v = (Math.round((v-0)*fact))/fact;
13713             var z = (''+fact).substring(2);
13714             if (v == Math.floor(v)) {
13715                 return Math.floor(v) + '.' + z;
13716             }
13717             
13718             // now just padd decimals..
13719             var ps = String(v).split('.');
13720             var fd = (ps[1] + z);
13721             var r = fd.substring(0,n); 
13722             var rm = fd.substring(n); 
13723             if (rm < 5) {
13724                 return ps[0] + '.' + r;
13725             }
13726             r*=1; // turn it into a number;
13727             r++;
13728             if (String(r).length != n) {
13729                 ps[0]*=1;
13730                 ps[0]++;
13731                 r = String(r).substring(1); // chop the end off.
13732             }
13733             
13734             return ps[0] + '.' + r;
13735              
13736         },
13737         
13738         /**
13739          * Format a number as US currency
13740          * @param {Number/String} value The numeric value to format
13741          * @return {String} The formatted currency string
13742          */
13743         usMoney : function(v){
13744             return '$' + Roo.util.Format.number(v);
13745         },
13746         
13747         /**
13748          * Format a number
13749          * eventually this should probably emulate php's number_format
13750          * @param {Number/String} value The numeric value to format
13751          * @param {Number} decimals number of decimal places
13752          * @return {String} The formatted currency string
13753          */
13754         number : function(v,decimals)
13755         {
13756             // multiply and round.
13757             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13758             var mul = Math.pow(10, decimals);
13759             var zero = String(mul).substring(1);
13760             v = (Math.round((v-0)*mul))/mul;
13761             
13762             // if it's '0' number.. then
13763             
13764             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13765             v = String(v);
13766             var ps = v.split('.');
13767             var whole = ps[0];
13768             
13769             
13770             var r = /(\d+)(\d{3})/;
13771             // add comma's
13772             while (r.test(whole)) {
13773                 whole = whole.replace(r, '$1' + ',' + '$2');
13774             }
13775             
13776             
13777             var sub = ps[1] ?
13778                     // has decimals..
13779                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13780                     // does not have decimals
13781                     (decimals ? ('.' + zero) : '');
13782             
13783             
13784             return whole + sub ;
13785         },
13786         
13787         /**
13788          * Parse a value into a formatted date using the specified format pattern.
13789          * @param {Mixed} value The value to format
13790          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13791          * @return {String} The formatted date string
13792          */
13793         date : function(v, format){
13794             if(!v){
13795                 return "";
13796             }
13797             if(!(v instanceof Date)){
13798                 v = new Date(Date.parse(v));
13799             }
13800             return v.dateFormat(format || Roo.util.Format.defaults.date);
13801         },
13802
13803         /**
13804          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13805          * @param {String} format Any valid date format string
13806          * @return {Function} The date formatting function
13807          */
13808         dateRenderer : function(format){
13809             return function(v){
13810                 return Roo.util.Format.date(v, format);  
13811             };
13812         },
13813
13814         // private
13815         stripTagsRE : /<\/?[^>]+>/gi,
13816         
13817         /**
13818          * Strips all HTML tags
13819          * @param {Mixed} value The text from which to strip tags
13820          * @return {String} The stripped text
13821          */
13822         stripTags : function(v){
13823             return !v ? v : String(v).replace(this.stripTagsRE, "");
13824         }
13825     };
13826 }();
13827 Roo.util.Format.defaults = {
13828     date : 'd/M/Y'
13829 };/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840
13841  
13842
13843 /**
13844  * @class Roo.MasterTemplate
13845  * @extends Roo.Template
13846  * Provides a template that can have child templates. The syntax is:
13847 <pre><code>
13848 var t = new Roo.MasterTemplate(
13849         '&lt;select name="{name}"&gt;',
13850                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13851         '&lt;/select&gt;'
13852 );
13853 t.add('options', {value: 'foo', text: 'bar'});
13854 // or you can add multiple child elements in one shot
13855 t.addAll('options', [
13856     {value: 'foo', text: 'bar'},
13857     {value: 'foo2', text: 'bar2'},
13858     {value: 'foo3', text: 'bar3'}
13859 ]);
13860 // then append, applying the master template values
13861 t.append('my-form', {name: 'my-select'});
13862 </code></pre>
13863 * A name attribute for the child template is not required if you have only one child
13864 * template or you want to refer to them by index.
13865  */
13866 Roo.MasterTemplate = function(){
13867     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13868     this.originalHtml = this.html;
13869     var st = {};
13870     var m, re = this.subTemplateRe;
13871     re.lastIndex = 0;
13872     var subIndex = 0;
13873     while(m = re.exec(this.html)){
13874         var name = m[1], content = m[2];
13875         st[subIndex] = {
13876             name: name,
13877             index: subIndex,
13878             buffer: [],
13879             tpl : new Roo.Template(content)
13880         };
13881         if(name){
13882             st[name] = st[subIndex];
13883         }
13884         st[subIndex].tpl.compile();
13885         st[subIndex].tpl.call = this.call.createDelegate(this);
13886         subIndex++;
13887     }
13888     this.subCount = subIndex;
13889     this.subs = st;
13890 };
13891 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13892     /**
13893     * The regular expression used to match sub templates
13894     * @type RegExp
13895     * @property
13896     */
13897     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13898
13899     /**
13900      * Applies the passed values to a child template.
13901      * @param {String/Number} name (optional) The name or index of the child template
13902      * @param {Array/Object} values The values to be applied to the template
13903      * @return {MasterTemplate} this
13904      */
13905      add : function(name, values){
13906         if(arguments.length == 1){
13907             values = arguments[0];
13908             name = 0;
13909         }
13910         var s = this.subs[name];
13911         s.buffer[s.buffer.length] = s.tpl.apply(values);
13912         return this;
13913     },
13914
13915     /**
13916      * Applies all the passed values to a child template.
13917      * @param {String/Number} name (optional) The name or index of the child template
13918      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13919      * @param {Boolean} reset (optional) True to reset the template first
13920      * @return {MasterTemplate} this
13921      */
13922     fill : function(name, values, reset){
13923         var a = arguments;
13924         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13925             values = a[0];
13926             name = 0;
13927             reset = a[1];
13928         }
13929         if(reset){
13930             this.reset();
13931         }
13932         for(var i = 0, len = values.length; i < len; i++){
13933             this.add(name, values[i]);
13934         }
13935         return this;
13936     },
13937
13938     /**
13939      * Resets the template for reuse
13940      * @return {MasterTemplate} this
13941      */
13942      reset : function(){
13943         var s = this.subs;
13944         for(var i = 0; i < this.subCount; i++){
13945             s[i].buffer = [];
13946         }
13947         return this;
13948     },
13949
13950     applyTemplate : function(values){
13951         var s = this.subs;
13952         var replaceIndex = -1;
13953         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13954             return s[++replaceIndex].buffer.join("");
13955         });
13956         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13957     },
13958
13959     apply : function(){
13960         return this.applyTemplate.apply(this, arguments);
13961     },
13962
13963     compile : function(){return this;}
13964 });
13965
13966 /**
13967  * Alias for fill().
13968  * @method
13969  */
13970 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13971  /**
13972  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13973  * var tpl = Roo.MasterTemplate.from('element-id');
13974  * @param {String/HTMLElement} el
13975  * @param {Object} config
13976  * @static
13977  */
13978 Roo.MasterTemplate.from = function(el, config){
13979     el = Roo.getDom(el);
13980     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13981 };/*
13982  * Based on:
13983  * Ext JS Library 1.1.1
13984  * Copyright(c) 2006-2007, Ext JS, LLC.
13985  *
13986  * Originally Released Under LGPL - original licence link has changed is not relivant.
13987  *
13988  * Fork - LGPL
13989  * <script type="text/javascript">
13990  */
13991
13992  
13993 /**
13994  * @class Roo.util.CSS
13995  * Utility class for manipulating CSS rules
13996  * @singleton
13997  */
13998 Roo.util.CSS = function(){
13999         var rules = null;
14000         var doc = document;
14001
14002     var camelRe = /(-[a-z])/gi;
14003     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14004
14005    return {
14006    /**
14007     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14008     * tag and appended to the HEAD of the document.
14009     * @param {String|Object} cssText The text containing the css rules
14010     * @param {String} id An id to add to the stylesheet for later removal
14011     * @return {StyleSheet}
14012     */
14013     createStyleSheet : function(cssText, id){
14014         var ss;
14015         var head = doc.getElementsByTagName("head")[0];
14016         var nrules = doc.createElement("style");
14017         nrules.setAttribute("type", "text/css");
14018         if(id){
14019             nrules.setAttribute("id", id);
14020         }
14021         if (typeof(cssText) != 'string') {
14022             // support object maps..
14023             // not sure if this a good idea.. 
14024             // perhaps it should be merged with the general css handling
14025             // and handle js style props.
14026             var cssTextNew = [];
14027             for(var n in cssText) {
14028                 var citems = [];
14029                 for(var k in cssText[n]) {
14030                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14031                 }
14032                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14033                 
14034             }
14035             cssText = cssTextNew.join("\n");
14036             
14037         }
14038        
14039        
14040        if(Roo.isIE){
14041            head.appendChild(nrules);
14042            ss = nrules.styleSheet;
14043            ss.cssText = cssText;
14044        }else{
14045            try{
14046                 nrules.appendChild(doc.createTextNode(cssText));
14047            }catch(e){
14048                nrules.cssText = cssText; 
14049            }
14050            head.appendChild(nrules);
14051            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14052        }
14053        this.cacheStyleSheet(ss);
14054        return ss;
14055    },
14056
14057    /**
14058     * Removes a style or link tag by id
14059     * @param {String} id The id of the tag
14060     */
14061    removeStyleSheet : function(id){
14062        var existing = doc.getElementById(id);
14063        if(existing){
14064            existing.parentNode.removeChild(existing);
14065        }
14066    },
14067
14068    /**
14069     * Dynamically swaps an existing stylesheet reference for a new one
14070     * @param {String} id The id of an existing link tag to remove
14071     * @param {String} url The href of the new stylesheet to include
14072     */
14073    swapStyleSheet : function(id, url){
14074        this.removeStyleSheet(id);
14075        var ss = doc.createElement("link");
14076        ss.setAttribute("rel", "stylesheet");
14077        ss.setAttribute("type", "text/css");
14078        ss.setAttribute("id", id);
14079        ss.setAttribute("href", url);
14080        doc.getElementsByTagName("head")[0].appendChild(ss);
14081    },
14082    
14083    /**
14084     * Refresh the rule cache if you have dynamically added stylesheets
14085     * @return {Object} An object (hash) of rules indexed by selector
14086     */
14087    refreshCache : function(){
14088        return this.getRules(true);
14089    },
14090
14091    // private
14092    cacheStyleSheet : function(stylesheet){
14093        if(!rules){
14094            rules = {};
14095        }
14096        try{// try catch for cross domain access issue
14097            var ssRules = stylesheet.cssRules || stylesheet.rules;
14098            for(var j = ssRules.length-1; j >= 0; --j){
14099                rules[ssRules[j].selectorText] = ssRules[j];
14100            }
14101        }catch(e){}
14102    },
14103    
14104    /**
14105     * Gets all css rules for the document
14106     * @param {Boolean} refreshCache true to refresh the internal cache
14107     * @return {Object} An object (hash) of rules indexed by selector
14108     */
14109    getRules : function(refreshCache){
14110                 if(rules == null || refreshCache){
14111                         rules = {};
14112                         var ds = doc.styleSheets;
14113                         for(var i =0, len = ds.length; i < len; i++){
14114                             try{
14115                         this.cacheStyleSheet(ds[i]);
14116                     }catch(e){} 
14117                 }
14118                 }
14119                 return rules;
14120         },
14121         
14122         /**
14123     * Gets an an individual CSS rule by selector(s)
14124     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14125     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14126     * @return {CSSRule} The CSS rule or null if one is not found
14127     */
14128    getRule : function(selector, refreshCache){
14129                 var rs = this.getRules(refreshCache);
14130                 if(!(selector instanceof Array)){
14131                     return rs[selector];
14132                 }
14133                 for(var i = 0; i < selector.length; i++){
14134                         if(rs[selector[i]]){
14135                                 return rs[selector[i]];
14136                         }
14137                 }
14138                 return null;
14139         },
14140         
14141         
14142         /**
14143     * Updates a rule property
14144     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14145     * @param {String} property The css property
14146     * @param {String} value The new value for the property
14147     * @return {Boolean} true If a rule was found and updated
14148     */
14149    updateRule : function(selector, property, value){
14150                 if(!(selector instanceof Array)){
14151                         var rule = this.getRule(selector);
14152                         if(rule){
14153                                 rule.style[property.replace(camelRe, camelFn)] = value;
14154                                 return true;
14155                         }
14156                 }else{
14157                         for(var i = 0; i < selector.length; i++){
14158                                 if(this.updateRule(selector[i], property, value)){
14159                                         return true;
14160                                 }
14161                         }
14162                 }
14163                 return false;
14164         }
14165    };   
14166 }();/*
14167  * Based on:
14168  * Ext JS Library 1.1.1
14169  * Copyright(c) 2006-2007, Ext JS, LLC.
14170  *
14171  * Originally Released Under LGPL - original licence link has changed is not relivant.
14172  *
14173  * Fork - LGPL
14174  * <script type="text/javascript">
14175  */
14176
14177  
14178
14179 /**
14180  * @class Roo.util.ClickRepeater
14181  * @extends Roo.util.Observable
14182  * 
14183  * A wrapper class which can be applied to any element. Fires a "click" event while the
14184  * mouse is pressed. The interval between firings may be specified in the config but
14185  * defaults to 10 milliseconds.
14186  * 
14187  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14188  * 
14189  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14190  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14191  * Similar to an autorepeat key delay.
14192  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14193  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14194  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14195  *           "interval" and "delay" are ignored. "immediate" is honored.
14196  * @cfg {Boolean} preventDefault True to prevent the default click event
14197  * @cfg {Boolean} stopDefault True to stop the default click event
14198  * 
14199  * @history
14200  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14201  *     2007-02-02 jvs Renamed to ClickRepeater
14202  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14203  *
14204  *  @constructor
14205  * @param {String/HTMLElement/Element} el The element to listen on
14206  * @param {Object} config
14207  **/
14208 Roo.util.ClickRepeater = function(el, config)
14209 {
14210     this.el = Roo.get(el);
14211     this.el.unselectable();
14212
14213     Roo.apply(this, config);
14214
14215     this.addEvents({
14216     /**
14217      * @event mousedown
14218      * Fires when the mouse button is depressed.
14219      * @param {Roo.util.ClickRepeater} this
14220      */
14221         "mousedown" : true,
14222     /**
14223      * @event click
14224      * Fires on a specified interval during the time the element is pressed.
14225      * @param {Roo.util.ClickRepeater} this
14226      */
14227         "click" : true,
14228     /**
14229      * @event mouseup
14230      * Fires when the mouse key is released.
14231      * @param {Roo.util.ClickRepeater} this
14232      */
14233         "mouseup" : true
14234     });
14235
14236     this.el.on("mousedown", this.handleMouseDown, this);
14237     if(this.preventDefault || this.stopDefault){
14238         this.el.on("click", function(e){
14239             if(this.preventDefault){
14240                 e.preventDefault();
14241             }
14242             if(this.stopDefault){
14243                 e.stopEvent();
14244             }
14245         }, this);
14246     }
14247
14248     // allow inline handler
14249     if(this.handler){
14250         this.on("click", this.handler,  this.scope || this);
14251     }
14252
14253     Roo.util.ClickRepeater.superclass.constructor.call(this);
14254 };
14255
14256 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14257     interval : 20,
14258     delay: 250,
14259     preventDefault : true,
14260     stopDefault : false,
14261     timer : 0,
14262
14263     // private
14264     handleMouseDown : function(){
14265         clearTimeout(this.timer);
14266         this.el.blur();
14267         if(this.pressClass){
14268             this.el.addClass(this.pressClass);
14269         }
14270         this.mousedownTime = new Date();
14271
14272         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14273         this.el.on("mouseout", this.handleMouseOut, this);
14274
14275         this.fireEvent("mousedown", this);
14276         this.fireEvent("click", this);
14277         
14278         this.timer = this.click.defer(this.delay || this.interval, this);
14279     },
14280
14281     // private
14282     click : function(){
14283         this.fireEvent("click", this);
14284         this.timer = this.click.defer(this.getInterval(), this);
14285     },
14286
14287     // private
14288     getInterval: function(){
14289         if(!this.accelerate){
14290             return this.interval;
14291         }
14292         var pressTime = this.mousedownTime.getElapsed();
14293         if(pressTime < 500){
14294             return 400;
14295         }else if(pressTime < 1700){
14296             return 320;
14297         }else if(pressTime < 2600){
14298             return 250;
14299         }else if(pressTime < 3500){
14300             return 180;
14301         }else if(pressTime < 4400){
14302             return 140;
14303         }else if(pressTime < 5300){
14304             return 80;
14305         }else if(pressTime < 6200){
14306             return 50;
14307         }else{
14308             return 10;
14309         }
14310     },
14311
14312     // private
14313     handleMouseOut : function(){
14314         clearTimeout(this.timer);
14315         if(this.pressClass){
14316             this.el.removeClass(this.pressClass);
14317         }
14318         this.el.on("mouseover", this.handleMouseReturn, this);
14319     },
14320
14321     // private
14322     handleMouseReturn : function(){
14323         this.el.un("mouseover", this.handleMouseReturn);
14324         if(this.pressClass){
14325             this.el.addClass(this.pressClass);
14326         }
14327         this.click();
14328     },
14329
14330     // private
14331     handleMouseUp : function(){
14332         clearTimeout(this.timer);
14333         this.el.un("mouseover", this.handleMouseReturn);
14334         this.el.un("mouseout", this.handleMouseOut);
14335         Roo.get(document).un("mouseup", this.handleMouseUp);
14336         this.el.removeClass(this.pressClass);
14337         this.fireEvent("mouseup", this);
14338     }
14339 });/*
14340  * Based on:
14341  * Ext JS Library 1.1.1
14342  * Copyright(c) 2006-2007, Ext JS, LLC.
14343  *
14344  * Originally Released Under LGPL - original licence link has changed is not relivant.
14345  *
14346  * Fork - LGPL
14347  * <script type="text/javascript">
14348  */
14349
14350  
14351 /**
14352  * @class Roo.KeyNav
14353  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14354  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14355  * way to implement custom navigation schemes for any UI component.</p>
14356  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14357  * pageUp, pageDown, del, home, end.  Usage:</p>
14358  <pre><code>
14359 var nav = new Roo.KeyNav("my-element", {
14360     "left" : function(e){
14361         this.moveLeft(e.ctrlKey);
14362     },
14363     "right" : function(e){
14364         this.moveRight(e.ctrlKey);
14365     },
14366     "enter" : function(e){
14367         this.save();
14368     },
14369     scope : this
14370 });
14371 </code></pre>
14372  * @constructor
14373  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14374  * @param {Object} config The config
14375  */
14376 Roo.KeyNav = function(el, config){
14377     this.el = Roo.get(el);
14378     Roo.apply(this, config);
14379     if(!this.disabled){
14380         this.disabled = true;
14381         this.enable();
14382     }
14383 };
14384
14385 Roo.KeyNav.prototype = {
14386     /**
14387      * @cfg {Boolean} disabled
14388      * True to disable this KeyNav instance (defaults to false)
14389      */
14390     disabled : false,
14391     /**
14392      * @cfg {String} defaultEventAction
14393      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14394      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14395      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14396      */
14397     defaultEventAction: "stopEvent",
14398     /**
14399      * @cfg {Boolean} forceKeyDown
14400      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14401      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14402      * handle keydown instead of keypress.
14403      */
14404     forceKeyDown : false,
14405
14406     // private
14407     prepareEvent : function(e){
14408         var k = e.getKey();
14409         var h = this.keyToHandler[k];
14410         //if(h && this[h]){
14411         //    e.stopPropagation();
14412         //}
14413         if(Roo.isSafari && h && k >= 37 && k <= 40){
14414             e.stopEvent();
14415         }
14416     },
14417
14418     // private
14419     relay : function(e){
14420         var k = e.getKey();
14421         var h = this.keyToHandler[k];
14422         if(h && this[h]){
14423             if(this.doRelay(e, this[h], h) !== true){
14424                 e[this.defaultEventAction]();
14425             }
14426         }
14427     },
14428
14429     // private
14430     doRelay : function(e, h, hname){
14431         return h.call(this.scope || this, e);
14432     },
14433
14434     // possible handlers
14435     enter : false,
14436     left : false,
14437     right : false,
14438     up : false,
14439     down : false,
14440     tab : false,
14441     esc : false,
14442     pageUp : false,
14443     pageDown : false,
14444     del : false,
14445     home : false,
14446     end : false,
14447
14448     // quick lookup hash
14449     keyToHandler : {
14450         37 : "left",
14451         39 : "right",
14452         38 : "up",
14453         40 : "down",
14454         33 : "pageUp",
14455         34 : "pageDown",
14456         46 : "del",
14457         36 : "home",
14458         35 : "end",
14459         13 : "enter",
14460         27 : "esc",
14461         9  : "tab"
14462     },
14463
14464         /**
14465          * Enable this KeyNav
14466          */
14467         enable: function(){
14468                 if(this.disabled){
14469             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14470             // the EventObject will normalize Safari automatically
14471             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14472                 this.el.on("keydown", this.relay,  this);
14473             }else{
14474                 this.el.on("keydown", this.prepareEvent,  this);
14475                 this.el.on("keypress", this.relay,  this);
14476             }
14477                     this.disabled = false;
14478                 }
14479         },
14480
14481         /**
14482          * Disable this KeyNav
14483          */
14484         disable: function(){
14485                 if(!this.disabled){
14486                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14487                 this.el.un("keydown", this.relay);
14488             }else{
14489                 this.el.un("keydown", this.prepareEvent);
14490                 this.el.un("keypress", this.relay);
14491             }
14492                     this.disabled = true;
14493                 }
14494         }
14495 };/*
14496  * Based on:
14497  * Ext JS Library 1.1.1
14498  * Copyright(c) 2006-2007, Ext JS, LLC.
14499  *
14500  * Originally Released Under LGPL - original licence link has changed is not relivant.
14501  *
14502  * Fork - LGPL
14503  * <script type="text/javascript">
14504  */
14505
14506  
14507 /**
14508  * @class Roo.KeyMap
14509  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14510  * The constructor accepts the same config object as defined by {@link #addBinding}.
14511  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14512  * combination it will call the function with this signature (if the match is a multi-key
14513  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14514  * A KeyMap can also handle a string representation of keys.<br />
14515  * Usage:
14516  <pre><code>
14517 // map one key by key code
14518 var map = new Roo.KeyMap("my-element", {
14519     key: 13, // or Roo.EventObject.ENTER
14520     fn: myHandler,
14521     scope: myObject
14522 });
14523
14524 // map multiple keys to one action by string
14525 var map = new Roo.KeyMap("my-element", {
14526     key: "a\r\n\t",
14527     fn: myHandler,
14528     scope: myObject
14529 });
14530
14531 // map multiple keys to multiple actions by strings and array of codes
14532 var map = new Roo.KeyMap("my-element", [
14533     {
14534         key: [10,13],
14535         fn: function(){ alert("Return was pressed"); }
14536     }, {
14537         key: "abc",
14538         fn: function(){ alert('a, b or c was pressed'); }
14539     }, {
14540         key: "\t",
14541         ctrl:true,
14542         shift:true,
14543         fn: function(){ alert('Control + shift + tab was pressed.'); }
14544     }
14545 ]);
14546 </code></pre>
14547  * <b>Note: A KeyMap starts enabled</b>
14548  * @constructor
14549  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14550  * @param {Object} config The config (see {@link #addBinding})
14551  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14552  */
14553 Roo.KeyMap = function(el, config, eventName){
14554     this.el  = Roo.get(el);
14555     this.eventName = eventName || "keydown";
14556     this.bindings = [];
14557     if(config){
14558         this.addBinding(config);
14559     }
14560     this.enable();
14561 };
14562
14563 Roo.KeyMap.prototype = {
14564     /**
14565      * True to stop the event from bubbling and prevent the default browser action if the
14566      * key was handled by the KeyMap (defaults to false)
14567      * @type Boolean
14568      */
14569     stopEvent : false,
14570
14571     /**
14572      * Add a new binding to this KeyMap. The following config object properties are supported:
14573      * <pre>
14574 Property    Type             Description
14575 ----------  ---------------  ----------------------------------------------------------------------
14576 key         String/Array     A single keycode or an array of keycodes to handle
14577 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14578 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14579 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14580 fn          Function         The function to call when KeyMap finds the expected key combination
14581 scope       Object           The scope of the callback function
14582 </pre>
14583      *
14584      * Usage:
14585      * <pre><code>
14586 // Create a KeyMap
14587 var map = new Roo.KeyMap(document, {
14588     key: Roo.EventObject.ENTER,
14589     fn: handleKey,
14590     scope: this
14591 });
14592
14593 //Add a new binding to the existing KeyMap later
14594 map.addBinding({
14595     key: 'abc',
14596     shift: true,
14597     fn: handleKey,
14598     scope: this
14599 });
14600 </code></pre>
14601      * @param {Object/Array} config A single KeyMap config or an array of configs
14602      */
14603         addBinding : function(config){
14604         if(config instanceof Array){
14605             for(var i = 0, len = config.length; i < len; i++){
14606                 this.addBinding(config[i]);
14607             }
14608             return;
14609         }
14610         var keyCode = config.key,
14611             shift = config.shift, 
14612             ctrl = config.ctrl, 
14613             alt = config.alt,
14614             fn = config.fn,
14615             scope = config.scope;
14616         if(typeof keyCode == "string"){
14617             var ks = [];
14618             var keyString = keyCode.toUpperCase();
14619             for(var j = 0, len = keyString.length; j < len; j++){
14620                 ks.push(keyString.charCodeAt(j));
14621             }
14622             keyCode = ks;
14623         }
14624         var keyArray = keyCode instanceof Array;
14625         var handler = function(e){
14626             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14627                 var k = e.getKey();
14628                 if(keyArray){
14629                     for(var i = 0, len = keyCode.length; i < len; i++){
14630                         if(keyCode[i] == k){
14631                           if(this.stopEvent){
14632                               e.stopEvent();
14633                           }
14634                           fn.call(scope || window, k, e);
14635                           return;
14636                         }
14637                     }
14638                 }else{
14639                     if(k == keyCode){
14640                         if(this.stopEvent){
14641                            e.stopEvent();
14642                         }
14643                         fn.call(scope || window, k, e);
14644                     }
14645                 }
14646             }
14647         };
14648         this.bindings.push(handler);  
14649         },
14650
14651     /**
14652      * Shorthand for adding a single key listener
14653      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14654      * following options:
14655      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14656      * @param {Function} fn The function to call
14657      * @param {Object} scope (optional) The scope of the function
14658      */
14659     on : function(key, fn, scope){
14660         var keyCode, shift, ctrl, alt;
14661         if(typeof key == "object" && !(key instanceof Array)){
14662             keyCode = key.key;
14663             shift = key.shift;
14664             ctrl = key.ctrl;
14665             alt = key.alt;
14666         }else{
14667             keyCode = key;
14668         }
14669         this.addBinding({
14670             key: keyCode,
14671             shift: shift,
14672             ctrl: ctrl,
14673             alt: alt,
14674             fn: fn,
14675             scope: scope
14676         })
14677     },
14678
14679     // private
14680     handleKeyDown : function(e){
14681             if(this.enabled){ //just in case
14682             var b = this.bindings;
14683             for(var i = 0, len = b.length; i < len; i++){
14684                 b[i].call(this, e);
14685             }
14686             }
14687         },
14688         
14689         /**
14690          * Returns true if this KeyMap is enabled
14691          * @return {Boolean} 
14692          */
14693         isEnabled : function(){
14694             return this.enabled;  
14695         },
14696         
14697         /**
14698          * Enables this KeyMap
14699          */
14700         enable: function(){
14701                 if(!this.enabled){
14702                     this.el.on(this.eventName, this.handleKeyDown, this);
14703                     this.enabled = true;
14704                 }
14705         },
14706
14707         /**
14708          * Disable this KeyMap
14709          */
14710         disable: function(){
14711                 if(this.enabled){
14712                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14713                     this.enabled = false;
14714                 }
14715         }
14716 };/*
14717  * Based on:
14718  * Ext JS Library 1.1.1
14719  * Copyright(c) 2006-2007, Ext JS, LLC.
14720  *
14721  * Originally Released Under LGPL - original licence link has changed is not relivant.
14722  *
14723  * Fork - LGPL
14724  * <script type="text/javascript">
14725  */
14726
14727  
14728 /**
14729  * @class Roo.util.TextMetrics
14730  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14731  * wide, in pixels, a given block of text will be.
14732  * @singleton
14733  */
14734 Roo.util.TextMetrics = function(){
14735     var shared;
14736     return {
14737         /**
14738          * Measures the size of the specified text
14739          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14740          * that can affect the size of the rendered text
14741          * @param {String} text The text to measure
14742          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14743          * in order to accurately measure the text height
14744          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14745          */
14746         measure : function(el, text, fixedWidth){
14747             if(!shared){
14748                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14749             }
14750             shared.bind(el);
14751             shared.setFixedWidth(fixedWidth || 'auto');
14752             return shared.getSize(text);
14753         },
14754
14755         /**
14756          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14757          * the overhead of multiple calls to initialize the style properties on each measurement.
14758          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14759          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14760          * in order to accurately measure the text height
14761          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14762          */
14763         createInstance : function(el, fixedWidth){
14764             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14765         }
14766     };
14767 }();
14768
14769  
14770
14771 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14772     var ml = new Roo.Element(document.createElement('div'));
14773     document.body.appendChild(ml.dom);
14774     ml.position('absolute');
14775     ml.setLeftTop(-1000, -1000);
14776     ml.hide();
14777
14778     if(fixedWidth){
14779         ml.setWidth(fixedWidth);
14780     }
14781      
14782     var instance = {
14783         /**
14784          * Returns the size of the specified text based on the internal element's style and width properties
14785          * @memberOf Roo.util.TextMetrics.Instance#
14786          * @param {String} text The text to measure
14787          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14788          */
14789         getSize : function(text){
14790             ml.update(text);
14791             var s = ml.getSize();
14792             ml.update('');
14793             return s;
14794         },
14795
14796         /**
14797          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14798          * that can affect the size of the rendered text
14799          * @memberOf Roo.util.TextMetrics.Instance#
14800          * @param {String/HTMLElement} el The element, dom node or id
14801          */
14802         bind : function(el){
14803             ml.setStyle(
14804                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14805             );
14806         },
14807
14808         /**
14809          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14810          * to set a fixed width in order to accurately measure the text height.
14811          * @memberOf Roo.util.TextMetrics.Instance#
14812          * @param {Number} width The width to set on the element
14813          */
14814         setFixedWidth : function(width){
14815             ml.setWidth(width);
14816         },
14817
14818         /**
14819          * Returns the measured width of the specified text
14820          * @memberOf Roo.util.TextMetrics.Instance#
14821          * @param {String} text The text to measure
14822          * @return {Number} width The width in pixels
14823          */
14824         getWidth : function(text){
14825             ml.dom.style.width = 'auto';
14826             return this.getSize(text).width;
14827         },
14828
14829         /**
14830          * Returns the measured height of the specified text.  For multiline text, be sure to call
14831          * {@link #setFixedWidth} if necessary.
14832          * @memberOf Roo.util.TextMetrics.Instance#
14833          * @param {String} text The text to measure
14834          * @return {Number} height The height in pixels
14835          */
14836         getHeight : function(text){
14837             return this.getSize(text).height;
14838         }
14839     };
14840
14841     instance.bind(bindTo);
14842
14843     return instance;
14844 };
14845
14846 // backwards compat
14847 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14848  * Based on:
14849  * Ext JS Library 1.1.1
14850  * Copyright(c) 2006-2007, Ext JS, LLC.
14851  *
14852  * Originally Released Under LGPL - original licence link has changed is not relivant.
14853  *
14854  * Fork - LGPL
14855  * <script type="text/javascript">
14856  */
14857
14858 /**
14859  * @class Roo.state.Provider
14860  * Abstract base class for state provider implementations. This class provides methods
14861  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14862  * Provider interface.
14863  */
14864 Roo.state.Provider = function(){
14865     /**
14866      * @event statechange
14867      * Fires when a state change occurs.
14868      * @param {Provider} this This state provider
14869      * @param {String} key The state key which was changed
14870      * @param {String} value The encoded value for the state
14871      */
14872     this.addEvents({
14873         "statechange": true
14874     });
14875     this.state = {};
14876     Roo.state.Provider.superclass.constructor.call(this);
14877 };
14878 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14879     /**
14880      * Returns the current value for a key
14881      * @param {String} name The key name
14882      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14883      * @return {Mixed} The state data
14884      */
14885     get : function(name, defaultValue){
14886         return typeof this.state[name] == "undefined" ?
14887             defaultValue : this.state[name];
14888     },
14889     
14890     /**
14891      * Clears a value from the state
14892      * @param {String} name The key name
14893      */
14894     clear : function(name){
14895         delete this.state[name];
14896         this.fireEvent("statechange", this, name, null);
14897     },
14898     
14899     /**
14900      * Sets the value for a key
14901      * @param {String} name The key name
14902      * @param {Mixed} value The value to set
14903      */
14904     set : function(name, value){
14905         this.state[name] = value;
14906         this.fireEvent("statechange", this, name, value);
14907     },
14908     
14909     /**
14910      * Decodes a string previously encoded with {@link #encodeValue}.
14911      * @param {String} value The value to decode
14912      * @return {Mixed} The decoded value
14913      */
14914     decodeValue : function(cookie){
14915         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14916         var matches = re.exec(unescape(cookie));
14917         if(!matches || !matches[1]) {
14918             return; // non state cookie
14919         }
14920         var type = matches[1];
14921         var v = matches[2];
14922         switch(type){
14923             case "n":
14924                 return parseFloat(v);
14925             case "d":
14926                 return new Date(Date.parse(v));
14927             case "b":
14928                 return (v == "1");
14929             case "a":
14930                 var all = [];
14931                 var values = v.split("^");
14932                 for(var i = 0, len = values.length; i < len; i++){
14933                     all.push(this.decodeValue(values[i]));
14934                 }
14935                 return all;
14936            case "o":
14937                 var all = {};
14938                 var values = v.split("^");
14939                 for(var i = 0, len = values.length; i < len; i++){
14940                     var kv = values[i].split("=");
14941                     all[kv[0]] = this.decodeValue(kv[1]);
14942                 }
14943                 return all;
14944            default:
14945                 return v;
14946         }
14947     },
14948     
14949     /**
14950      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14951      * @param {Mixed} value The value to encode
14952      * @return {String} The encoded value
14953      */
14954     encodeValue : function(v){
14955         var enc;
14956         if(typeof v == "number"){
14957             enc = "n:" + v;
14958         }else if(typeof v == "boolean"){
14959             enc = "b:" + (v ? "1" : "0");
14960         }else if(v instanceof Date){
14961             enc = "d:" + v.toGMTString();
14962         }else if(v instanceof Array){
14963             var flat = "";
14964             for(var i = 0, len = v.length; i < len; i++){
14965                 flat += this.encodeValue(v[i]);
14966                 if(i != len-1) {
14967                     flat += "^";
14968                 }
14969             }
14970             enc = "a:" + flat;
14971         }else if(typeof v == "object"){
14972             var flat = "";
14973             for(var key in v){
14974                 if(typeof v[key] != "function"){
14975                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14976                 }
14977             }
14978             enc = "o:" + flat.substring(0, flat.length-1);
14979         }else{
14980             enc = "s:" + v;
14981         }
14982         return escape(enc);        
14983     }
14984 });
14985
14986 /*
14987  * Based on:
14988  * Ext JS Library 1.1.1
14989  * Copyright(c) 2006-2007, Ext JS, LLC.
14990  *
14991  * Originally Released Under LGPL - original licence link has changed is not relivant.
14992  *
14993  * Fork - LGPL
14994  * <script type="text/javascript">
14995  */
14996 /**
14997  * @class Roo.state.Manager
14998  * This is the global state manager. By default all components that are "state aware" check this class
14999  * for state information if you don't pass them a custom state provider. In order for this class
15000  * to be useful, it must be initialized with a provider when your application initializes.
15001  <pre><code>
15002 // in your initialization function
15003 init : function(){
15004    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15005    ...
15006    // supposed you have a {@link Roo.BorderLayout}
15007    var layout = new Roo.BorderLayout(...);
15008    layout.restoreState();
15009    // or a {Roo.BasicDialog}
15010    var dialog = new Roo.BasicDialog(...);
15011    dialog.restoreState();
15012  </code></pre>
15013  * @singleton
15014  */
15015 Roo.state.Manager = function(){
15016     var provider = new Roo.state.Provider();
15017     
15018     return {
15019         /**
15020          * Configures the default state provider for your application
15021          * @param {Provider} stateProvider The state provider to set
15022          */
15023         setProvider : function(stateProvider){
15024             provider = stateProvider;
15025         },
15026         
15027         /**
15028          * Returns the current value for a key
15029          * @param {String} name The key name
15030          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15031          * @return {Mixed} The state data
15032          */
15033         get : function(key, defaultValue){
15034             return provider.get(key, defaultValue);
15035         },
15036         
15037         /**
15038          * Sets the value for a key
15039          * @param {String} name The key name
15040          * @param {Mixed} value The state data
15041          */
15042          set : function(key, value){
15043             provider.set(key, value);
15044         },
15045         
15046         /**
15047          * Clears a value from the state
15048          * @param {String} name The key name
15049          */
15050         clear : function(key){
15051             provider.clear(key);
15052         },
15053         
15054         /**
15055          * Gets the currently configured state provider
15056          * @return {Provider} The state provider
15057          */
15058         getProvider : function(){
15059             return provider;
15060         }
15061     };
15062 }();
15063 /*
15064  * Based on:
15065  * Ext JS Library 1.1.1
15066  * Copyright(c) 2006-2007, Ext JS, LLC.
15067  *
15068  * Originally Released Under LGPL - original licence link has changed is not relivant.
15069  *
15070  * Fork - LGPL
15071  * <script type="text/javascript">
15072  */
15073 /**
15074  * @class Roo.state.CookieProvider
15075  * @extends Roo.state.Provider
15076  * The default Provider implementation which saves state via cookies.
15077  * <br />Usage:
15078  <pre><code>
15079    var cp = new Roo.state.CookieProvider({
15080        path: "/cgi-bin/",
15081        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15082        domain: "roojs.com"
15083    })
15084    Roo.state.Manager.setProvider(cp);
15085  </code></pre>
15086  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15087  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15088  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15089  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15090  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15091  * domain the page is running on including the 'www' like 'www.roojs.com')
15092  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15093  * @constructor
15094  * Create a new CookieProvider
15095  * @param {Object} config The configuration object
15096  */
15097 Roo.state.CookieProvider = function(config){
15098     Roo.state.CookieProvider.superclass.constructor.call(this);
15099     this.path = "/";
15100     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15101     this.domain = null;
15102     this.secure = false;
15103     Roo.apply(this, config);
15104     this.state = this.readCookies();
15105 };
15106
15107 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15108     // private
15109     set : function(name, value){
15110         if(typeof value == "undefined" || value === null){
15111             this.clear(name);
15112             return;
15113         }
15114         this.setCookie(name, value);
15115         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15116     },
15117
15118     // private
15119     clear : function(name){
15120         this.clearCookie(name);
15121         Roo.state.CookieProvider.superclass.clear.call(this, name);
15122     },
15123
15124     // private
15125     readCookies : function(){
15126         var cookies = {};
15127         var c = document.cookie + ";";
15128         var re = /\s?(.*?)=(.*?);/g;
15129         var matches;
15130         while((matches = re.exec(c)) != null){
15131             var name = matches[1];
15132             var value = matches[2];
15133             if(name && name.substring(0,3) == "ys-"){
15134                 cookies[name.substr(3)] = this.decodeValue(value);
15135             }
15136         }
15137         return cookies;
15138     },
15139
15140     // private
15141     setCookie : function(name, value){
15142         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15143            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15144            ((this.path == null) ? "" : ("; path=" + this.path)) +
15145            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15146            ((this.secure == true) ? "; secure" : "");
15147     },
15148
15149     // private
15150     clearCookie : function(name){
15151         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15152            ((this.path == null) ? "" : ("; path=" + this.path)) +
15153            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15154            ((this.secure == true) ? "; secure" : "");
15155     }
15156 });/*
15157  * Based on:
15158  * Ext JS Library 1.1.1
15159  * Copyright(c) 2006-2007, Ext JS, LLC.
15160  *
15161  * Originally Released Under LGPL - original licence link has changed is not relivant.
15162  *
15163  * Fork - LGPL
15164  * <script type="text/javascript">
15165  */
15166  
15167
15168 /**
15169  * @class Roo.ComponentMgr
15170  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15171  * @singleton
15172  */
15173 Roo.ComponentMgr = function(){
15174     var all = new Roo.util.MixedCollection();
15175
15176     return {
15177         /**
15178          * Registers a component.
15179          * @param {Roo.Component} c The component
15180          */
15181         register : function(c){
15182             all.add(c);
15183         },
15184
15185         /**
15186          * Unregisters a component.
15187          * @param {Roo.Component} c The component
15188          */
15189         unregister : function(c){
15190             all.remove(c);
15191         },
15192
15193         /**
15194          * Returns a component by id
15195          * @param {String} id The component id
15196          */
15197         get : function(id){
15198             return all.get(id);
15199         },
15200
15201         /**
15202          * Registers a function that will be called when a specified component is added to ComponentMgr
15203          * @param {String} id The component id
15204          * @param {Funtction} fn The callback function
15205          * @param {Object} scope The scope of the callback
15206          */
15207         onAvailable : function(id, fn, scope){
15208             all.on("add", function(index, o){
15209                 if(o.id == id){
15210                     fn.call(scope || o, o);
15211                     all.un("add", fn, scope);
15212                 }
15213             });
15214         }
15215     };
15216 }();/*
15217  * Based on:
15218  * Ext JS Library 1.1.1
15219  * Copyright(c) 2006-2007, Ext JS, LLC.
15220  *
15221  * Originally Released Under LGPL - original licence link has changed is not relivant.
15222  *
15223  * Fork - LGPL
15224  * <script type="text/javascript">
15225  */
15226  
15227 /**
15228  * @class Roo.Component
15229  * @extends Roo.util.Observable
15230  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15231  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15232  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15233  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15234  * All visual components (widgets) that require rendering into a layout should subclass Component.
15235  * @constructor
15236  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15237  * 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
15238  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15239  */
15240 Roo.Component = function(config){
15241     config = config || {};
15242     if(config.tagName || config.dom || typeof config == "string"){ // element object
15243         config = {el: config, id: config.id || config};
15244     }
15245     this.initialConfig = config;
15246
15247     Roo.apply(this, config);
15248     this.addEvents({
15249         /**
15250          * @event disable
15251          * Fires after the component is disabled.
15252              * @param {Roo.Component} this
15253              */
15254         disable : true,
15255         /**
15256          * @event enable
15257          * Fires after the component is enabled.
15258              * @param {Roo.Component} this
15259              */
15260         enable : true,
15261         /**
15262          * @event beforeshow
15263          * Fires before the component is shown.  Return false to stop the show.
15264              * @param {Roo.Component} this
15265              */
15266         beforeshow : true,
15267         /**
15268          * @event show
15269          * Fires after the component is shown.
15270              * @param {Roo.Component} this
15271              */
15272         show : true,
15273         /**
15274          * @event beforehide
15275          * Fires before the component is hidden. Return false to stop the hide.
15276              * @param {Roo.Component} this
15277              */
15278         beforehide : true,
15279         /**
15280          * @event hide
15281          * Fires after the component is hidden.
15282              * @param {Roo.Component} this
15283              */
15284         hide : true,
15285         /**
15286          * @event beforerender
15287          * Fires before the component is rendered. Return false to stop the render.
15288              * @param {Roo.Component} this
15289              */
15290         beforerender : true,
15291         /**
15292          * @event render
15293          * Fires after the component is rendered.
15294              * @param {Roo.Component} this
15295              */
15296         render : true,
15297         /**
15298          * @event beforedestroy
15299          * Fires before the component is destroyed. Return false to stop the destroy.
15300              * @param {Roo.Component} this
15301              */
15302         beforedestroy : true,
15303         /**
15304          * @event destroy
15305          * Fires after the component is destroyed.
15306              * @param {Roo.Component} this
15307              */
15308         destroy : true
15309     });
15310     if(!this.id){
15311         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15312     }
15313     Roo.ComponentMgr.register(this);
15314     Roo.Component.superclass.constructor.call(this);
15315     this.initComponent();
15316     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15317         this.render(this.renderTo);
15318         delete this.renderTo;
15319     }
15320 };
15321
15322 /** @private */
15323 Roo.Component.AUTO_ID = 1000;
15324
15325 Roo.extend(Roo.Component, Roo.util.Observable, {
15326     /**
15327      * @scope Roo.Component.prototype
15328      * @type {Boolean}
15329      * true if this component is hidden. Read-only.
15330      */
15331     hidden : false,
15332     /**
15333      * @type {Boolean}
15334      * true if this component is disabled. Read-only.
15335      */
15336     disabled : false,
15337     /**
15338      * @type {Boolean}
15339      * true if this component has been rendered. Read-only.
15340      */
15341     rendered : false,
15342     
15343     /** @cfg {String} disableClass
15344      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15345      */
15346     disabledClass : "x-item-disabled",
15347         /** @cfg {Boolean} allowDomMove
15348          * Whether the component can move the Dom node when rendering (defaults to true).
15349          */
15350     allowDomMove : true,
15351     /** @cfg {String} hideMode (display|visibility)
15352      * How this component should hidden. Supported values are
15353      * "visibility" (css visibility), "offsets" (negative offset position) and
15354      * "display" (css display) - defaults to "display".
15355      */
15356     hideMode: 'display',
15357
15358     /** @private */
15359     ctype : "Roo.Component",
15360
15361     /**
15362      * @cfg {String} actionMode 
15363      * which property holds the element that used for  hide() / show() / disable() / enable()
15364      * default is 'el' 
15365      */
15366     actionMode : "el",
15367
15368     /** @private */
15369     getActionEl : function(){
15370         return this[this.actionMode];
15371     },
15372
15373     initComponent : Roo.emptyFn,
15374     /**
15375      * If this is a lazy rendering component, render it to its container element.
15376      * @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.
15377      */
15378     render : function(container, position){
15379         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15380             if(!container && this.el){
15381                 this.el = Roo.get(this.el);
15382                 container = this.el.dom.parentNode;
15383                 this.allowDomMove = false;
15384             }
15385             this.container = Roo.get(container);
15386             this.rendered = true;
15387             if(position !== undefined){
15388                 if(typeof position == 'number'){
15389                     position = this.container.dom.childNodes[position];
15390                 }else{
15391                     position = Roo.getDom(position);
15392                 }
15393             }
15394             this.onRender(this.container, position || null);
15395             if(this.cls){
15396                 this.el.addClass(this.cls);
15397                 delete this.cls;
15398             }
15399             if(this.style){
15400                 this.el.applyStyles(this.style);
15401                 delete this.style;
15402             }
15403             this.fireEvent("render", this);
15404             this.afterRender(this.container);
15405             if(this.hidden){
15406                 this.hide();
15407             }
15408             if(this.disabled){
15409                 this.disable();
15410             }
15411         }
15412         return this;
15413     },
15414
15415     /** @private */
15416     // default function is not really useful
15417     onRender : function(ct, position){
15418         if(this.el){
15419             this.el = Roo.get(this.el);
15420             if(this.allowDomMove !== false){
15421                 ct.dom.insertBefore(this.el.dom, position);
15422             }
15423         }
15424     },
15425
15426     /** @private */
15427     getAutoCreate : function(){
15428         var cfg = typeof this.autoCreate == "object" ?
15429                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15430         if(this.id && !cfg.id){
15431             cfg.id = this.id;
15432         }
15433         return cfg;
15434     },
15435
15436     /** @private */
15437     afterRender : Roo.emptyFn,
15438
15439     /**
15440      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15441      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15442      */
15443     destroy : function(){
15444         if(this.fireEvent("beforedestroy", this) !== false){
15445             this.purgeListeners();
15446             this.beforeDestroy();
15447             if(this.rendered){
15448                 this.el.removeAllListeners();
15449                 this.el.remove();
15450                 if(this.actionMode == "container"){
15451                     this.container.remove();
15452                 }
15453             }
15454             this.onDestroy();
15455             Roo.ComponentMgr.unregister(this);
15456             this.fireEvent("destroy", this);
15457         }
15458     },
15459
15460         /** @private */
15461     beforeDestroy : function(){
15462
15463     },
15464
15465         /** @private */
15466         onDestroy : function(){
15467
15468     },
15469
15470     /**
15471      * Returns the underlying {@link Roo.Element}.
15472      * @return {Roo.Element} The element
15473      */
15474     getEl : function(){
15475         return this.el;
15476     },
15477
15478     /**
15479      * Returns the id of this component.
15480      * @return {String}
15481      */
15482     getId : function(){
15483         return this.id;
15484     },
15485
15486     /**
15487      * Try to focus this component.
15488      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15489      * @return {Roo.Component} this
15490      */
15491     focus : function(selectText){
15492         if(this.rendered){
15493             this.el.focus();
15494             if(selectText === true){
15495                 this.el.dom.select();
15496             }
15497         }
15498         return this;
15499     },
15500
15501     /** @private */
15502     blur : function(){
15503         if(this.rendered){
15504             this.el.blur();
15505         }
15506         return this;
15507     },
15508
15509     /**
15510      * Disable this component.
15511      * @return {Roo.Component} this
15512      */
15513     disable : function(){
15514         if(this.rendered){
15515             this.onDisable();
15516         }
15517         this.disabled = true;
15518         this.fireEvent("disable", this);
15519         return this;
15520     },
15521
15522         // private
15523     onDisable : function(){
15524         this.getActionEl().addClass(this.disabledClass);
15525         this.el.dom.disabled = true;
15526     },
15527
15528     /**
15529      * Enable this component.
15530      * @return {Roo.Component} this
15531      */
15532     enable : function(){
15533         if(this.rendered){
15534             this.onEnable();
15535         }
15536         this.disabled = false;
15537         this.fireEvent("enable", this);
15538         return this;
15539     },
15540
15541         // private
15542     onEnable : function(){
15543         this.getActionEl().removeClass(this.disabledClass);
15544         this.el.dom.disabled = false;
15545     },
15546
15547     /**
15548      * Convenience function for setting disabled/enabled by boolean.
15549      * @param {Boolean} disabled
15550      */
15551     setDisabled : function(disabled){
15552         this[disabled ? "disable" : "enable"]();
15553     },
15554
15555     /**
15556      * Show this component.
15557      * @return {Roo.Component} this
15558      */
15559     show: function(){
15560         if(this.fireEvent("beforeshow", this) !== false){
15561             this.hidden = false;
15562             if(this.rendered){
15563                 this.onShow();
15564             }
15565             this.fireEvent("show", this);
15566         }
15567         return this;
15568     },
15569
15570     // private
15571     onShow : function(){
15572         var ae = this.getActionEl();
15573         if(this.hideMode == 'visibility'){
15574             ae.dom.style.visibility = "visible";
15575         }else if(this.hideMode == 'offsets'){
15576             ae.removeClass('x-hidden');
15577         }else{
15578             ae.dom.style.display = "";
15579         }
15580     },
15581
15582     /**
15583      * Hide this component.
15584      * @return {Roo.Component} this
15585      */
15586     hide: function(){
15587         if(this.fireEvent("beforehide", this) !== false){
15588             this.hidden = true;
15589             if(this.rendered){
15590                 this.onHide();
15591             }
15592             this.fireEvent("hide", this);
15593         }
15594         return this;
15595     },
15596
15597     // private
15598     onHide : function(){
15599         var ae = this.getActionEl();
15600         if(this.hideMode == 'visibility'){
15601             ae.dom.style.visibility = "hidden";
15602         }else if(this.hideMode == 'offsets'){
15603             ae.addClass('x-hidden');
15604         }else{
15605             ae.dom.style.display = "none";
15606         }
15607     },
15608
15609     /**
15610      * Convenience function to hide or show this component by boolean.
15611      * @param {Boolean} visible True to show, false to hide
15612      * @return {Roo.Component} this
15613      */
15614     setVisible: function(visible){
15615         if(visible) {
15616             this.show();
15617         }else{
15618             this.hide();
15619         }
15620         return this;
15621     },
15622
15623     /**
15624      * Returns true if this component is visible.
15625      */
15626     isVisible : function(){
15627         return this.getActionEl().isVisible();
15628     },
15629
15630     cloneConfig : function(overrides){
15631         overrides = overrides || {};
15632         var id = overrides.id || Roo.id();
15633         var cfg = Roo.applyIf(overrides, this.initialConfig);
15634         cfg.id = id; // prevent dup id
15635         return new this.constructor(cfg);
15636     }
15637 });/*
15638  * Based on:
15639  * Ext JS Library 1.1.1
15640  * Copyright(c) 2006-2007, Ext JS, LLC.
15641  *
15642  * Originally Released Under LGPL - original licence link has changed is not relivant.
15643  *
15644  * Fork - LGPL
15645  * <script type="text/javascript">
15646  */
15647
15648 /**
15649  * @class Roo.BoxComponent
15650  * @extends Roo.Component
15651  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15652  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15653  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15654  * layout containers.
15655  * @constructor
15656  * @param {Roo.Element/String/Object} config The configuration options.
15657  */
15658 Roo.BoxComponent = function(config){
15659     Roo.Component.call(this, config);
15660     this.addEvents({
15661         /**
15662          * @event resize
15663          * Fires after the component is resized.
15664              * @param {Roo.Component} this
15665              * @param {Number} adjWidth The box-adjusted width that was set
15666              * @param {Number} adjHeight The box-adjusted height that was set
15667              * @param {Number} rawWidth The width that was originally specified
15668              * @param {Number} rawHeight The height that was originally specified
15669              */
15670         resize : true,
15671         /**
15672          * @event move
15673          * Fires after the component is moved.
15674              * @param {Roo.Component} this
15675              * @param {Number} x The new x position
15676              * @param {Number} y The new y position
15677              */
15678         move : true
15679     });
15680 };
15681
15682 Roo.extend(Roo.BoxComponent, Roo.Component, {
15683     // private, set in afterRender to signify that the component has been rendered
15684     boxReady : false,
15685     // private, used to defer height settings to subclasses
15686     deferHeight: false,
15687     /** @cfg {Number} width
15688      * width (optional) size of component
15689      */
15690      /** @cfg {Number} height
15691      * height (optional) size of component
15692      */
15693      
15694     /**
15695      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15696      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15697      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15698      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15699      * @return {Roo.BoxComponent} this
15700      */
15701     setSize : function(w, h){
15702         // support for standard size objects
15703         if(typeof w == 'object'){
15704             h = w.height;
15705             w = w.width;
15706         }
15707         // not rendered
15708         if(!this.boxReady){
15709             this.width = w;
15710             this.height = h;
15711             return this;
15712         }
15713
15714         // prevent recalcs when not needed
15715         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15716             return this;
15717         }
15718         this.lastSize = {width: w, height: h};
15719
15720         var adj = this.adjustSize(w, h);
15721         var aw = adj.width, ah = adj.height;
15722         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15723             var rz = this.getResizeEl();
15724             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15725                 rz.setSize(aw, ah);
15726             }else if(!this.deferHeight && ah !== undefined){
15727                 rz.setHeight(ah);
15728             }else if(aw !== undefined){
15729                 rz.setWidth(aw);
15730             }
15731             this.onResize(aw, ah, w, h);
15732             this.fireEvent('resize', this, aw, ah, w, h);
15733         }
15734         return this;
15735     },
15736
15737     /**
15738      * Gets the current size of the component's underlying element.
15739      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15740      */
15741     getSize : function(){
15742         return this.el.getSize();
15743     },
15744
15745     /**
15746      * Gets the current XY position of the component's underlying element.
15747      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15748      * @return {Array} The XY position of the element (e.g., [100, 200])
15749      */
15750     getPosition : function(local){
15751         if(local === true){
15752             return [this.el.getLeft(true), this.el.getTop(true)];
15753         }
15754         return this.xy || this.el.getXY();
15755     },
15756
15757     /**
15758      * Gets the current box measurements of the component's underlying element.
15759      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15760      * @returns {Object} box An object in the format {x, y, width, height}
15761      */
15762     getBox : function(local){
15763         var s = this.el.getSize();
15764         if(local){
15765             s.x = this.el.getLeft(true);
15766             s.y = this.el.getTop(true);
15767         }else{
15768             var xy = this.xy || this.el.getXY();
15769             s.x = xy[0];
15770             s.y = xy[1];
15771         }
15772         return s;
15773     },
15774
15775     /**
15776      * Sets the current box measurements of the component's underlying element.
15777      * @param {Object} box An object in the format {x, y, width, height}
15778      * @returns {Roo.BoxComponent} this
15779      */
15780     updateBox : function(box){
15781         this.setSize(box.width, box.height);
15782         this.setPagePosition(box.x, box.y);
15783         return this;
15784     },
15785
15786     // protected
15787     getResizeEl : function(){
15788         return this.resizeEl || this.el;
15789     },
15790
15791     // protected
15792     getPositionEl : function(){
15793         return this.positionEl || this.el;
15794     },
15795
15796     /**
15797      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15798      * This method fires the move event.
15799      * @param {Number} left The new left
15800      * @param {Number} top The new top
15801      * @returns {Roo.BoxComponent} this
15802      */
15803     setPosition : function(x, y){
15804         this.x = x;
15805         this.y = y;
15806         if(!this.boxReady){
15807             return this;
15808         }
15809         var adj = this.adjustPosition(x, y);
15810         var ax = adj.x, ay = adj.y;
15811
15812         var el = this.getPositionEl();
15813         if(ax !== undefined || ay !== undefined){
15814             if(ax !== undefined && ay !== undefined){
15815                 el.setLeftTop(ax, ay);
15816             }else if(ax !== undefined){
15817                 el.setLeft(ax);
15818             }else if(ay !== undefined){
15819                 el.setTop(ay);
15820             }
15821             this.onPosition(ax, ay);
15822             this.fireEvent('move', this, ax, ay);
15823         }
15824         return this;
15825     },
15826
15827     /**
15828      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15829      * This method fires the move event.
15830      * @param {Number} x The new x position
15831      * @param {Number} y The new y position
15832      * @returns {Roo.BoxComponent} this
15833      */
15834     setPagePosition : function(x, y){
15835         this.pageX = x;
15836         this.pageY = y;
15837         if(!this.boxReady){
15838             return;
15839         }
15840         if(x === undefined || y === undefined){ // cannot translate undefined points
15841             return;
15842         }
15843         var p = this.el.translatePoints(x, y);
15844         this.setPosition(p.left, p.top);
15845         return this;
15846     },
15847
15848     // private
15849     onRender : function(ct, position){
15850         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15851         if(this.resizeEl){
15852             this.resizeEl = Roo.get(this.resizeEl);
15853         }
15854         if(this.positionEl){
15855             this.positionEl = Roo.get(this.positionEl);
15856         }
15857     },
15858
15859     // private
15860     afterRender : function(){
15861         Roo.BoxComponent.superclass.afterRender.call(this);
15862         this.boxReady = true;
15863         this.setSize(this.width, this.height);
15864         if(this.x || this.y){
15865             this.setPosition(this.x, this.y);
15866         }
15867         if(this.pageX || this.pageY){
15868             this.setPagePosition(this.pageX, this.pageY);
15869         }
15870     },
15871
15872     /**
15873      * Force the component's size to recalculate based on the underlying element's current height and width.
15874      * @returns {Roo.BoxComponent} this
15875      */
15876     syncSize : function(){
15877         delete this.lastSize;
15878         this.setSize(this.el.getWidth(), this.el.getHeight());
15879         return this;
15880     },
15881
15882     /**
15883      * Called after the component is resized, this method is empty by default but can be implemented by any
15884      * subclass that needs to perform custom logic after a resize occurs.
15885      * @param {Number} adjWidth The box-adjusted width that was set
15886      * @param {Number} adjHeight The box-adjusted height that was set
15887      * @param {Number} rawWidth The width that was originally specified
15888      * @param {Number} rawHeight The height that was originally specified
15889      */
15890     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15891
15892     },
15893
15894     /**
15895      * Called after the component is moved, this method is empty by default but can be implemented by any
15896      * subclass that needs to perform custom logic after a move occurs.
15897      * @param {Number} x The new x position
15898      * @param {Number} y The new y position
15899      */
15900     onPosition : function(x, y){
15901
15902     },
15903
15904     // private
15905     adjustSize : function(w, h){
15906         if(this.autoWidth){
15907             w = 'auto';
15908         }
15909         if(this.autoHeight){
15910             h = 'auto';
15911         }
15912         return {width : w, height: h};
15913     },
15914
15915     // private
15916     adjustPosition : function(x, y){
15917         return {x : x, y: y};
15918     }
15919 });/*
15920  * Original code for Roojs - LGPL
15921  * <script type="text/javascript">
15922  */
15923  
15924 /**
15925  * @class Roo.XComponent
15926  * A delayed Element creator...
15927  * Or a way to group chunks of interface together.
15928  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15929  *  used in conjunction with XComponent.build() it will create an instance of each element,
15930  *  then call addxtype() to build the User interface.
15931  * 
15932  * Mypart.xyx = new Roo.XComponent({
15933
15934     parent : 'Mypart.xyz', // empty == document.element.!!
15935     order : '001',
15936     name : 'xxxx'
15937     region : 'xxxx'
15938     disabled : function() {} 
15939      
15940     tree : function() { // return an tree of xtype declared components
15941         var MODULE = this;
15942         return 
15943         {
15944             xtype : 'NestedLayoutPanel',
15945             // technicall
15946         }
15947      ]
15948  *})
15949  *
15950  *
15951  * It can be used to build a big heiracy, with parent etc.
15952  * or you can just use this to render a single compoent to a dom element
15953  * MYPART.render(Roo.Element | String(id) | dom_element )
15954  *
15955  *
15956  * Usage patterns.
15957  *
15958  * Classic Roo
15959  *
15960  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15961  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15962  *
15963  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15964  *
15965  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15966  * - if mulitple topModules exist, the last one is defined as the top module.
15967  *
15968  * Embeded Roo
15969  * 
15970  * When the top level or multiple modules are to embedded into a existing HTML page,
15971  * the parent element can container '#id' of the element where the module will be drawn.
15972  *
15973  * Bootstrap Roo
15974  *
15975  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15976  * it relies more on a include mechanism, where sub modules are included into an outer page.
15977  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15978  * 
15979  * Bootstrap Roo Included elements
15980  *
15981  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15982  * hence confusing the component builder as it thinks there are multiple top level elements. 
15983  *
15984  * 
15985  * 
15986  * @extends Roo.util.Observable
15987  * @constructor
15988  * @param cfg {Object} configuration of component
15989  * 
15990  */
15991 Roo.XComponent = function(cfg) {
15992     Roo.apply(this, cfg);
15993     this.addEvents({ 
15994         /**
15995              * @event built
15996              * Fires when this the componnt is built
15997              * @param {Roo.XComponent} c the component
15998              */
15999         'built' : true
16000         
16001     });
16002     this.region = this.region || 'center'; // default..
16003     Roo.XComponent.register(this);
16004     this.modules = false;
16005     this.el = false; // where the layout goes..
16006     
16007     
16008 }
16009 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16010     /**
16011      * @property el
16012      * The created element (with Roo.factory())
16013      * @type {Roo.Layout}
16014      */
16015     el  : false,
16016     
16017     /**
16018      * @property el
16019      * for BC  - use el in new code
16020      * @type {Roo.Layout}
16021      */
16022     panel : false,
16023     
16024     /**
16025      * @property layout
16026      * for BC  - use el in new code
16027      * @type {Roo.Layout}
16028      */
16029     layout : false,
16030     
16031      /**
16032      * @cfg {Function|boolean} disabled
16033      * If this module is disabled by some rule, return true from the funtion
16034      */
16035     disabled : false,
16036     
16037     /**
16038      * @cfg {String} parent 
16039      * Name of parent element which it get xtype added to..
16040      */
16041     parent: false,
16042     
16043     /**
16044      * @cfg {String} order
16045      * Used to set the order in which elements are created (usefull for multiple tabs)
16046      */
16047     
16048     order : false,
16049     /**
16050      * @cfg {String} name
16051      * String to display while loading.
16052      */
16053     name : false,
16054     /**
16055      * @cfg {String} region
16056      * Region to render component to (defaults to center)
16057      */
16058     region : 'center',
16059     
16060     /**
16061      * @cfg {Array} items
16062      * A single item array - the first element is the root of the tree..
16063      * It's done this way to stay compatible with the Xtype system...
16064      */
16065     items : false,
16066     
16067     /**
16068      * @property _tree
16069      * The method that retuns the tree of parts that make up this compoennt 
16070      * @type {function}
16071      */
16072     _tree  : false,
16073     
16074      /**
16075      * render
16076      * render element to dom or tree
16077      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16078      */
16079     
16080     render : function(el)
16081     {
16082         
16083         el = el || false;
16084         var hp = this.parent ? 1 : 0;
16085         Roo.debug &&  Roo.log(this);
16086         
16087         var tree = this._tree ? this._tree() : this.tree();
16088
16089         
16090         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16091             // if parent is a '#.....' string, then let's use that..
16092             var ename = this.parent.substr(1);
16093             this.parent = false;
16094             Roo.debug && Roo.log(ename);
16095             switch (ename) {
16096                 case 'bootstrap-body':
16097                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16098                         // this is the BorderLayout standard?
16099                        this.parent = { el : true };
16100                        break;
16101                     }
16102                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16103                         // need to insert stuff...
16104                         this.parent =  {
16105                              el : new Roo.bootstrap.layout.Border({
16106                                  el : document.body, 
16107                      
16108                                  center: {
16109                                     titlebar: false,
16110                                     autoScroll:false,
16111                                     closeOnTab: true,
16112                                     tabPosition: 'top',
16113                                       //resizeTabs: true,
16114                                     alwaysShowTabs: true,
16115                                     hideTabs: false
16116                                      //minTabWidth: 140
16117                                  }
16118                              })
16119                         
16120                          };
16121                          break;
16122                     }
16123                          
16124                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16125                         this.parent = { el :  new  Roo.bootstrap.Body() };
16126                         Roo.debug && Roo.log("setting el to doc body");
16127                          
16128                     } else {
16129                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16130                     }
16131                     break;
16132                 case 'bootstrap':
16133                     this.parent = { el : true};
16134                     // fall through
16135                 default:
16136                     el = Roo.get(ename);
16137                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16138                         this.parent = { el : true};
16139                     }
16140                     
16141                     break;
16142             }
16143                 
16144             
16145             if (!el && !this.parent) {
16146                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16147                 return;
16148             }
16149         }
16150         
16151         Roo.debug && Roo.log("EL:");
16152         Roo.debug && Roo.log(el);
16153         Roo.debug && Roo.log("this.parent.el:");
16154         Roo.debug && Roo.log(this.parent.el);
16155         
16156
16157         // altertive root elements ??? - we need a better way to indicate these.
16158         var is_alt = Roo.XComponent.is_alt ||
16159                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16160                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16161                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16162         
16163         
16164         
16165         if (!this.parent && is_alt) {
16166             //el = Roo.get(document.body);
16167             this.parent = { el : true };
16168         }
16169             
16170             
16171         
16172         if (!this.parent) {
16173             
16174             Roo.debug && Roo.log("no parent - creating one");
16175             
16176             el = el ? Roo.get(el) : false;      
16177             
16178             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16179                 
16180                 this.parent =  {
16181                     el : new Roo.bootstrap.layout.Border({
16182                         el: el || document.body,
16183                     
16184                         center: {
16185                             titlebar: false,
16186                             autoScroll:false,
16187                             closeOnTab: true,
16188                             tabPosition: 'top',
16189                              //resizeTabs: true,
16190                             alwaysShowTabs: false,
16191                             hideTabs: true,
16192                             minTabWidth: 140,
16193                             overflow: 'visible'
16194                          }
16195                      })
16196                 };
16197             } else {
16198             
16199                 // it's a top level one..
16200                 this.parent =  {
16201                     el : new Roo.BorderLayout(el || document.body, {
16202                         center: {
16203                             titlebar: false,
16204                             autoScroll:false,
16205                             closeOnTab: true,
16206                             tabPosition: 'top',
16207                              //resizeTabs: true,
16208                             alwaysShowTabs: el && hp? false :  true,
16209                             hideTabs: el || !hp ? true :  false,
16210                             minTabWidth: 140
16211                          }
16212                     })
16213                 };
16214             }
16215         }
16216         
16217         if (!this.parent.el) {
16218                 // probably an old style ctor, which has been disabled.
16219                 return;
16220
16221         }
16222                 // The 'tree' method is  '_tree now' 
16223             
16224         tree.region = tree.region || this.region;
16225         var is_body = false;
16226         if (this.parent.el === true) {
16227             // bootstrap... - body..
16228             if (el) {
16229                 tree.el = el;
16230             }
16231             this.parent.el = Roo.factory(tree);
16232             is_body = true;
16233         }
16234         
16235         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16236         this.fireEvent('built', this);
16237         
16238         this.panel = this.el;
16239         this.layout = this.panel.layout;
16240         this.parentLayout = this.parent.layout  || false;  
16241          
16242     }
16243     
16244 });
16245
16246 Roo.apply(Roo.XComponent, {
16247     /**
16248      * @property  hideProgress
16249      * true to disable the building progress bar.. usefull on single page renders.
16250      * @type Boolean
16251      */
16252     hideProgress : false,
16253     /**
16254      * @property  buildCompleted
16255      * True when the builder has completed building the interface.
16256      * @type Boolean
16257      */
16258     buildCompleted : false,
16259      
16260     /**
16261      * @property  topModule
16262      * the upper most module - uses document.element as it's constructor.
16263      * @type Object
16264      */
16265      
16266     topModule  : false,
16267       
16268     /**
16269      * @property  modules
16270      * array of modules to be created by registration system.
16271      * @type {Array} of Roo.XComponent
16272      */
16273     
16274     modules : [],
16275     /**
16276      * @property  elmodules
16277      * array of modules to be created by which use #ID 
16278      * @type {Array} of Roo.XComponent
16279      */
16280      
16281     elmodules : [],
16282
16283      /**
16284      * @property  is_alt
16285      * Is an alternative Root - normally used by bootstrap or other systems,
16286      *    where the top element in the tree can wrap 'body' 
16287      * @type {boolean}  (default false)
16288      */
16289      
16290     is_alt : false,
16291     /**
16292      * @property  build_from_html
16293      * Build elements from html - used by bootstrap HTML stuff 
16294      *    - this is cleared after build is completed
16295      * @type {boolean}    (default false)
16296      */
16297      
16298     build_from_html : false,
16299     /**
16300      * Register components to be built later.
16301      *
16302      * This solves the following issues
16303      * - Building is not done on page load, but after an authentication process has occured.
16304      * - Interface elements are registered on page load
16305      * - Parent Interface elements may not be loaded before child, so this handles that..
16306      * 
16307      *
16308      * example:
16309      * 
16310      * MyApp.register({
16311           order : '000001',
16312           module : 'Pman.Tab.projectMgr',
16313           region : 'center',
16314           parent : 'Pman.layout',
16315           disabled : false,  // or use a function..
16316         })
16317      
16318      * * @param {Object} details about module
16319      */
16320     register : function(obj) {
16321                 
16322         Roo.XComponent.event.fireEvent('register', obj);
16323         switch(typeof(obj.disabled) ) {
16324                 
16325             case 'undefined':
16326                 break;
16327             
16328             case 'function':
16329                 if ( obj.disabled() ) {
16330                         return;
16331                 }
16332                 break;
16333             
16334             default:
16335                 if (obj.disabled) {
16336                         return;
16337                 }
16338                 break;
16339         }
16340                 
16341         this.modules.push(obj);
16342          
16343     },
16344     /**
16345      * convert a string to an object..
16346      * eg. 'AAA.BBB' -> finds AAA.BBB
16347
16348      */
16349     
16350     toObject : function(str)
16351     {
16352         if (!str || typeof(str) == 'object') {
16353             return str;
16354         }
16355         if (str.substring(0,1) == '#') {
16356             return str;
16357         }
16358
16359         var ar = str.split('.');
16360         var rt, o;
16361         rt = ar.shift();
16362             /** eval:var:o */
16363         try {
16364             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16365         } catch (e) {
16366             throw "Module not found : " + str;
16367         }
16368         
16369         if (o === false) {
16370             throw "Module not found : " + str;
16371         }
16372         Roo.each(ar, function(e) {
16373             if (typeof(o[e]) == 'undefined') {
16374                 throw "Module not found : " + str;
16375             }
16376             o = o[e];
16377         });
16378         
16379         return o;
16380         
16381     },
16382     
16383     
16384     /**
16385      * move modules into their correct place in the tree..
16386      * 
16387      */
16388     preBuild : function ()
16389     {
16390         var _t = this;
16391         Roo.each(this.modules , function (obj)
16392         {
16393             Roo.XComponent.event.fireEvent('beforebuild', obj);
16394             
16395             var opar = obj.parent;
16396             try { 
16397                 obj.parent = this.toObject(opar);
16398             } catch(e) {
16399                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16400                 return;
16401             }
16402             
16403             if (!obj.parent) {
16404                 Roo.debug && Roo.log("GOT top level module");
16405                 Roo.debug && Roo.log(obj);
16406                 obj.modules = new Roo.util.MixedCollection(false, 
16407                     function(o) { return o.order + '' }
16408                 );
16409                 this.topModule = obj;
16410                 return;
16411             }
16412                         // parent is a string (usually a dom element name..)
16413             if (typeof(obj.parent) == 'string') {
16414                 this.elmodules.push(obj);
16415                 return;
16416             }
16417             if (obj.parent.constructor != Roo.XComponent) {
16418                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16419             }
16420             if (!obj.parent.modules) {
16421                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16422                     function(o) { return o.order + '' }
16423                 );
16424             }
16425             if (obj.parent.disabled) {
16426                 obj.disabled = true;
16427             }
16428             obj.parent.modules.add(obj);
16429         }, this);
16430     },
16431     
16432      /**
16433      * make a list of modules to build.
16434      * @return {Array} list of modules. 
16435      */ 
16436     
16437     buildOrder : function()
16438     {
16439         var _this = this;
16440         var cmp = function(a,b) {   
16441             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16442         };
16443         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16444             throw "No top level modules to build";
16445         }
16446         
16447         // make a flat list in order of modules to build.
16448         var mods = this.topModule ? [ this.topModule ] : [];
16449                 
16450         
16451         // elmodules (is a list of DOM based modules )
16452         Roo.each(this.elmodules, function(e) {
16453             mods.push(e);
16454             if (!this.topModule &&
16455                 typeof(e.parent) == 'string' &&
16456                 e.parent.substring(0,1) == '#' &&
16457                 Roo.get(e.parent.substr(1))
16458                ) {
16459                 
16460                 _this.topModule = e;
16461             }
16462             
16463         });
16464
16465         
16466         // add modules to their parents..
16467         var addMod = function(m) {
16468             Roo.debug && Roo.log("build Order: add: " + m.name);
16469                 
16470             mods.push(m);
16471             if (m.modules && !m.disabled) {
16472                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16473                 m.modules.keySort('ASC',  cmp );
16474                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16475     
16476                 m.modules.each(addMod);
16477             } else {
16478                 Roo.debug && Roo.log("build Order: no child modules");
16479             }
16480             // not sure if this is used any more..
16481             if (m.finalize) {
16482                 m.finalize.name = m.name + " (clean up) ";
16483                 mods.push(m.finalize);
16484             }
16485             
16486         }
16487         if (this.topModule && this.topModule.modules) { 
16488             this.topModule.modules.keySort('ASC',  cmp );
16489             this.topModule.modules.each(addMod);
16490         } 
16491         return mods;
16492     },
16493     
16494      /**
16495      * Build the registered modules.
16496      * @param {Object} parent element.
16497      * @param {Function} optional method to call after module has been added.
16498      * 
16499      */ 
16500    
16501     build : function(opts) 
16502     {
16503         
16504         if (typeof(opts) != 'undefined') {
16505             Roo.apply(this,opts);
16506         }
16507         
16508         this.preBuild();
16509         var mods = this.buildOrder();
16510       
16511         //this.allmods = mods;
16512         //Roo.debug && Roo.log(mods);
16513         //return;
16514         if (!mods.length) { // should not happen
16515             throw "NO modules!!!";
16516         }
16517         
16518         
16519         var msg = "Building Interface...";
16520         // flash it up as modal - so we store the mask!?
16521         if (!this.hideProgress && Roo.MessageBox) {
16522             Roo.MessageBox.show({ title: 'loading' });
16523             Roo.MessageBox.show({
16524                title: "Please wait...",
16525                msg: msg,
16526                width:450,
16527                progress:true,
16528                closable:false,
16529                modal: false
16530               
16531             });
16532         }
16533         var total = mods.length;
16534         
16535         var _this = this;
16536         var progressRun = function() {
16537             if (!mods.length) {
16538                 Roo.debug && Roo.log('hide?');
16539                 if (!this.hideProgress && Roo.MessageBox) {
16540                     Roo.MessageBox.hide();
16541                 }
16542                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16543                 
16544                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16545                 
16546                 // THE END...
16547                 return false;   
16548             }
16549             
16550             var m = mods.shift();
16551             
16552             
16553             Roo.debug && Roo.log(m);
16554             // not sure if this is supported any more.. - modules that are are just function
16555             if (typeof(m) == 'function') { 
16556                 m.call(this);
16557                 return progressRun.defer(10, _this);
16558             } 
16559             
16560             
16561             msg = "Building Interface " + (total  - mods.length) + 
16562                     " of " + total + 
16563                     (m.name ? (' - ' + m.name) : '');
16564                         Roo.debug && Roo.log(msg);
16565             if (!_this.hideProgress &&  Roo.MessageBox) { 
16566                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16567             }
16568             
16569          
16570             // is the module disabled?
16571             var disabled = (typeof(m.disabled) == 'function') ?
16572                 m.disabled.call(m.module.disabled) : m.disabled;    
16573             
16574             
16575             if (disabled) {
16576                 return progressRun(); // we do not update the display!
16577             }
16578             
16579             // now build 
16580             
16581                         
16582                         
16583             m.render();
16584             // it's 10 on top level, and 1 on others??? why...
16585             return progressRun.defer(10, _this);
16586              
16587         }
16588         progressRun.defer(1, _this);
16589      
16590         
16591         
16592     },
16593         
16594         
16595         /**
16596          * Event Object.
16597          *
16598          *
16599          */
16600         event: false, 
16601     /**
16602          * wrapper for event.on - aliased later..  
16603          * Typically use to register a event handler for register:
16604          *
16605          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16606          *
16607          */
16608     on : false
16609    
16610     
16611     
16612 });
16613
16614 Roo.XComponent.event = new Roo.util.Observable({
16615                 events : { 
16616                         /**
16617                          * @event register
16618                          * Fires when an Component is registered,
16619                          * set the disable property on the Component to stop registration.
16620                          * @param {Roo.XComponent} c the component being registerd.
16621                          * 
16622                          */
16623                         'register' : true,
16624             /**
16625                          * @event beforebuild
16626                          * Fires before each Component is built
16627                          * can be used to apply permissions.
16628                          * @param {Roo.XComponent} c the component being registerd.
16629                          * 
16630                          */
16631                         'beforebuild' : true,
16632                         /**
16633                          * @event buildcomplete
16634                          * Fires on the top level element when all elements have been built
16635                          * @param {Roo.XComponent} the top level component.
16636                          */
16637                         'buildcomplete' : true
16638                         
16639                 }
16640 });
16641
16642 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16643  //
16644  /**
16645  * marked - a markdown parser
16646  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16647  * https://github.com/chjj/marked
16648  */
16649
16650
16651 /**
16652  *
16653  * Roo.Markdown - is a very crude wrapper around marked..
16654  *
16655  * usage:
16656  * 
16657  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16658  * 
16659  * Note: move the sample code to the bottom of this
16660  * file before uncommenting it.
16661  *
16662  */
16663
16664 Roo.Markdown = {};
16665 Roo.Markdown.toHtml = function(text) {
16666     
16667     var c = new Roo.Markdown.marked.setOptions({
16668             renderer: new Roo.Markdown.marked.Renderer(),
16669             gfm: true,
16670             tables: true,
16671             breaks: false,
16672             pedantic: false,
16673             sanitize: false,
16674             smartLists: true,
16675             smartypants: false
16676           });
16677     // A FEW HACKS!!?
16678     
16679     text = text.replace(/\\\n/g,' ');
16680     return Roo.Markdown.marked(text);
16681 };
16682 //
16683 // converter
16684 //
16685 // Wraps all "globals" so that the only thing
16686 // exposed is makeHtml().
16687 //
16688 (function() {
16689     
16690     /**
16691      * Block-Level Grammar
16692      */
16693     
16694     var block = {
16695       newline: /^\n+/,
16696       code: /^( {4}[^\n]+\n*)+/,
16697       fences: noop,
16698       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16699       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16700       nptable: noop,
16701       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16702       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16703       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16704       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16705       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16706       table: noop,
16707       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16708       text: /^[^\n]+/
16709     };
16710     
16711     block.bullet = /(?:[*+-]|\d+\.)/;
16712     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16713     block.item = replace(block.item, 'gm')
16714       (/bull/g, block.bullet)
16715       ();
16716     
16717     block.list = replace(block.list)
16718       (/bull/g, block.bullet)
16719       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16720       ('def', '\\n+(?=' + block.def.source + ')')
16721       ();
16722     
16723     block.blockquote = replace(block.blockquote)
16724       ('def', block.def)
16725       ();
16726     
16727     block._tag = '(?!(?:'
16728       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16729       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16730       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16731     
16732     block.html = replace(block.html)
16733       ('comment', /<!--[\s\S]*?-->/)
16734       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16735       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16736       (/tag/g, block._tag)
16737       ();
16738     
16739     block.paragraph = replace(block.paragraph)
16740       ('hr', block.hr)
16741       ('heading', block.heading)
16742       ('lheading', block.lheading)
16743       ('blockquote', block.blockquote)
16744       ('tag', '<' + block._tag)
16745       ('def', block.def)
16746       ();
16747     
16748     /**
16749      * Normal Block Grammar
16750      */
16751     
16752     block.normal = merge({}, block);
16753     
16754     /**
16755      * GFM Block Grammar
16756      */
16757     
16758     block.gfm = merge({}, block.normal, {
16759       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16760       paragraph: /^/,
16761       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16762     });
16763     
16764     block.gfm.paragraph = replace(block.paragraph)
16765       ('(?!', '(?!'
16766         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16767         + block.list.source.replace('\\1', '\\3') + '|')
16768       ();
16769     
16770     /**
16771      * GFM + Tables Block Grammar
16772      */
16773     
16774     block.tables = merge({}, block.gfm, {
16775       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16776       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16777     });
16778     
16779     /**
16780      * Block Lexer
16781      */
16782     
16783     function Lexer(options) {
16784       this.tokens = [];
16785       this.tokens.links = {};
16786       this.options = options || marked.defaults;
16787       this.rules = block.normal;
16788     
16789       if (this.options.gfm) {
16790         if (this.options.tables) {
16791           this.rules = block.tables;
16792         } else {
16793           this.rules = block.gfm;
16794         }
16795       }
16796     }
16797     
16798     /**
16799      * Expose Block Rules
16800      */
16801     
16802     Lexer.rules = block;
16803     
16804     /**
16805      * Static Lex Method
16806      */
16807     
16808     Lexer.lex = function(src, options) {
16809       var lexer = new Lexer(options);
16810       return lexer.lex(src);
16811     };
16812     
16813     /**
16814      * Preprocessing
16815      */
16816     
16817     Lexer.prototype.lex = function(src) {
16818       src = src
16819         .replace(/\r\n|\r/g, '\n')
16820         .replace(/\t/g, '    ')
16821         .replace(/\u00a0/g, ' ')
16822         .replace(/\u2424/g, '\n');
16823     
16824       return this.token(src, true);
16825     };
16826     
16827     /**
16828      * Lexing
16829      */
16830     
16831     Lexer.prototype.token = function(src, top, bq) {
16832       var src = src.replace(/^ +$/gm, '')
16833         , next
16834         , loose
16835         , cap
16836         , bull
16837         , b
16838         , item
16839         , space
16840         , i
16841         , l;
16842     
16843       while (src) {
16844         // newline
16845         if (cap = this.rules.newline.exec(src)) {
16846           src = src.substring(cap[0].length);
16847           if (cap[0].length > 1) {
16848             this.tokens.push({
16849               type: 'space'
16850             });
16851           }
16852         }
16853     
16854         // code
16855         if (cap = this.rules.code.exec(src)) {
16856           src = src.substring(cap[0].length);
16857           cap = cap[0].replace(/^ {4}/gm, '');
16858           this.tokens.push({
16859             type: 'code',
16860             text: !this.options.pedantic
16861               ? cap.replace(/\n+$/, '')
16862               : cap
16863           });
16864           continue;
16865         }
16866     
16867         // fences (gfm)
16868         if (cap = this.rules.fences.exec(src)) {
16869           src = src.substring(cap[0].length);
16870           this.tokens.push({
16871             type: 'code',
16872             lang: cap[2],
16873             text: cap[3] || ''
16874           });
16875           continue;
16876         }
16877     
16878         // heading
16879         if (cap = this.rules.heading.exec(src)) {
16880           src = src.substring(cap[0].length);
16881           this.tokens.push({
16882             type: 'heading',
16883             depth: cap[1].length,
16884             text: cap[2]
16885           });
16886           continue;
16887         }
16888     
16889         // table no leading pipe (gfm)
16890         if (top && (cap = this.rules.nptable.exec(src))) {
16891           src = src.substring(cap[0].length);
16892     
16893           item = {
16894             type: 'table',
16895             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16896             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16897             cells: cap[3].replace(/\n$/, '').split('\n')
16898           };
16899     
16900           for (i = 0; i < item.align.length; i++) {
16901             if (/^ *-+: *$/.test(item.align[i])) {
16902               item.align[i] = 'right';
16903             } else if (/^ *:-+: *$/.test(item.align[i])) {
16904               item.align[i] = 'center';
16905             } else if (/^ *:-+ *$/.test(item.align[i])) {
16906               item.align[i] = 'left';
16907             } else {
16908               item.align[i] = null;
16909             }
16910           }
16911     
16912           for (i = 0; i < item.cells.length; i++) {
16913             item.cells[i] = item.cells[i].split(/ *\| */);
16914           }
16915     
16916           this.tokens.push(item);
16917     
16918           continue;
16919         }
16920     
16921         // lheading
16922         if (cap = this.rules.lheading.exec(src)) {
16923           src = src.substring(cap[0].length);
16924           this.tokens.push({
16925             type: 'heading',
16926             depth: cap[2] === '=' ? 1 : 2,
16927             text: cap[1]
16928           });
16929           continue;
16930         }
16931     
16932         // hr
16933         if (cap = this.rules.hr.exec(src)) {
16934           src = src.substring(cap[0].length);
16935           this.tokens.push({
16936             type: 'hr'
16937           });
16938           continue;
16939         }
16940     
16941         // blockquote
16942         if (cap = this.rules.blockquote.exec(src)) {
16943           src = src.substring(cap[0].length);
16944     
16945           this.tokens.push({
16946             type: 'blockquote_start'
16947           });
16948     
16949           cap = cap[0].replace(/^ *> ?/gm, '');
16950     
16951           // Pass `top` to keep the current
16952           // "toplevel" state. This is exactly
16953           // how markdown.pl works.
16954           this.token(cap, top, true);
16955     
16956           this.tokens.push({
16957             type: 'blockquote_end'
16958           });
16959     
16960           continue;
16961         }
16962     
16963         // list
16964         if (cap = this.rules.list.exec(src)) {
16965           src = src.substring(cap[0].length);
16966           bull = cap[2];
16967     
16968           this.tokens.push({
16969             type: 'list_start',
16970             ordered: bull.length > 1
16971           });
16972     
16973           // Get each top-level item.
16974           cap = cap[0].match(this.rules.item);
16975     
16976           next = false;
16977           l = cap.length;
16978           i = 0;
16979     
16980           for (; i < l; i++) {
16981             item = cap[i];
16982     
16983             // Remove the list item's bullet
16984             // so it is seen as the next token.
16985             space = item.length;
16986             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16987     
16988             // Outdent whatever the
16989             // list item contains. Hacky.
16990             if (~item.indexOf('\n ')) {
16991               space -= item.length;
16992               item = !this.options.pedantic
16993                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16994                 : item.replace(/^ {1,4}/gm, '');
16995             }
16996     
16997             // Determine whether the next list item belongs here.
16998             // Backpedal if it does not belong in this list.
16999             if (this.options.smartLists && i !== l - 1) {
17000               b = block.bullet.exec(cap[i + 1])[0];
17001               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17002                 src = cap.slice(i + 1).join('\n') + src;
17003                 i = l - 1;
17004               }
17005             }
17006     
17007             // Determine whether item is loose or not.
17008             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17009             // for discount behavior.
17010             loose = next || /\n\n(?!\s*$)/.test(item);
17011             if (i !== l - 1) {
17012               next = item.charAt(item.length - 1) === '\n';
17013               if (!loose) { loose = next; }
17014             }
17015     
17016             this.tokens.push({
17017               type: loose
17018                 ? 'loose_item_start'
17019                 : 'list_item_start'
17020             });
17021     
17022             // Recurse.
17023             this.token(item, false, bq);
17024     
17025             this.tokens.push({
17026               type: 'list_item_end'
17027             });
17028           }
17029     
17030           this.tokens.push({
17031             type: 'list_end'
17032           });
17033     
17034           continue;
17035         }
17036     
17037         // html
17038         if (cap = this.rules.html.exec(src)) {
17039           src = src.substring(cap[0].length);
17040           this.tokens.push({
17041             type: this.options.sanitize
17042               ? 'paragraph'
17043               : 'html',
17044             pre: !this.options.sanitizer
17045               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17046             text: cap[0]
17047           });
17048           continue;
17049         }
17050     
17051         // def
17052         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17053           src = src.substring(cap[0].length);
17054           this.tokens.links[cap[1].toLowerCase()] = {
17055             href: cap[2],
17056             title: cap[3]
17057           };
17058           continue;
17059         }
17060     
17061         // table (gfm)
17062         if (top && (cap = this.rules.table.exec(src))) {
17063           src = src.substring(cap[0].length);
17064     
17065           item = {
17066             type: 'table',
17067             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17068             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17069             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17070           };
17071     
17072           for (i = 0; i < item.align.length; i++) {
17073             if (/^ *-+: *$/.test(item.align[i])) {
17074               item.align[i] = 'right';
17075             } else if (/^ *:-+: *$/.test(item.align[i])) {
17076               item.align[i] = 'center';
17077             } else if (/^ *:-+ *$/.test(item.align[i])) {
17078               item.align[i] = 'left';
17079             } else {
17080               item.align[i] = null;
17081             }
17082           }
17083     
17084           for (i = 0; i < item.cells.length; i++) {
17085             item.cells[i] = item.cells[i]
17086               .replace(/^ *\| *| *\| *$/g, '')
17087               .split(/ *\| */);
17088           }
17089     
17090           this.tokens.push(item);
17091     
17092           continue;
17093         }
17094     
17095         // top-level paragraph
17096         if (top && (cap = this.rules.paragraph.exec(src))) {
17097           src = src.substring(cap[0].length);
17098           this.tokens.push({
17099             type: 'paragraph',
17100             text: cap[1].charAt(cap[1].length - 1) === '\n'
17101               ? cap[1].slice(0, -1)
17102               : cap[1]
17103           });
17104           continue;
17105         }
17106     
17107         // text
17108         if (cap = this.rules.text.exec(src)) {
17109           // Top-level should never reach here.
17110           src = src.substring(cap[0].length);
17111           this.tokens.push({
17112             type: 'text',
17113             text: cap[0]
17114           });
17115           continue;
17116         }
17117     
17118         if (src) {
17119           throw new
17120             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17121         }
17122       }
17123     
17124       return this.tokens;
17125     };
17126     
17127     /**
17128      * Inline-Level Grammar
17129      */
17130     
17131     var inline = {
17132       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17133       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17134       url: noop,
17135       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17136       link: /^!?\[(inside)\]\(href\)/,
17137       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17138       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17139       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17140       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17141       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17142       br: /^ {2,}\n(?!\s*$)/,
17143       del: noop,
17144       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17145     };
17146     
17147     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17148     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17149     
17150     inline.link = replace(inline.link)
17151       ('inside', inline._inside)
17152       ('href', inline._href)
17153       ();
17154     
17155     inline.reflink = replace(inline.reflink)
17156       ('inside', inline._inside)
17157       ();
17158     
17159     /**
17160      * Normal Inline Grammar
17161      */
17162     
17163     inline.normal = merge({}, inline);
17164     
17165     /**
17166      * Pedantic Inline Grammar
17167      */
17168     
17169     inline.pedantic = merge({}, inline.normal, {
17170       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17171       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17172     });
17173     
17174     /**
17175      * GFM Inline Grammar
17176      */
17177     
17178     inline.gfm = merge({}, inline.normal, {
17179       escape: replace(inline.escape)('])', '~|])')(),
17180       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17181       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17182       text: replace(inline.text)
17183         (']|', '~]|')
17184         ('|', '|https?://|')
17185         ()
17186     });
17187     
17188     /**
17189      * GFM + Line Breaks Inline Grammar
17190      */
17191     
17192     inline.breaks = merge({}, inline.gfm, {
17193       br: replace(inline.br)('{2,}', '*')(),
17194       text: replace(inline.gfm.text)('{2,}', '*')()
17195     });
17196     
17197     /**
17198      * Inline Lexer & Compiler
17199      */
17200     
17201     function InlineLexer(links, options) {
17202       this.options = options || marked.defaults;
17203       this.links = links;
17204       this.rules = inline.normal;
17205       this.renderer = this.options.renderer || new Renderer;
17206       this.renderer.options = this.options;
17207     
17208       if (!this.links) {
17209         throw new
17210           Error('Tokens array requires a `links` property.');
17211       }
17212     
17213       if (this.options.gfm) {
17214         if (this.options.breaks) {
17215           this.rules = inline.breaks;
17216         } else {
17217           this.rules = inline.gfm;
17218         }
17219       } else if (this.options.pedantic) {
17220         this.rules = inline.pedantic;
17221       }
17222     }
17223     
17224     /**
17225      * Expose Inline Rules
17226      */
17227     
17228     InlineLexer.rules = inline;
17229     
17230     /**
17231      * Static Lexing/Compiling Method
17232      */
17233     
17234     InlineLexer.output = function(src, links, options) {
17235       var inline = new InlineLexer(links, options);
17236       return inline.output(src);
17237     };
17238     
17239     /**
17240      * Lexing/Compiling
17241      */
17242     
17243     InlineLexer.prototype.output = function(src) {
17244       var out = ''
17245         , link
17246         , text
17247         , href
17248         , cap;
17249     
17250       while (src) {
17251         // escape
17252         if (cap = this.rules.escape.exec(src)) {
17253           src = src.substring(cap[0].length);
17254           out += cap[1];
17255           continue;
17256         }
17257     
17258         // autolink
17259         if (cap = this.rules.autolink.exec(src)) {
17260           src = src.substring(cap[0].length);
17261           if (cap[2] === '@') {
17262             text = cap[1].charAt(6) === ':'
17263               ? this.mangle(cap[1].substring(7))
17264               : this.mangle(cap[1]);
17265             href = this.mangle('mailto:') + text;
17266           } else {
17267             text = escape(cap[1]);
17268             href = text;
17269           }
17270           out += this.renderer.link(href, null, text);
17271           continue;
17272         }
17273     
17274         // url (gfm)
17275         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17276           src = src.substring(cap[0].length);
17277           text = escape(cap[1]);
17278           href = text;
17279           out += this.renderer.link(href, null, text);
17280           continue;
17281         }
17282     
17283         // tag
17284         if (cap = this.rules.tag.exec(src)) {
17285           if (!this.inLink && /^<a /i.test(cap[0])) {
17286             this.inLink = true;
17287           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17288             this.inLink = false;
17289           }
17290           src = src.substring(cap[0].length);
17291           out += this.options.sanitize
17292             ? this.options.sanitizer
17293               ? this.options.sanitizer(cap[0])
17294               : escape(cap[0])
17295             : cap[0];
17296           continue;
17297         }
17298     
17299         // link
17300         if (cap = this.rules.link.exec(src)) {
17301           src = src.substring(cap[0].length);
17302           this.inLink = true;
17303           out += this.outputLink(cap, {
17304             href: cap[2],
17305             title: cap[3]
17306           });
17307           this.inLink = false;
17308           continue;
17309         }
17310     
17311         // reflink, nolink
17312         if ((cap = this.rules.reflink.exec(src))
17313             || (cap = this.rules.nolink.exec(src))) {
17314           src = src.substring(cap[0].length);
17315           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17316           link = this.links[link.toLowerCase()];
17317           if (!link || !link.href) {
17318             out += cap[0].charAt(0);
17319             src = cap[0].substring(1) + src;
17320             continue;
17321           }
17322           this.inLink = true;
17323           out += this.outputLink(cap, link);
17324           this.inLink = false;
17325           continue;
17326         }
17327     
17328         // strong
17329         if (cap = this.rules.strong.exec(src)) {
17330           src = src.substring(cap[0].length);
17331           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17332           continue;
17333         }
17334     
17335         // em
17336         if (cap = this.rules.em.exec(src)) {
17337           src = src.substring(cap[0].length);
17338           out += this.renderer.em(this.output(cap[2] || cap[1]));
17339           continue;
17340         }
17341     
17342         // code
17343         if (cap = this.rules.code.exec(src)) {
17344           src = src.substring(cap[0].length);
17345           out += this.renderer.codespan(escape(cap[2], true));
17346           continue;
17347         }
17348     
17349         // br
17350         if (cap = this.rules.br.exec(src)) {
17351           src = src.substring(cap[0].length);
17352           out += this.renderer.br();
17353           continue;
17354         }
17355     
17356         // del (gfm)
17357         if (cap = this.rules.del.exec(src)) {
17358           src = src.substring(cap[0].length);
17359           out += this.renderer.del(this.output(cap[1]));
17360           continue;
17361         }
17362     
17363         // text
17364         if (cap = this.rules.text.exec(src)) {
17365           src = src.substring(cap[0].length);
17366           out += this.renderer.text(escape(this.smartypants(cap[0])));
17367           continue;
17368         }
17369     
17370         if (src) {
17371           throw new
17372             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17373         }
17374       }
17375     
17376       return out;
17377     };
17378     
17379     /**
17380      * Compile Link
17381      */
17382     
17383     InlineLexer.prototype.outputLink = function(cap, link) {
17384       var href = escape(link.href)
17385         , title = link.title ? escape(link.title) : null;
17386     
17387       return cap[0].charAt(0) !== '!'
17388         ? this.renderer.link(href, title, this.output(cap[1]))
17389         : this.renderer.image(href, title, escape(cap[1]));
17390     };
17391     
17392     /**
17393      * Smartypants Transformations
17394      */
17395     
17396     InlineLexer.prototype.smartypants = function(text) {
17397       if (!this.options.smartypants)  { return text; }
17398       return text
17399         // em-dashes
17400         .replace(/---/g, '\u2014')
17401         // en-dashes
17402         .replace(/--/g, '\u2013')
17403         // opening singles
17404         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17405         // closing singles & apostrophes
17406         .replace(/'/g, '\u2019')
17407         // opening doubles
17408         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17409         // closing doubles
17410         .replace(/"/g, '\u201d')
17411         // ellipses
17412         .replace(/\.{3}/g, '\u2026');
17413     };
17414     
17415     /**
17416      * Mangle Links
17417      */
17418     
17419     InlineLexer.prototype.mangle = function(text) {
17420       if (!this.options.mangle) { return text; }
17421       var out = ''
17422         , l = text.length
17423         , i = 0
17424         , ch;
17425     
17426       for (; i < l; i++) {
17427         ch = text.charCodeAt(i);
17428         if (Math.random() > 0.5) {
17429           ch = 'x' + ch.toString(16);
17430         }
17431         out += '&#' + ch + ';';
17432       }
17433     
17434       return out;
17435     };
17436     
17437     /**
17438      * Renderer
17439      */
17440     
17441     function Renderer(options) {
17442       this.options = options || {};
17443     }
17444     
17445     Renderer.prototype.code = function(code, lang, escaped) {
17446       if (this.options.highlight) {
17447         var out = this.options.highlight(code, lang);
17448         if (out != null && out !== code) {
17449           escaped = true;
17450           code = out;
17451         }
17452       } else {
17453             // hack!!! - it's already escapeD?
17454             escaped = true;
17455       }
17456     
17457       if (!lang) {
17458         return '<pre><code>'
17459           + (escaped ? code : escape(code, true))
17460           + '\n</code></pre>';
17461       }
17462     
17463       return '<pre><code class="'
17464         + this.options.langPrefix
17465         + escape(lang, true)
17466         + '">'
17467         + (escaped ? code : escape(code, true))
17468         + '\n</code></pre>\n';
17469     };
17470     
17471     Renderer.prototype.blockquote = function(quote) {
17472       return '<blockquote>\n' + quote + '</blockquote>\n';
17473     };
17474     
17475     Renderer.prototype.html = function(html) {
17476       return html;
17477     };
17478     
17479     Renderer.prototype.heading = function(text, level, raw) {
17480       return '<h'
17481         + level
17482         + ' id="'
17483         + this.options.headerPrefix
17484         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17485         + '">'
17486         + text
17487         + '</h'
17488         + level
17489         + '>\n';
17490     };
17491     
17492     Renderer.prototype.hr = function() {
17493       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17494     };
17495     
17496     Renderer.prototype.list = function(body, ordered) {
17497       var type = ordered ? 'ol' : 'ul';
17498       return '<' + type + '>\n' + body + '</' + type + '>\n';
17499     };
17500     
17501     Renderer.prototype.listitem = function(text) {
17502       return '<li>' + text + '</li>\n';
17503     };
17504     
17505     Renderer.prototype.paragraph = function(text) {
17506       return '<p>' + text + '</p>\n';
17507     };
17508     
17509     Renderer.prototype.table = function(header, body) {
17510       return '<table class="table table-striped">\n'
17511         + '<thead>\n'
17512         + header
17513         + '</thead>\n'
17514         + '<tbody>\n'
17515         + body
17516         + '</tbody>\n'
17517         + '</table>\n';
17518     };
17519     
17520     Renderer.prototype.tablerow = function(content) {
17521       return '<tr>\n' + content + '</tr>\n';
17522     };
17523     
17524     Renderer.prototype.tablecell = function(content, flags) {
17525       var type = flags.header ? 'th' : 'td';
17526       var tag = flags.align
17527         ? '<' + type + ' style="text-align:' + flags.align + '">'
17528         : '<' + type + '>';
17529       return tag + content + '</' + type + '>\n';
17530     };
17531     
17532     // span level renderer
17533     Renderer.prototype.strong = function(text) {
17534       return '<strong>' + text + '</strong>';
17535     };
17536     
17537     Renderer.prototype.em = function(text) {
17538       return '<em>' + text + '</em>';
17539     };
17540     
17541     Renderer.prototype.codespan = function(text) {
17542       return '<code>' + text + '</code>';
17543     };
17544     
17545     Renderer.prototype.br = function() {
17546       return this.options.xhtml ? '<br/>' : '<br>';
17547     };
17548     
17549     Renderer.prototype.del = function(text) {
17550       return '<del>' + text + '</del>';
17551     };
17552     
17553     Renderer.prototype.link = function(href, title, text) {
17554       if (this.options.sanitize) {
17555         try {
17556           var prot = decodeURIComponent(unescape(href))
17557             .replace(/[^\w:]/g, '')
17558             .toLowerCase();
17559         } catch (e) {
17560           return '';
17561         }
17562         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17563           return '';
17564         }
17565       }
17566       var out = '<a href="' + href + '"';
17567       if (title) {
17568         out += ' title="' + title + '"';
17569       }
17570       out += '>' + text + '</a>';
17571       return out;
17572     };
17573     
17574     Renderer.prototype.image = function(href, title, text) {
17575       var out = '<img src="' + href + '" alt="' + text + '"';
17576       if (title) {
17577         out += ' title="' + title + '"';
17578       }
17579       out += this.options.xhtml ? '/>' : '>';
17580       return out;
17581     };
17582     
17583     Renderer.prototype.text = function(text) {
17584       return text;
17585     };
17586     
17587     /**
17588      * Parsing & Compiling
17589      */
17590     
17591     function Parser(options) {
17592       this.tokens = [];
17593       this.token = null;
17594       this.options = options || marked.defaults;
17595       this.options.renderer = this.options.renderer || new Renderer;
17596       this.renderer = this.options.renderer;
17597       this.renderer.options = this.options;
17598     }
17599     
17600     /**
17601      * Static Parse Method
17602      */
17603     
17604     Parser.parse = function(src, options, renderer) {
17605       var parser = new Parser(options, renderer);
17606       return parser.parse(src);
17607     };
17608     
17609     /**
17610      * Parse Loop
17611      */
17612     
17613     Parser.prototype.parse = function(src) {
17614       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17615       this.tokens = src.reverse();
17616     
17617       var out = '';
17618       while (this.next()) {
17619         out += this.tok();
17620       }
17621     
17622       return out;
17623     };
17624     
17625     /**
17626      * Next Token
17627      */
17628     
17629     Parser.prototype.next = function() {
17630       return this.token = this.tokens.pop();
17631     };
17632     
17633     /**
17634      * Preview Next Token
17635      */
17636     
17637     Parser.prototype.peek = function() {
17638       return this.tokens[this.tokens.length - 1] || 0;
17639     };
17640     
17641     /**
17642      * Parse Text Tokens
17643      */
17644     
17645     Parser.prototype.parseText = function() {
17646       var body = this.token.text;
17647     
17648       while (this.peek().type === 'text') {
17649         body += '\n' + this.next().text;
17650       }
17651     
17652       return this.inline.output(body);
17653     };
17654     
17655     /**
17656      * Parse Current Token
17657      */
17658     
17659     Parser.prototype.tok = function() {
17660       switch (this.token.type) {
17661         case 'space': {
17662           return '';
17663         }
17664         case 'hr': {
17665           return this.renderer.hr();
17666         }
17667         case 'heading': {
17668           return this.renderer.heading(
17669             this.inline.output(this.token.text),
17670             this.token.depth,
17671             this.token.text);
17672         }
17673         case 'code': {
17674           return this.renderer.code(this.token.text,
17675             this.token.lang,
17676             this.token.escaped);
17677         }
17678         case 'table': {
17679           var header = ''
17680             , body = ''
17681             , i
17682             , row
17683             , cell
17684             , flags
17685             , j;
17686     
17687           // header
17688           cell = '';
17689           for (i = 0; i < this.token.header.length; i++) {
17690             flags = { header: true, align: this.token.align[i] };
17691             cell += this.renderer.tablecell(
17692               this.inline.output(this.token.header[i]),
17693               { header: true, align: this.token.align[i] }
17694             );
17695           }
17696           header += this.renderer.tablerow(cell);
17697     
17698           for (i = 0; i < this.token.cells.length; i++) {
17699             row = this.token.cells[i];
17700     
17701             cell = '';
17702             for (j = 0; j < row.length; j++) {
17703               cell += this.renderer.tablecell(
17704                 this.inline.output(row[j]),
17705                 { header: false, align: this.token.align[j] }
17706               );
17707             }
17708     
17709             body += this.renderer.tablerow(cell);
17710           }
17711           return this.renderer.table(header, body);
17712         }
17713         case 'blockquote_start': {
17714           var body = '';
17715     
17716           while (this.next().type !== 'blockquote_end') {
17717             body += this.tok();
17718           }
17719     
17720           return this.renderer.blockquote(body);
17721         }
17722         case 'list_start': {
17723           var body = ''
17724             , ordered = this.token.ordered;
17725     
17726           while (this.next().type !== 'list_end') {
17727             body += this.tok();
17728           }
17729     
17730           return this.renderer.list(body, ordered);
17731         }
17732         case 'list_item_start': {
17733           var body = '';
17734     
17735           while (this.next().type !== 'list_item_end') {
17736             body += this.token.type === 'text'
17737               ? this.parseText()
17738               : this.tok();
17739           }
17740     
17741           return this.renderer.listitem(body);
17742         }
17743         case 'loose_item_start': {
17744           var body = '';
17745     
17746           while (this.next().type !== 'list_item_end') {
17747             body += this.tok();
17748           }
17749     
17750           return this.renderer.listitem(body);
17751         }
17752         case 'html': {
17753           var html = !this.token.pre && !this.options.pedantic
17754             ? this.inline.output(this.token.text)
17755             : this.token.text;
17756           return this.renderer.html(html);
17757         }
17758         case 'paragraph': {
17759           return this.renderer.paragraph(this.inline.output(this.token.text));
17760         }
17761         case 'text': {
17762           return this.renderer.paragraph(this.parseText());
17763         }
17764       }
17765     };
17766     
17767     /**
17768      * Helpers
17769      */
17770     
17771     function escape(html, encode) {
17772       return html
17773         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17774         .replace(/</g, '&lt;')
17775         .replace(/>/g, '&gt;')
17776         .replace(/"/g, '&quot;')
17777         .replace(/'/g, '&#39;');
17778     }
17779     
17780     function unescape(html) {
17781         // explicitly match decimal, hex, and named HTML entities 
17782       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17783         n = n.toLowerCase();
17784         if (n === 'colon') { return ':'; }
17785         if (n.charAt(0) === '#') {
17786           return n.charAt(1) === 'x'
17787             ? String.fromCharCode(parseInt(n.substring(2), 16))
17788             : String.fromCharCode(+n.substring(1));
17789         }
17790         return '';
17791       });
17792     }
17793     
17794     function replace(regex, opt) {
17795       regex = regex.source;
17796       opt = opt || '';
17797       return function self(name, val) {
17798         if (!name) { return new RegExp(regex, opt); }
17799         val = val.source || val;
17800         val = val.replace(/(^|[^\[])\^/g, '$1');
17801         regex = regex.replace(name, val);
17802         return self;
17803       };
17804     }
17805     
17806     function noop() {}
17807     noop.exec = noop;
17808     
17809     function merge(obj) {
17810       var i = 1
17811         , target
17812         , key;
17813     
17814       for (; i < arguments.length; i++) {
17815         target = arguments[i];
17816         for (key in target) {
17817           if (Object.prototype.hasOwnProperty.call(target, key)) {
17818             obj[key] = target[key];
17819           }
17820         }
17821       }
17822     
17823       return obj;
17824     }
17825     
17826     
17827     /**
17828      * Marked
17829      */
17830     
17831     function marked(src, opt, callback) {
17832       if (callback || typeof opt === 'function') {
17833         if (!callback) {
17834           callback = opt;
17835           opt = null;
17836         }
17837     
17838         opt = merge({}, marked.defaults, opt || {});
17839     
17840         var highlight = opt.highlight
17841           , tokens
17842           , pending
17843           , i = 0;
17844     
17845         try {
17846           tokens = Lexer.lex(src, opt)
17847         } catch (e) {
17848           return callback(e);
17849         }
17850     
17851         pending = tokens.length;
17852     
17853         var done = function(err) {
17854           if (err) {
17855             opt.highlight = highlight;
17856             return callback(err);
17857           }
17858     
17859           var out;
17860     
17861           try {
17862             out = Parser.parse(tokens, opt);
17863           } catch (e) {
17864             err = e;
17865           }
17866     
17867           opt.highlight = highlight;
17868     
17869           return err
17870             ? callback(err)
17871             : callback(null, out);
17872         };
17873     
17874         if (!highlight || highlight.length < 3) {
17875           return done();
17876         }
17877     
17878         delete opt.highlight;
17879     
17880         if (!pending) { return done(); }
17881     
17882         for (; i < tokens.length; i++) {
17883           (function(token) {
17884             if (token.type !== 'code') {
17885               return --pending || done();
17886             }
17887             return highlight(token.text, token.lang, function(err, code) {
17888               if (err) { return done(err); }
17889               if (code == null || code === token.text) {
17890                 return --pending || done();
17891               }
17892               token.text = code;
17893               token.escaped = true;
17894               --pending || done();
17895             });
17896           })(tokens[i]);
17897         }
17898     
17899         return;
17900       }
17901       try {
17902         if (opt) { opt = merge({}, marked.defaults, opt); }
17903         return Parser.parse(Lexer.lex(src, opt), opt);
17904       } catch (e) {
17905         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17906         if ((opt || marked.defaults).silent) {
17907           return '<p>An error occured:</p><pre>'
17908             + escape(e.message + '', true)
17909             + '</pre>';
17910         }
17911         throw e;
17912       }
17913     }
17914     
17915     /**
17916      * Options
17917      */
17918     
17919     marked.options =
17920     marked.setOptions = function(opt) {
17921       merge(marked.defaults, opt);
17922       return marked;
17923     };
17924     
17925     marked.defaults = {
17926       gfm: true,
17927       tables: true,
17928       breaks: false,
17929       pedantic: false,
17930       sanitize: false,
17931       sanitizer: null,
17932       mangle: true,
17933       smartLists: false,
17934       silent: false,
17935       highlight: null,
17936       langPrefix: 'lang-',
17937       smartypants: false,
17938       headerPrefix: '',
17939       renderer: new Renderer,
17940       xhtml: false
17941     };
17942     
17943     /**
17944      * Expose
17945      */
17946     
17947     marked.Parser = Parser;
17948     marked.parser = Parser.parse;
17949     
17950     marked.Renderer = Renderer;
17951     
17952     marked.Lexer = Lexer;
17953     marked.lexer = Lexer.lex;
17954     
17955     marked.InlineLexer = InlineLexer;
17956     marked.inlineLexer = InlineLexer.output;
17957     
17958     marked.parse = marked;
17959     
17960     Roo.Markdown.marked = marked;
17961
17962 })();/*
17963  * Based on:
17964  * Ext JS Library 1.1.1
17965  * Copyright(c) 2006-2007, Ext JS, LLC.
17966  *
17967  * Originally Released Under LGPL - original licence link has changed is not relivant.
17968  *
17969  * Fork - LGPL
17970  * <script type="text/javascript">
17971  */
17972
17973
17974
17975 /*
17976  * These classes are derivatives of the similarly named classes in the YUI Library.
17977  * The original license:
17978  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17979  * Code licensed under the BSD License:
17980  * http://developer.yahoo.net/yui/license.txt
17981  */
17982
17983 (function() {
17984
17985 var Event=Roo.EventManager;
17986 var Dom=Roo.lib.Dom;
17987
17988 /**
17989  * @class Roo.dd.DragDrop
17990  * @extends Roo.util.Observable
17991  * Defines the interface and base operation of items that that can be
17992  * dragged or can be drop targets.  It was designed to be extended, overriding
17993  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17994  * Up to three html elements can be associated with a DragDrop instance:
17995  * <ul>
17996  * <li>linked element: the element that is passed into the constructor.
17997  * This is the element which defines the boundaries for interaction with
17998  * other DragDrop objects.</li>
17999  * <li>handle element(s): The drag operation only occurs if the element that
18000  * was clicked matches a handle element.  By default this is the linked
18001  * element, but there are times that you will want only a portion of the
18002  * linked element to initiate the drag operation, and the setHandleElId()
18003  * method provides a way to define this.</li>
18004  * <li>drag element: this represents the element that would be moved along
18005  * with the cursor during a drag operation.  By default, this is the linked
18006  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18007  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18008  * </li>
18009  * </ul>
18010  * This class should not be instantiated until the onload event to ensure that
18011  * the associated elements are available.
18012  * The following would define a DragDrop obj that would interact with any
18013  * other DragDrop obj in the "group1" group:
18014  * <pre>
18015  *  dd = new Roo.dd.DragDrop("div1", "group1");
18016  * </pre>
18017  * Since none of the event handlers have been implemented, nothing would
18018  * actually happen if you were to run the code above.  Normally you would
18019  * override this class or one of the default implementations, but you can
18020  * also override the methods you want on an instance of the class...
18021  * <pre>
18022  *  dd.onDragDrop = function(e, id) {
18023  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18024  *  }
18025  * </pre>
18026  * @constructor
18027  * @param {String} id of the element that is linked to this instance
18028  * @param {String} sGroup the group of related DragDrop objects
18029  * @param {object} config an object containing configurable attributes
18030  *                Valid properties for DragDrop:
18031  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18032  */
18033 Roo.dd.DragDrop = function(id, sGroup, config) {
18034     if (id) {
18035         this.init(id, sGroup, config);
18036     }
18037     
18038 };
18039
18040 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18041
18042     /**
18043      * The id of the element associated with this object.  This is what we
18044      * refer to as the "linked element" because the size and position of
18045      * this element is used to determine when the drag and drop objects have
18046      * interacted.
18047      * @property id
18048      * @type String
18049      */
18050     id: null,
18051
18052     /**
18053      * Configuration attributes passed into the constructor
18054      * @property config
18055      * @type object
18056      */
18057     config: null,
18058
18059     /**
18060      * The id of the element that will be dragged.  By default this is same
18061      * as the linked element , but could be changed to another element. Ex:
18062      * Roo.dd.DDProxy
18063      * @property dragElId
18064      * @type String
18065      * @private
18066      */
18067     dragElId: null,
18068
18069     /**
18070      * the id of the element that initiates the drag operation.  By default
18071      * this is the linked element, but could be changed to be a child of this
18072      * element.  This lets us do things like only starting the drag when the
18073      * header element within the linked html element is clicked.
18074      * @property handleElId
18075      * @type String
18076      * @private
18077      */
18078     handleElId: null,
18079
18080     /**
18081      * An associative array of HTML tags that will be ignored if clicked.
18082      * @property invalidHandleTypes
18083      * @type {string: string}
18084      */
18085     invalidHandleTypes: null,
18086
18087     /**
18088      * An associative array of ids for elements that will be ignored if clicked
18089      * @property invalidHandleIds
18090      * @type {string: string}
18091      */
18092     invalidHandleIds: null,
18093
18094     /**
18095      * An indexted array of css class names for elements that will be ignored
18096      * if clicked.
18097      * @property invalidHandleClasses
18098      * @type string[]
18099      */
18100     invalidHandleClasses: null,
18101
18102     /**
18103      * The linked element's absolute X position at the time the drag was
18104      * started
18105      * @property startPageX
18106      * @type int
18107      * @private
18108      */
18109     startPageX: 0,
18110
18111     /**
18112      * The linked element's absolute X position at the time the drag was
18113      * started
18114      * @property startPageY
18115      * @type int
18116      * @private
18117      */
18118     startPageY: 0,
18119
18120     /**
18121      * The group defines a logical collection of DragDrop objects that are
18122      * related.  Instances only get events when interacting with other
18123      * DragDrop object in the same group.  This lets us define multiple
18124      * groups using a single DragDrop subclass if we want.
18125      * @property groups
18126      * @type {string: string}
18127      */
18128     groups: null,
18129
18130     /**
18131      * Individual drag/drop instances can be locked.  This will prevent
18132      * onmousedown start drag.
18133      * @property locked
18134      * @type boolean
18135      * @private
18136      */
18137     locked: false,
18138
18139     /**
18140      * Lock this instance
18141      * @method lock
18142      */
18143     lock: function() { this.locked = true; },
18144
18145     /**
18146      * Unlock this instace
18147      * @method unlock
18148      */
18149     unlock: function() { this.locked = false; },
18150
18151     /**
18152      * By default, all insances can be a drop target.  This can be disabled by
18153      * setting isTarget to false.
18154      * @method isTarget
18155      * @type boolean
18156      */
18157     isTarget: true,
18158
18159     /**
18160      * The padding configured for this drag and drop object for calculating
18161      * the drop zone intersection with this object.
18162      * @method padding
18163      * @type int[]
18164      */
18165     padding: null,
18166
18167     /**
18168      * Cached reference to the linked element
18169      * @property _domRef
18170      * @private
18171      */
18172     _domRef: null,
18173
18174     /**
18175      * Internal typeof flag
18176      * @property __ygDragDrop
18177      * @private
18178      */
18179     __ygDragDrop: true,
18180
18181     /**
18182      * Set to true when horizontal contraints are applied
18183      * @property constrainX
18184      * @type boolean
18185      * @private
18186      */
18187     constrainX: false,
18188
18189     /**
18190      * Set to true when vertical contraints are applied
18191      * @property constrainY
18192      * @type boolean
18193      * @private
18194      */
18195     constrainY: false,
18196
18197     /**
18198      * The left constraint
18199      * @property minX
18200      * @type int
18201      * @private
18202      */
18203     minX: 0,
18204
18205     /**
18206      * The right constraint
18207      * @property maxX
18208      * @type int
18209      * @private
18210      */
18211     maxX: 0,
18212
18213     /**
18214      * The up constraint
18215      * @property minY
18216      * @type int
18217      * @type int
18218      * @private
18219      */
18220     minY: 0,
18221
18222     /**
18223      * The down constraint
18224      * @property maxY
18225      * @type int
18226      * @private
18227      */
18228     maxY: 0,
18229
18230     /**
18231      * Maintain offsets when we resetconstraints.  Set to true when you want
18232      * the position of the element relative to its parent to stay the same
18233      * when the page changes
18234      *
18235      * @property maintainOffset
18236      * @type boolean
18237      */
18238     maintainOffset: false,
18239
18240     /**
18241      * Array of pixel locations the element will snap to if we specified a
18242      * horizontal graduation/interval.  This array is generated automatically
18243      * when you define a tick interval.
18244      * @property xTicks
18245      * @type int[]
18246      */
18247     xTicks: null,
18248
18249     /**
18250      * Array of pixel locations the element will snap to if we specified a
18251      * vertical graduation/interval.  This array is generated automatically
18252      * when you define a tick interval.
18253      * @property yTicks
18254      * @type int[]
18255      */
18256     yTicks: null,
18257
18258     /**
18259      * By default the drag and drop instance will only respond to the primary
18260      * button click (left button for a right-handed mouse).  Set to true to
18261      * allow drag and drop to start with any mouse click that is propogated
18262      * by the browser
18263      * @property primaryButtonOnly
18264      * @type boolean
18265      */
18266     primaryButtonOnly: true,
18267
18268     /**
18269      * The availabe property is false until the linked dom element is accessible.
18270      * @property available
18271      * @type boolean
18272      */
18273     available: false,
18274
18275     /**
18276      * By default, drags can only be initiated if the mousedown occurs in the
18277      * region the linked element is.  This is done in part to work around a
18278      * bug in some browsers that mis-report the mousedown if the previous
18279      * mouseup happened outside of the window.  This property is set to true
18280      * if outer handles are defined.
18281      *
18282      * @property hasOuterHandles
18283      * @type boolean
18284      * @default false
18285      */
18286     hasOuterHandles: false,
18287
18288     /**
18289      * Code that executes immediately before the startDrag event
18290      * @method b4StartDrag
18291      * @private
18292      */
18293     b4StartDrag: function(x, y) { },
18294
18295     /**
18296      * Abstract method called after a drag/drop object is clicked
18297      * and the drag or mousedown time thresholds have beeen met.
18298      * @method startDrag
18299      * @param {int} X click location
18300      * @param {int} Y click location
18301      */
18302     startDrag: function(x, y) { /* override this */ },
18303
18304     /**
18305      * Code that executes immediately before the onDrag event
18306      * @method b4Drag
18307      * @private
18308      */
18309     b4Drag: function(e) { },
18310
18311     /**
18312      * Abstract method called during the onMouseMove event while dragging an
18313      * object.
18314      * @method onDrag
18315      * @param {Event} e the mousemove event
18316      */
18317     onDrag: function(e) { /* override this */ },
18318
18319     /**
18320      * Abstract method called when this element fist begins hovering over
18321      * another DragDrop obj
18322      * @method onDragEnter
18323      * @param {Event} e the mousemove event
18324      * @param {String|DragDrop[]} id In POINT mode, the element
18325      * id this is hovering over.  In INTERSECT mode, an array of one or more
18326      * dragdrop items being hovered over.
18327      */
18328     onDragEnter: function(e, id) { /* override this */ },
18329
18330     /**
18331      * Code that executes immediately before the onDragOver event
18332      * @method b4DragOver
18333      * @private
18334      */
18335     b4DragOver: function(e) { },
18336
18337     /**
18338      * Abstract method called when this element is hovering over another
18339      * DragDrop obj
18340      * @method onDragOver
18341      * @param {Event} e the mousemove event
18342      * @param {String|DragDrop[]} id In POINT mode, the element
18343      * id this is hovering over.  In INTERSECT mode, an array of dd items
18344      * being hovered over.
18345      */
18346     onDragOver: function(e, id) { /* override this */ },
18347
18348     /**
18349      * Code that executes immediately before the onDragOut event
18350      * @method b4DragOut
18351      * @private
18352      */
18353     b4DragOut: function(e) { },
18354
18355     /**
18356      * Abstract method called when we are no longer hovering over an element
18357      * @method onDragOut
18358      * @param {Event} e the mousemove event
18359      * @param {String|DragDrop[]} id In POINT mode, the element
18360      * id this was hovering over.  In INTERSECT mode, an array of dd items
18361      * that the mouse is no longer over.
18362      */
18363     onDragOut: function(e, id) { /* override this */ },
18364
18365     /**
18366      * Code that executes immediately before the onDragDrop event
18367      * @method b4DragDrop
18368      * @private
18369      */
18370     b4DragDrop: function(e) { },
18371
18372     /**
18373      * Abstract method called when this item is dropped on another DragDrop
18374      * obj
18375      * @method onDragDrop
18376      * @param {Event} e the mouseup event
18377      * @param {String|DragDrop[]} id In POINT mode, the element
18378      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18379      * was dropped on.
18380      */
18381     onDragDrop: function(e, id) { /* override this */ },
18382
18383     /**
18384      * Abstract method called when this item is dropped on an area with no
18385      * drop target
18386      * @method onInvalidDrop
18387      * @param {Event} e the mouseup event
18388      */
18389     onInvalidDrop: function(e) { /* override this */ },
18390
18391     /**
18392      * Code that executes immediately before the endDrag event
18393      * @method b4EndDrag
18394      * @private
18395      */
18396     b4EndDrag: function(e) { },
18397
18398     /**
18399      * Fired when we are done dragging the object
18400      * @method endDrag
18401      * @param {Event} e the mouseup event
18402      */
18403     endDrag: function(e) { /* override this */ },
18404
18405     /**
18406      * Code executed immediately before the onMouseDown event
18407      * @method b4MouseDown
18408      * @param {Event} e the mousedown event
18409      * @private
18410      */
18411     b4MouseDown: function(e) {  },
18412
18413     /**
18414      * Event handler that fires when a drag/drop obj gets a mousedown
18415      * @method onMouseDown
18416      * @param {Event} e the mousedown event
18417      */
18418     onMouseDown: function(e) { /* override this */ },
18419
18420     /**
18421      * Event handler that fires when a drag/drop obj gets a mouseup
18422      * @method onMouseUp
18423      * @param {Event} e the mouseup event
18424      */
18425     onMouseUp: function(e) { /* override this */ },
18426
18427     /**
18428      * Override the onAvailable method to do what is needed after the initial
18429      * position was determined.
18430      * @method onAvailable
18431      */
18432     onAvailable: function () {
18433     },
18434
18435     /*
18436      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18437      * @type Object
18438      */
18439     defaultPadding : {left:0, right:0, top:0, bottom:0},
18440
18441     /*
18442      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18443  *
18444  * Usage:
18445  <pre><code>
18446  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18447                 { dragElId: "existingProxyDiv" });
18448  dd.startDrag = function(){
18449      this.constrainTo("parent-id");
18450  };
18451  </code></pre>
18452  * Or you can initalize it using the {@link Roo.Element} object:
18453  <pre><code>
18454  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18455      startDrag : function(){
18456          this.constrainTo("parent-id");
18457      }
18458  });
18459  </code></pre>
18460      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18461      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18462      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18463      * an object containing the sides to pad. For example: {right:10, bottom:10}
18464      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18465      */
18466     constrainTo : function(constrainTo, pad, inContent){
18467         if(typeof pad == "number"){
18468             pad = {left: pad, right:pad, top:pad, bottom:pad};
18469         }
18470         pad = pad || this.defaultPadding;
18471         var b = Roo.get(this.getEl()).getBox();
18472         var ce = Roo.get(constrainTo);
18473         var s = ce.getScroll();
18474         var c, cd = ce.dom;
18475         if(cd == document.body){
18476             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18477         }else{
18478             xy = ce.getXY();
18479             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18480         }
18481
18482
18483         var topSpace = b.y - c.y;
18484         var leftSpace = b.x - c.x;
18485
18486         this.resetConstraints();
18487         this.setXConstraint(leftSpace - (pad.left||0), // left
18488                 c.width - leftSpace - b.width - (pad.right||0) //right
18489         );
18490         this.setYConstraint(topSpace - (pad.top||0), //top
18491                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18492         );
18493     },
18494
18495     /**
18496      * Returns a reference to the linked element
18497      * @method getEl
18498      * @return {HTMLElement} the html element
18499      */
18500     getEl: function() {
18501         if (!this._domRef) {
18502             this._domRef = Roo.getDom(this.id);
18503         }
18504
18505         return this._domRef;
18506     },
18507
18508     /**
18509      * Returns a reference to the actual element to drag.  By default this is
18510      * the same as the html element, but it can be assigned to another
18511      * element. An example of this can be found in Roo.dd.DDProxy
18512      * @method getDragEl
18513      * @return {HTMLElement} the html element
18514      */
18515     getDragEl: function() {
18516         return Roo.getDom(this.dragElId);
18517     },
18518
18519     /**
18520      * Sets up the DragDrop object.  Must be called in the constructor of any
18521      * Roo.dd.DragDrop subclass
18522      * @method init
18523      * @param id the id of the linked element
18524      * @param {String} sGroup the group of related items
18525      * @param {object} config configuration attributes
18526      */
18527     init: function(id, sGroup, config) {
18528         this.initTarget(id, sGroup, config);
18529         if (!Roo.isTouch) {
18530             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18531         }
18532         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18533         // Event.on(this.id, "selectstart", Event.preventDefault);
18534     },
18535
18536     /**
18537      * Initializes Targeting functionality only... the object does not
18538      * get a mousedown handler.
18539      * @method initTarget
18540      * @param id the id of the linked element
18541      * @param {String} sGroup the group of related items
18542      * @param {object} config configuration attributes
18543      */
18544     initTarget: function(id, sGroup, config) {
18545
18546         // configuration attributes
18547         this.config = config || {};
18548
18549         // create a local reference to the drag and drop manager
18550         this.DDM = Roo.dd.DDM;
18551         // initialize the groups array
18552         this.groups = {};
18553
18554         // assume that we have an element reference instead of an id if the
18555         // parameter is not a string
18556         if (typeof id !== "string") {
18557             id = Roo.id(id);
18558         }
18559
18560         // set the id
18561         this.id = id;
18562
18563         // add to an interaction group
18564         this.addToGroup((sGroup) ? sGroup : "default");
18565
18566         // We don't want to register this as the handle with the manager
18567         // so we just set the id rather than calling the setter.
18568         this.handleElId = id;
18569
18570         // the linked element is the element that gets dragged by default
18571         this.setDragElId(id);
18572
18573         // by default, clicked anchors will not start drag operations.
18574         this.invalidHandleTypes = { A: "A" };
18575         this.invalidHandleIds = {};
18576         this.invalidHandleClasses = [];
18577
18578         this.applyConfig();
18579
18580         this.handleOnAvailable();
18581     },
18582
18583     /**
18584      * Applies the configuration parameters that were passed into the constructor.
18585      * This is supposed to happen at each level through the inheritance chain.  So
18586      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18587      * DragDrop in order to get all of the parameters that are available in
18588      * each object.
18589      * @method applyConfig
18590      */
18591     applyConfig: function() {
18592
18593         // configurable properties:
18594         //    padding, isTarget, maintainOffset, primaryButtonOnly
18595         this.padding           = this.config.padding || [0, 0, 0, 0];
18596         this.isTarget          = (this.config.isTarget !== false);
18597         this.maintainOffset    = (this.config.maintainOffset);
18598         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18599
18600     },
18601
18602     /**
18603      * Executed when the linked element is available
18604      * @method handleOnAvailable
18605      * @private
18606      */
18607     handleOnAvailable: function() {
18608         this.available = true;
18609         this.resetConstraints();
18610         this.onAvailable();
18611     },
18612
18613      /**
18614      * Configures the padding for the target zone in px.  Effectively expands
18615      * (or reduces) the virtual object size for targeting calculations.
18616      * Supports css-style shorthand; if only one parameter is passed, all sides
18617      * will have that padding, and if only two are passed, the top and bottom
18618      * will have the first param, the left and right the second.
18619      * @method setPadding
18620      * @param {int} iTop    Top pad
18621      * @param {int} iRight  Right pad
18622      * @param {int} iBot    Bot pad
18623      * @param {int} iLeft   Left pad
18624      */
18625     setPadding: function(iTop, iRight, iBot, iLeft) {
18626         // this.padding = [iLeft, iRight, iTop, iBot];
18627         if (!iRight && 0 !== iRight) {
18628             this.padding = [iTop, iTop, iTop, iTop];
18629         } else if (!iBot && 0 !== iBot) {
18630             this.padding = [iTop, iRight, iTop, iRight];
18631         } else {
18632             this.padding = [iTop, iRight, iBot, iLeft];
18633         }
18634     },
18635
18636     /**
18637      * Stores the initial placement of the linked element.
18638      * @method setInitialPosition
18639      * @param {int} diffX   the X offset, default 0
18640      * @param {int} diffY   the Y offset, default 0
18641      */
18642     setInitPosition: function(diffX, diffY) {
18643         var el = this.getEl();
18644
18645         if (!this.DDM.verifyEl(el)) {
18646             return;
18647         }
18648
18649         var dx = diffX || 0;
18650         var dy = diffY || 0;
18651
18652         var p = Dom.getXY( el );
18653
18654         this.initPageX = p[0] - dx;
18655         this.initPageY = p[1] - dy;
18656
18657         this.lastPageX = p[0];
18658         this.lastPageY = p[1];
18659
18660
18661         this.setStartPosition(p);
18662     },
18663
18664     /**
18665      * Sets the start position of the element.  This is set when the obj
18666      * is initialized, the reset when a drag is started.
18667      * @method setStartPosition
18668      * @param pos current position (from previous lookup)
18669      * @private
18670      */
18671     setStartPosition: function(pos) {
18672         var p = pos || Dom.getXY( this.getEl() );
18673         this.deltaSetXY = null;
18674
18675         this.startPageX = p[0];
18676         this.startPageY = p[1];
18677     },
18678
18679     /**
18680      * Add this instance to a group of related drag/drop objects.  All
18681      * instances belong to at least one group, and can belong to as many
18682      * groups as needed.
18683      * @method addToGroup
18684      * @param sGroup {string} the name of the group
18685      */
18686     addToGroup: function(sGroup) {
18687         this.groups[sGroup] = true;
18688         this.DDM.regDragDrop(this, sGroup);
18689     },
18690
18691     /**
18692      * Remove's this instance from the supplied interaction group
18693      * @method removeFromGroup
18694      * @param {string}  sGroup  The group to drop
18695      */
18696     removeFromGroup: function(sGroup) {
18697         if (this.groups[sGroup]) {
18698             delete this.groups[sGroup];
18699         }
18700
18701         this.DDM.removeDDFromGroup(this, sGroup);
18702     },
18703
18704     /**
18705      * Allows you to specify that an element other than the linked element
18706      * will be moved with the cursor during a drag
18707      * @method setDragElId
18708      * @param id {string} the id of the element that will be used to initiate the drag
18709      */
18710     setDragElId: function(id) {
18711         this.dragElId = id;
18712     },
18713
18714     /**
18715      * Allows you to specify a child of the linked element that should be
18716      * used to initiate the drag operation.  An example of this would be if
18717      * you have a content div with text and links.  Clicking anywhere in the
18718      * content area would normally start the drag operation.  Use this method
18719      * to specify that an element inside of the content div is the element
18720      * that starts the drag operation.
18721      * @method setHandleElId
18722      * @param id {string} the id of the element that will be used to
18723      * initiate the drag.
18724      */
18725     setHandleElId: function(id) {
18726         if (typeof id !== "string") {
18727             id = Roo.id(id);
18728         }
18729         this.handleElId = id;
18730         this.DDM.regHandle(this.id, id);
18731     },
18732
18733     /**
18734      * Allows you to set an element outside of the linked element as a drag
18735      * handle
18736      * @method setOuterHandleElId
18737      * @param id the id of the element that will be used to initiate the drag
18738      */
18739     setOuterHandleElId: function(id) {
18740         if (typeof id !== "string") {
18741             id = Roo.id(id);
18742         }
18743         Event.on(id, "mousedown",
18744                 this.handleMouseDown, this);
18745         this.setHandleElId(id);
18746
18747         this.hasOuterHandles = true;
18748     },
18749
18750     /**
18751      * Remove all drag and drop hooks for this element
18752      * @method unreg
18753      */
18754     unreg: function() {
18755         Event.un(this.id, "mousedown",
18756                 this.handleMouseDown);
18757         Event.un(this.id, "touchstart",
18758                 this.handleMouseDown);
18759         this._domRef = null;
18760         this.DDM._remove(this);
18761     },
18762
18763     destroy : function(){
18764         this.unreg();
18765     },
18766
18767     /**
18768      * Returns true if this instance is locked, or the drag drop mgr is locked
18769      * (meaning that all drag/drop is disabled on the page.)
18770      * @method isLocked
18771      * @return {boolean} true if this obj or all drag/drop is locked, else
18772      * false
18773      */
18774     isLocked: function() {
18775         return (this.DDM.isLocked() || this.locked);
18776     },
18777
18778     /**
18779      * Fired when this object is clicked
18780      * @method handleMouseDown
18781      * @param {Event} e
18782      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18783      * @private
18784      */
18785     handleMouseDown: function(e, oDD){
18786      
18787         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18788             //Roo.log('not touch/ button !=0');
18789             return;
18790         }
18791         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18792             return; // double touch..
18793         }
18794         
18795
18796         if (this.isLocked()) {
18797             //Roo.log('locked');
18798             return;
18799         }
18800
18801         this.DDM.refreshCache(this.groups);
18802 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18803         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18804         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18805             //Roo.log('no outer handes or not over target');
18806                 // do nothing.
18807         } else {
18808 //            Roo.log('check validator');
18809             if (this.clickValidator(e)) {
18810 //                Roo.log('validate success');
18811                 // set the initial element position
18812                 this.setStartPosition();
18813
18814
18815                 this.b4MouseDown(e);
18816                 this.onMouseDown(e);
18817
18818                 this.DDM.handleMouseDown(e, this);
18819
18820                 this.DDM.stopEvent(e);
18821             } else {
18822
18823
18824             }
18825         }
18826     },
18827
18828     clickValidator: function(e) {
18829         var target = e.getTarget();
18830         return ( this.isValidHandleChild(target) &&
18831                     (this.id == this.handleElId ||
18832                         this.DDM.handleWasClicked(target, this.id)) );
18833     },
18834
18835     /**
18836      * Allows you to specify a tag name that should not start a drag operation
18837      * when clicked.  This is designed to facilitate embedding links within a
18838      * drag handle that do something other than start the drag.
18839      * @method addInvalidHandleType
18840      * @param {string} tagName the type of element to exclude
18841      */
18842     addInvalidHandleType: function(tagName) {
18843         var type = tagName.toUpperCase();
18844         this.invalidHandleTypes[type] = type;
18845     },
18846
18847     /**
18848      * Lets you to specify an element id for a child of a drag handle
18849      * that should not initiate a drag
18850      * @method addInvalidHandleId
18851      * @param {string} id the element id of the element you wish to ignore
18852      */
18853     addInvalidHandleId: function(id) {
18854         if (typeof id !== "string") {
18855             id = Roo.id(id);
18856         }
18857         this.invalidHandleIds[id] = id;
18858     },
18859
18860     /**
18861      * Lets you specify a css class of elements that will not initiate a drag
18862      * @method addInvalidHandleClass
18863      * @param {string} cssClass the class of the elements you wish to ignore
18864      */
18865     addInvalidHandleClass: function(cssClass) {
18866         this.invalidHandleClasses.push(cssClass);
18867     },
18868
18869     /**
18870      * Unsets an excluded tag name set by addInvalidHandleType
18871      * @method removeInvalidHandleType
18872      * @param {string} tagName the type of element to unexclude
18873      */
18874     removeInvalidHandleType: function(tagName) {
18875         var type = tagName.toUpperCase();
18876         // this.invalidHandleTypes[type] = null;
18877         delete this.invalidHandleTypes[type];
18878     },
18879
18880     /**
18881      * Unsets an invalid handle id
18882      * @method removeInvalidHandleId
18883      * @param {string} id the id of the element to re-enable
18884      */
18885     removeInvalidHandleId: function(id) {
18886         if (typeof id !== "string") {
18887             id = Roo.id(id);
18888         }
18889         delete this.invalidHandleIds[id];
18890     },
18891
18892     /**
18893      * Unsets an invalid css class
18894      * @method removeInvalidHandleClass
18895      * @param {string} cssClass the class of the element(s) you wish to
18896      * re-enable
18897      */
18898     removeInvalidHandleClass: function(cssClass) {
18899         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18900             if (this.invalidHandleClasses[i] == cssClass) {
18901                 delete this.invalidHandleClasses[i];
18902             }
18903         }
18904     },
18905
18906     /**
18907      * Checks the tag exclusion list to see if this click should be ignored
18908      * @method isValidHandleChild
18909      * @param {HTMLElement} node the HTMLElement to evaluate
18910      * @return {boolean} true if this is a valid tag type, false if not
18911      */
18912     isValidHandleChild: function(node) {
18913
18914         var valid = true;
18915         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18916         var nodeName;
18917         try {
18918             nodeName = node.nodeName.toUpperCase();
18919         } catch(e) {
18920             nodeName = node.nodeName;
18921         }
18922         valid = valid && !this.invalidHandleTypes[nodeName];
18923         valid = valid && !this.invalidHandleIds[node.id];
18924
18925         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18926             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18927         }
18928
18929
18930         return valid;
18931
18932     },
18933
18934     /**
18935      * Create the array of horizontal tick marks if an interval was specified
18936      * in setXConstraint().
18937      * @method setXTicks
18938      * @private
18939      */
18940     setXTicks: function(iStartX, iTickSize) {
18941         this.xTicks = [];
18942         this.xTickSize = iTickSize;
18943
18944         var tickMap = {};
18945
18946         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18947             if (!tickMap[i]) {
18948                 this.xTicks[this.xTicks.length] = i;
18949                 tickMap[i] = true;
18950             }
18951         }
18952
18953         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18954             if (!tickMap[i]) {
18955                 this.xTicks[this.xTicks.length] = i;
18956                 tickMap[i] = true;
18957             }
18958         }
18959
18960         this.xTicks.sort(this.DDM.numericSort) ;
18961     },
18962
18963     /**
18964      * Create the array of vertical tick marks if an interval was specified in
18965      * setYConstraint().
18966      * @method setYTicks
18967      * @private
18968      */
18969     setYTicks: function(iStartY, iTickSize) {
18970         this.yTicks = [];
18971         this.yTickSize = iTickSize;
18972
18973         var tickMap = {};
18974
18975         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18976             if (!tickMap[i]) {
18977                 this.yTicks[this.yTicks.length] = i;
18978                 tickMap[i] = true;
18979             }
18980         }
18981
18982         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18983             if (!tickMap[i]) {
18984                 this.yTicks[this.yTicks.length] = i;
18985                 tickMap[i] = true;
18986             }
18987         }
18988
18989         this.yTicks.sort(this.DDM.numericSort) ;
18990     },
18991
18992     /**
18993      * By default, the element can be dragged any place on the screen.  Use
18994      * this method to limit the horizontal travel of the element.  Pass in
18995      * 0,0 for the parameters if you want to lock the drag to the y axis.
18996      * @method setXConstraint
18997      * @param {int} iLeft the number of pixels the element can move to the left
18998      * @param {int} iRight the number of pixels the element can move to the
18999      * right
19000      * @param {int} iTickSize optional parameter for specifying that the
19001      * element
19002      * should move iTickSize pixels at a time.
19003      */
19004     setXConstraint: function(iLeft, iRight, iTickSize) {
19005         this.leftConstraint = iLeft;
19006         this.rightConstraint = iRight;
19007
19008         this.minX = this.initPageX - iLeft;
19009         this.maxX = this.initPageX + iRight;
19010         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19011
19012         this.constrainX = true;
19013     },
19014
19015     /**
19016      * Clears any constraints applied to this instance.  Also clears ticks
19017      * since they can't exist independent of a constraint at this time.
19018      * @method clearConstraints
19019      */
19020     clearConstraints: function() {
19021         this.constrainX = false;
19022         this.constrainY = false;
19023         this.clearTicks();
19024     },
19025
19026     /**
19027      * Clears any tick interval defined for this instance
19028      * @method clearTicks
19029      */
19030     clearTicks: function() {
19031         this.xTicks = null;
19032         this.yTicks = null;
19033         this.xTickSize = 0;
19034         this.yTickSize = 0;
19035     },
19036
19037     /**
19038      * By default, the element can be dragged any place on the screen.  Set
19039      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19040      * parameters if you want to lock the drag to the x axis.
19041      * @method setYConstraint
19042      * @param {int} iUp the number of pixels the element can move up
19043      * @param {int} iDown the number of pixels the element can move down
19044      * @param {int} iTickSize optional parameter for specifying that the
19045      * element should move iTickSize pixels at a time.
19046      */
19047     setYConstraint: function(iUp, iDown, iTickSize) {
19048         this.topConstraint = iUp;
19049         this.bottomConstraint = iDown;
19050
19051         this.minY = this.initPageY - iUp;
19052         this.maxY = this.initPageY + iDown;
19053         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19054
19055         this.constrainY = true;
19056
19057     },
19058
19059     /**
19060      * resetConstraints must be called if you manually reposition a dd element.
19061      * @method resetConstraints
19062      * @param {boolean} maintainOffset
19063      */
19064     resetConstraints: function() {
19065
19066
19067         // Maintain offsets if necessary
19068         if (this.initPageX || this.initPageX === 0) {
19069             // figure out how much this thing has moved
19070             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19071             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19072
19073             this.setInitPosition(dx, dy);
19074
19075         // This is the first time we have detected the element's position
19076         } else {
19077             this.setInitPosition();
19078         }
19079
19080         if (this.constrainX) {
19081             this.setXConstraint( this.leftConstraint,
19082                                  this.rightConstraint,
19083                                  this.xTickSize        );
19084         }
19085
19086         if (this.constrainY) {
19087             this.setYConstraint( this.topConstraint,
19088                                  this.bottomConstraint,
19089                                  this.yTickSize         );
19090         }
19091     },
19092
19093     /**
19094      * Normally the drag element is moved pixel by pixel, but we can specify
19095      * that it move a number of pixels at a time.  This method resolves the
19096      * location when we have it set up like this.
19097      * @method getTick
19098      * @param {int} val where we want to place the object
19099      * @param {int[]} tickArray sorted array of valid points
19100      * @return {int} the closest tick
19101      * @private
19102      */
19103     getTick: function(val, tickArray) {
19104
19105         if (!tickArray) {
19106             // If tick interval is not defined, it is effectively 1 pixel,
19107             // so we return the value passed to us.
19108             return val;
19109         } else if (tickArray[0] >= val) {
19110             // The value is lower than the first tick, so we return the first
19111             // tick.
19112             return tickArray[0];
19113         } else {
19114             for (var i=0, len=tickArray.length; i<len; ++i) {
19115                 var next = i + 1;
19116                 if (tickArray[next] && tickArray[next] >= val) {
19117                     var diff1 = val - tickArray[i];
19118                     var diff2 = tickArray[next] - val;
19119                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19120                 }
19121             }
19122
19123             // The value is larger than the last tick, so we return the last
19124             // tick.
19125             return tickArray[tickArray.length - 1];
19126         }
19127     },
19128
19129     /**
19130      * toString method
19131      * @method toString
19132      * @return {string} string representation of the dd obj
19133      */
19134     toString: function() {
19135         return ("DragDrop " + this.id);
19136     }
19137
19138 });
19139
19140 })();
19141 /*
19142  * Based on:
19143  * Ext JS Library 1.1.1
19144  * Copyright(c) 2006-2007, Ext JS, LLC.
19145  *
19146  * Originally Released Under LGPL - original licence link has changed is not relivant.
19147  *
19148  * Fork - LGPL
19149  * <script type="text/javascript">
19150  */
19151
19152
19153 /**
19154  * The drag and drop utility provides a framework for building drag and drop
19155  * applications.  In addition to enabling drag and drop for specific elements,
19156  * the drag and drop elements are tracked by the manager class, and the
19157  * interactions between the various elements are tracked during the drag and
19158  * the implementing code is notified about these important moments.
19159  */
19160
19161 // Only load the library once.  Rewriting the manager class would orphan
19162 // existing drag and drop instances.
19163 if (!Roo.dd.DragDropMgr) {
19164
19165 /**
19166  * @class Roo.dd.DragDropMgr
19167  * DragDropMgr is a singleton that tracks the element interaction for
19168  * all DragDrop items in the window.  Generally, you will not call
19169  * this class directly, but it does have helper methods that could
19170  * be useful in your DragDrop implementations.
19171  * @singleton
19172  */
19173 Roo.dd.DragDropMgr = function() {
19174
19175     var Event = Roo.EventManager;
19176
19177     return {
19178
19179         /**
19180          * Two dimensional Array of registered DragDrop objects.  The first
19181          * dimension is the DragDrop item group, the second the DragDrop
19182          * object.
19183          * @property ids
19184          * @type {string: string}
19185          * @private
19186          * @static
19187          */
19188         ids: {},
19189
19190         /**
19191          * Array of element ids defined as drag handles.  Used to determine
19192          * if the element that generated the mousedown event is actually the
19193          * handle and not the html element itself.
19194          * @property handleIds
19195          * @type {string: string}
19196          * @private
19197          * @static
19198          */
19199         handleIds: {},
19200
19201         /**
19202          * the DragDrop object that is currently being dragged
19203          * @property dragCurrent
19204          * @type DragDrop
19205          * @private
19206          * @static
19207          **/
19208         dragCurrent: null,
19209
19210         /**
19211          * the DragDrop object(s) that are being hovered over
19212          * @property dragOvers
19213          * @type Array
19214          * @private
19215          * @static
19216          */
19217         dragOvers: {},
19218
19219         /**
19220          * the X distance between the cursor and the object being dragged
19221          * @property deltaX
19222          * @type int
19223          * @private
19224          * @static
19225          */
19226         deltaX: 0,
19227
19228         /**
19229          * the Y distance between the cursor and the object being dragged
19230          * @property deltaY
19231          * @type int
19232          * @private
19233          * @static
19234          */
19235         deltaY: 0,
19236
19237         /**
19238          * Flag to determine if we should prevent the default behavior of the
19239          * events we define. By default this is true, but this can be set to
19240          * false if you need the default behavior (not recommended)
19241          * @property preventDefault
19242          * @type boolean
19243          * @static
19244          */
19245         preventDefault: true,
19246
19247         /**
19248          * Flag to determine if we should stop the propagation of the events
19249          * we generate. This is true by default but you may want to set it to
19250          * false if the html element contains other features that require the
19251          * mouse click.
19252          * @property stopPropagation
19253          * @type boolean
19254          * @static
19255          */
19256         stopPropagation: true,
19257
19258         /**
19259          * Internal flag that is set to true when drag and drop has been
19260          * intialized
19261          * @property initialized
19262          * @private
19263          * @static
19264          */
19265         initalized: false,
19266
19267         /**
19268          * All drag and drop can be disabled.
19269          * @property locked
19270          * @private
19271          * @static
19272          */
19273         locked: false,
19274
19275         /**
19276          * Called the first time an element is registered.
19277          * @method init
19278          * @private
19279          * @static
19280          */
19281         init: function() {
19282             this.initialized = true;
19283         },
19284
19285         /**
19286          * In point mode, drag and drop interaction is defined by the
19287          * location of the cursor during the drag/drop
19288          * @property POINT
19289          * @type int
19290          * @static
19291          */
19292         POINT: 0,
19293
19294         /**
19295          * In intersect mode, drag and drop interactio nis defined by the
19296          * overlap of two or more drag and drop objects.
19297          * @property INTERSECT
19298          * @type int
19299          * @static
19300          */
19301         INTERSECT: 1,
19302
19303         /**
19304          * The current drag and drop mode.  Default: POINT
19305          * @property mode
19306          * @type int
19307          * @static
19308          */
19309         mode: 0,
19310
19311         /**
19312          * Runs method on all drag and drop objects
19313          * @method _execOnAll
19314          * @private
19315          * @static
19316          */
19317         _execOnAll: function(sMethod, args) {
19318             for (var i in this.ids) {
19319                 for (var j in this.ids[i]) {
19320                     var oDD = this.ids[i][j];
19321                     if (! this.isTypeOfDD(oDD)) {
19322                         continue;
19323                     }
19324                     oDD[sMethod].apply(oDD, args);
19325                 }
19326             }
19327         },
19328
19329         /**
19330          * Drag and drop initialization.  Sets up the global event handlers
19331          * @method _onLoad
19332          * @private
19333          * @static
19334          */
19335         _onLoad: function() {
19336
19337             this.init();
19338
19339             if (!Roo.isTouch) {
19340                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19341                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19342             }
19343             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19344             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19345             
19346             Event.on(window,   "unload",    this._onUnload, this, true);
19347             Event.on(window,   "resize",    this._onResize, this, true);
19348             // Event.on(window,   "mouseout",    this._test);
19349
19350         },
19351
19352         /**
19353          * Reset constraints on all drag and drop objs
19354          * @method _onResize
19355          * @private
19356          * @static
19357          */
19358         _onResize: function(e) {
19359             this._execOnAll("resetConstraints", []);
19360         },
19361
19362         /**
19363          * Lock all drag and drop functionality
19364          * @method lock
19365          * @static
19366          */
19367         lock: function() { this.locked = true; },
19368
19369         /**
19370          * Unlock all drag and drop functionality
19371          * @method unlock
19372          * @static
19373          */
19374         unlock: function() { this.locked = false; },
19375
19376         /**
19377          * Is drag and drop locked?
19378          * @method isLocked
19379          * @return {boolean} True if drag and drop is locked, false otherwise.
19380          * @static
19381          */
19382         isLocked: function() { return this.locked; },
19383
19384         /**
19385          * Location cache that is set for all drag drop objects when a drag is
19386          * initiated, cleared when the drag is finished.
19387          * @property locationCache
19388          * @private
19389          * @static
19390          */
19391         locationCache: {},
19392
19393         /**
19394          * Set useCache to false if you want to force object the lookup of each
19395          * drag and drop linked element constantly during a drag.
19396          * @property useCache
19397          * @type boolean
19398          * @static
19399          */
19400         useCache: true,
19401
19402         /**
19403          * The number of pixels that the mouse needs to move after the
19404          * mousedown before the drag is initiated.  Default=3;
19405          * @property clickPixelThresh
19406          * @type int
19407          * @static
19408          */
19409         clickPixelThresh: 3,
19410
19411         /**
19412          * The number of milliseconds after the mousedown event to initiate the
19413          * drag if we don't get a mouseup event. Default=1000
19414          * @property clickTimeThresh
19415          * @type int
19416          * @static
19417          */
19418         clickTimeThresh: 350,
19419
19420         /**
19421          * Flag that indicates that either the drag pixel threshold or the
19422          * mousdown time threshold has been met
19423          * @property dragThreshMet
19424          * @type boolean
19425          * @private
19426          * @static
19427          */
19428         dragThreshMet: false,
19429
19430         /**
19431          * Timeout used for the click time threshold
19432          * @property clickTimeout
19433          * @type Object
19434          * @private
19435          * @static
19436          */
19437         clickTimeout: null,
19438
19439         /**
19440          * The X position of the mousedown event stored for later use when a
19441          * drag threshold is met.
19442          * @property startX
19443          * @type int
19444          * @private
19445          * @static
19446          */
19447         startX: 0,
19448
19449         /**
19450          * The Y position of the mousedown event stored for later use when a
19451          * drag threshold is met.
19452          * @property startY
19453          * @type int
19454          * @private
19455          * @static
19456          */
19457         startY: 0,
19458
19459         /**
19460          * Each DragDrop instance must be registered with the DragDropMgr.
19461          * This is executed in DragDrop.init()
19462          * @method regDragDrop
19463          * @param {DragDrop} oDD the DragDrop object to register
19464          * @param {String} sGroup the name of the group this element belongs to
19465          * @static
19466          */
19467         regDragDrop: function(oDD, sGroup) {
19468             if (!this.initialized) { this.init(); }
19469
19470             if (!this.ids[sGroup]) {
19471                 this.ids[sGroup] = {};
19472             }
19473             this.ids[sGroup][oDD.id] = oDD;
19474         },
19475
19476         /**
19477          * Removes the supplied dd instance from the supplied group. Executed
19478          * by DragDrop.removeFromGroup, so don't call this function directly.
19479          * @method removeDDFromGroup
19480          * @private
19481          * @static
19482          */
19483         removeDDFromGroup: function(oDD, sGroup) {
19484             if (!this.ids[sGroup]) {
19485                 this.ids[sGroup] = {};
19486             }
19487
19488             var obj = this.ids[sGroup];
19489             if (obj && obj[oDD.id]) {
19490                 delete obj[oDD.id];
19491             }
19492         },
19493
19494         /**
19495          * Unregisters a drag and drop item.  This is executed in
19496          * DragDrop.unreg, use that method instead of calling this directly.
19497          * @method _remove
19498          * @private
19499          * @static
19500          */
19501         _remove: function(oDD) {
19502             for (var g in oDD.groups) {
19503                 if (g && this.ids[g][oDD.id]) {
19504                     delete this.ids[g][oDD.id];
19505                 }
19506             }
19507             delete this.handleIds[oDD.id];
19508         },
19509
19510         /**
19511          * Each DragDrop handle element must be registered.  This is done
19512          * automatically when executing DragDrop.setHandleElId()
19513          * @method regHandle
19514          * @param {String} sDDId the DragDrop id this element is a handle for
19515          * @param {String} sHandleId the id of the element that is the drag
19516          * handle
19517          * @static
19518          */
19519         regHandle: function(sDDId, sHandleId) {
19520             if (!this.handleIds[sDDId]) {
19521                 this.handleIds[sDDId] = {};
19522             }
19523             this.handleIds[sDDId][sHandleId] = sHandleId;
19524         },
19525
19526         /**
19527          * Utility function to determine if a given element has been
19528          * registered as a drag drop item.
19529          * @method isDragDrop
19530          * @param {String} id the element id to check
19531          * @return {boolean} true if this element is a DragDrop item,
19532          * false otherwise
19533          * @static
19534          */
19535         isDragDrop: function(id) {
19536             return ( this.getDDById(id) ) ? true : false;
19537         },
19538
19539         /**
19540          * Returns the drag and drop instances that are in all groups the
19541          * passed in instance belongs to.
19542          * @method getRelated
19543          * @param {DragDrop} p_oDD the obj to get related data for
19544          * @param {boolean} bTargetsOnly if true, only return targetable objs
19545          * @return {DragDrop[]} the related instances
19546          * @static
19547          */
19548         getRelated: function(p_oDD, bTargetsOnly) {
19549             var oDDs = [];
19550             for (var i in p_oDD.groups) {
19551                 for (j in this.ids[i]) {
19552                     var dd = this.ids[i][j];
19553                     if (! this.isTypeOfDD(dd)) {
19554                         continue;
19555                     }
19556                     if (!bTargetsOnly || dd.isTarget) {
19557                         oDDs[oDDs.length] = dd;
19558                     }
19559                 }
19560             }
19561
19562             return oDDs;
19563         },
19564
19565         /**
19566          * Returns true if the specified dd target is a legal target for
19567          * the specifice drag obj
19568          * @method isLegalTarget
19569          * @param {DragDrop} the drag obj
19570          * @param {DragDrop} the target
19571          * @return {boolean} true if the target is a legal target for the
19572          * dd obj
19573          * @static
19574          */
19575         isLegalTarget: function (oDD, oTargetDD) {
19576             var targets = this.getRelated(oDD, true);
19577             for (var i=0, len=targets.length;i<len;++i) {
19578                 if (targets[i].id == oTargetDD.id) {
19579                     return true;
19580                 }
19581             }
19582
19583             return false;
19584         },
19585
19586         /**
19587          * My goal is to be able to transparently determine if an object is
19588          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19589          * returns "object", oDD.constructor.toString() always returns
19590          * "DragDrop" and not the name of the subclass.  So for now it just
19591          * evaluates a well-known variable in DragDrop.
19592          * @method isTypeOfDD
19593          * @param {Object} the object to evaluate
19594          * @return {boolean} true if typeof oDD = DragDrop
19595          * @static
19596          */
19597         isTypeOfDD: function (oDD) {
19598             return (oDD && oDD.__ygDragDrop);
19599         },
19600
19601         /**
19602          * Utility function to determine if a given element has been
19603          * registered as a drag drop handle for the given Drag Drop object.
19604          * @method isHandle
19605          * @param {String} id the element id to check
19606          * @return {boolean} true if this element is a DragDrop handle, false
19607          * otherwise
19608          * @static
19609          */
19610         isHandle: function(sDDId, sHandleId) {
19611             return ( this.handleIds[sDDId] &&
19612                             this.handleIds[sDDId][sHandleId] );
19613         },
19614
19615         /**
19616          * Returns the DragDrop instance for a given id
19617          * @method getDDById
19618          * @param {String} id the id of the DragDrop object
19619          * @return {DragDrop} the drag drop object, null if it is not found
19620          * @static
19621          */
19622         getDDById: function(id) {
19623             for (var i in this.ids) {
19624                 if (this.ids[i][id]) {
19625                     return this.ids[i][id];
19626                 }
19627             }
19628             return null;
19629         },
19630
19631         /**
19632          * Fired after a registered DragDrop object gets the mousedown event.
19633          * Sets up the events required to track the object being dragged
19634          * @method handleMouseDown
19635          * @param {Event} e the event
19636          * @param oDD the DragDrop object being dragged
19637          * @private
19638          * @static
19639          */
19640         handleMouseDown: function(e, oDD) {
19641             if(Roo.QuickTips){
19642                 Roo.QuickTips.disable();
19643             }
19644             this.currentTarget = e.getTarget();
19645
19646             this.dragCurrent = oDD;
19647
19648             var el = oDD.getEl();
19649
19650             // track start position
19651             this.startX = e.getPageX();
19652             this.startY = e.getPageY();
19653
19654             this.deltaX = this.startX - el.offsetLeft;
19655             this.deltaY = this.startY - el.offsetTop;
19656
19657             this.dragThreshMet = false;
19658
19659             this.clickTimeout = setTimeout(
19660                     function() {
19661                         var DDM = Roo.dd.DDM;
19662                         DDM.startDrag(DDM.startX, DDM.startY);
19663                     },
19664                     this.clickTimeThresh );
19665         },
19666
19667         /**
19668          * Fired when either the drag pixel threshol or the mousedown hold
19669          * time threshold has been met.
19670          * @method startDrag
19671          * @param x {int} the X position of the original mousedown
19672          * @param y {int} the Y position of the original mousedown
19673          * @static
19674          */
19675         startDrag: function(x, y) {
19676             clearTimeout(this.clickTimeout);
19677             if (this.dragCurrent) {
19678                 this.dragCurrent.b4StartDrag(x, y);
19679                 this.dragCurrent.startDrag(x, y);
19680             }
19681             this.dragThreshMet = true;
19682         },
19683
19684         /**
19685          * Internal function to handle the mouseup event.  Will be invoked
19686          * from the context of the document.
19687          * @method handleMouseUp
19688          * @param {Event} e the event
19689          * @private
19690          * @static
19691          */
19692         handleMouseUp: function(e) {
19693
19694             if(Roo.QuickTips){
19695                 Roo.QuickTips.enable();
19696             }
19697             if (! this.dragCurrent) {
19698                 return;
19699             }
19700
19701             clearTimeout(this.clickTimeout);
19702
19703             if (this.dragThreshMet) {
19704                 this.fireEvents(e, true);
19705             } else {
19706             }
19707
19708             this.stopDrag(e);
19709
19710             this.stopEvent(e);
19711         },
19712
19713         /**
19714          * Utility to stop event propagation and event default, if these
19715          * features are turned on.
19716          * @method stopEvent
19717          * @param {Event} e the event as returned by this.getEvent()
19718          * @static
19719          */
19720         stopEvent: function(e){
19721             if(this.stopPropagation) {
19722                 e.stopPropagation();
19723             }
19724
19725             if (this.preventDefault) {
19726                 e.preventDefault();
19727             }
19728         },
19729
19730         /**
19731          * Internal function to clean up event handlers after the drag
19732          * operation is complete
19733          * @method stopDrag
19734          * @param {Event} e the event
19735          * @private
19736          * @static
19737          */
19738         stopDrag: function(e) {
19739             // Fire the drag end event for the item that was dragged
19740             if (this.dragCurrent) {
19741                 if (this.dragThreshMet) {
19742                     this.dragCurrent.b4EndDrag(e);
19743                     this.dragCurrent.endDrag(e);
19744                 }
19745
19746                 this.dragCurrent.onMouseUp(e);
19747             }
19748
19749             this.dragCurrent = null;
19750             this.dragOvers = {};
19751         },
19752
19753         /**
19754          * Internal function to handle the mousemove event.  Will be invoked
19755          * from the context of the html element.
19756          *
19757          * @TODO figure out what we can do about mouse events lost when the
19758          * user drags objects beyond the window boundary.  Currently we can
19759          * detect this in internet explorer by verifying that the mouse is
19760          * down during the mousemove event.  Firefox doesn't give us the
19761          * button state on the mousemove event.
19762          * @method handleMouseMove
19763          * @param {Event} e the event
19764          * @private
19765          * @static
19766          */
19767         handleMouseMove: function(e) {
19768             if (! this.dragCurrent) {
19769                 return true;
19770             }
19771
19772             // var button = e.which || e.button;
19773
19774             // check for IE mouseup outside of page boundary
19775             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19776                 this.stopEvent(e);
19777                 return this.handleMouseUp(e);
19778             }
19779
19780             if (!this.dragThreshMet) {
19781                 var diffX = Math.abs(this.startX - e.getPageX());
19782                 var diffY = Math.abs(this.startY - e.getPageY());
19783                 if (diffX > this.clickPixelThresh ||
19784                             diffY > this.clickPixelThresh) {
19785                     this.startDrag(this.startX, this.startY);
19786                 }
19787             }
19788
19789             if (this.dragThreshMet) {
19790                 this.dragCurrent.b4Drag(e);
19791                 this.dragCurrent.onDrag(e);
19792                 if(!this.dragCurrent.moveOnly){
19793                     this.fireEvents(e, false);
19794                 }
19795             }
19796
19797             this.stopEvent(e);
19798
19799             return true;
19800         },
19801
19802         /**
19803          * Iterates over all of the DragDrop elements to find ones we are
19804          * hovering over or dropping on
19805          * @method fireEvents
19806          * @param {Event} e the event
19807          * @param {boolean} isDrop is this a drop op or a mouseover op?
19808          * @private
19809          * @static
19810          */
19811         fireEvents: function(e, isDrop) {
19812             var dc = this.dragCurrent;
19813
19814             // If the user did the mouse up outside of the window, we could
19815             // get here even though we have ended the drag.
19816             if (!dc || dc.isLocked()) {
19817                 return;
19818             }
19819
19820             var pt = e.getPoint();
19821
19822             // cache the previous dragOver array
19823             var oldOvers = [];
19824
19825             var outEvts   = [];
19826             var overEvts  = [];
19827             var dropEvts  = [];
19828             var enterEvts = [];
19829
19830             // Check to see if the object(s) we were hovering over is no longer
19831             // being hovered over so we can fire the onDragOut event
19832             for (var i in this.dragOvers) {
19833
19834                 var ddo = this.dragOvers[i];
19835
19836                 if (! this.isTypeOfDD(ddo)) {
19837                     continue;
19838                 }
19839
19840                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19841                     outEvts.push( ddo );
19842                 }
19843
19844                 oldOvers[i] = true;
19845                 delete this.dragOvers[i];
19846             }
19847
19848             for (var sGroup in dc.groups) {
19849
19850                 if ("string" != typeof sGroup) {
19851                     continue;
19852                 }
19853
19854                 for (i in this.ids[sGroup]) {
19855                     var oDD = this.ids[sGroup][i];
19856                     if (! this.isTypeOfDD(oDD)) {
19857                         continue;
19858                     }
19859
19860                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19861                         if (this.isOverTarget(pt, oDD, this.mode)) {
19862                             // look for drop interactions
19863                             if (isDrop) {
19864                                 dropEvts.push( oDD );
19865                             // look for drag enter and drag over interactions
19866                             } else {
19867
19868                                 // initial drag over: dragEnter fires
19869                                 if (!oldOvers[oDD.id]) {
19870                                     enterEvts.push( oDD );
19871                                 // subsequent drag overs: dragOver fires
19872                                 } else {
19873                                     overEvts.push( oDD );
19874                                 }
19875
19876                                 this.dragOvers[oDD.id] = oDD;
19877                             }
19878                         }
19879                     }
19880                 }
19881             }
19882
19883             if (this.mode) {
19884                 if (outEvts.length) {
19885                     dc.b4DragOut(e, outEvts);
19886                     dc.onDragOut(e, outEvts);
19887                 }
19888
19889                 if (enterEvts.length) {
19890                     dc.onDragEnter(e, enterEvts);
19891                 }
19892
19893                 if (overEvts.length) {
19894                     dc.b4DragOver(e, overEvts);
19895                     dc.onDragOver(e, overEvts);
19896                 }
19897
19898                 if (dropEvts.length) {
19899                     dc.b4DragDrop(e, dropEvts);
19900                     dc.onDragDrop(e, dropEvts);
19901                 }
19902
19903             } else {
19904                 // fire dragout events
19905                 var len = 0;
19906                 for (i=0, len=outEvts.length; i<len; ++i) {
19907                     dc.b4DragOut(e, outEvts[i].id);
19908                     dc.onDragOut(e, outEvts[i].id);
19909                 }
19910
19911                 // fire enter events
19912                 for (i=0,len=enterEvts.length; i<len; ++i) {
19913                     // dc.b4DragEnter(e, oDD.id);
19914                     dc.onDragEnter(e, enterEvts[i].id);
19915                 }
19916
19917                 // fire over events
19918                 for (i=0,len=overEvts.length; i<len; ++i) {
19919                     dc.b4DragOver(e, overEvts[i].id);
19920                     dc.onDragOver(e, overEvts[i].id);
19921                 }
19922
19923                 // fire drop events
19924                 for (i=0, len=dropEvts.length; i<len; ++i) {
19925                     dc.b4DragDrop(e, dropEvts[i].id);
19926                     dc.onDragDrop(e, dropEvts[i].id);
19927                 }
19928
19929             }
19930
19931             // notify about a drop that did not find a target
19932             if (isDrop && !dropEvts.length) {
19933                 dc.onInvalidDrop(e);
19934             }
19935
19936         },
19937
19938         /**
19939          * Helper function for getting the best match from the list of drag
19940          * and drop objects returned by the drag and drop events when we are
19941          * in INTERSECT mode.  It returns either the first object that the
19942          * cursor is over, or the object that has the greatest overlap with
19943          * the dragged element.
19944          * @method getBestMatch
19945          * @param  {DragDrop[]} dds The array of drag and drop objects
19946          * targeted
19947          * @return {DragDrop}       The best single match
19948          * @static
19949          */
19950         getBestMatch: function(dds) {
19951             var winner = null;
19952             // Return null if the input is not what we expect
19953             //if (!dds || !dds.length || dds.length == 0) {
19954                // winner = null;
19955             // If there is only one item, it wins
19956             //} else if (dds.length == 1) {
19957
19958             var len = dds.length;
19959
19960             if (len == 1) {
19961                 winner = dds[0];
19962             } else {
19963                 // Loop through the targeted items
19964                 for (var i=0; i<len; ++i) {
19965                     var dd = dds[i];
19966                     // If the cursor is over the object, it wins.  If the
19967                     // cursor is over multiple matches, the first one we come
19968                     // to wins.
19969                     if (dd.cursorIsOver) {
19970                         winner = dd;
19971                         break;
19972                     // Otherwise the object with the most overlap wins
19973                     } else {
19974                         if (!winner ||
19975                             winner.overlap.getArea() < dd.overlap.getArea()) {
19976                             winner = dd;
19977                         }
19978                     }
19979                 }
19980             }
19981
19982             return winner;
19983         },
19984
19985         /**
19986          * Refreshes the cache of the top-left and bottom-right points of the
19987          * drag and drop objects in the specified group(s).  This is in the
19988          * format that is stored in the drag and drop instance, so typical
19989          * usage is:
19990          * <code>
19991          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19992          * </code>
19993          * Alternatively:
19994          * <code>
19995          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19996          * </code>
19997          * @TODO this really should be an indexed array.  Alternatively this
19998          * method could accept both.
19999          * @method refreshCache
20000          * @param {Object} groups an associative array of groups to refresh
20001          * @static
20002          */
20003         refreshCache: function(groups) {
20004             for (var sGroup in groups) {
20005                 if ("string" != typeof sGroup) {
20006                     continue;
20007                 }
20008                 for (var i in this.ids[sGroup]) {
20009                     var oDD = this.ids[sGroup][i];
20010
20011                     if (this.isTypeOfDD(oDD)) {
20012                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20013                         var loc = this.getLocation(oDD);
20014                         if (loc) {
20015                             this.locationCache[oDD.id] = loc;
20016                         } else {
20017                             delete this.locationCache[oDD.id];
20018                             // this will unregister the drag and drop object if
20019                             // the element is not in a usable state
20020                             // oDD.unreg();
20021                         }
20022                     }
20023                 }
20024             }
20025         },
20026
20027         /**
20028          * This checks to make sure an element exists and is in the DOM.  The
20029          * main purpose is to handle cases where innerHTML is used to remove
20030          * drag and drop objects from the DOM.  IE provides an 'unspecified
20031          * error' when trying to access the offsetParent of such an element
20032          * @method verifyEl
20033          * @param {HTMLElement} el the element to check
20034          * @return {boolean} true if the element looks usable
20035          * @static
20036          */
20037         verifyEl: function(el) {
20038             if (el) {
20039                 var parent;
20040                 if(Roo.isIE){
20041                     try{
20042                         parent = el.offsetParent;
20043                     }catch(e){}
20044                 }else{
20045                     parent = el.offsetParent;
20046                 }
20047                 if (parent) {
20048                     return true;
20049                 }
20050             }
20051
20052             return false;
20053         },
20054
20055         /**
20056          * Returns a Region object containing the drag and drop element's position
20057          * and size, including the padding configured for it
20058          * @method getLocation
20059          * @param {DragDrop} oDD the drag and drop object to get the
20060          *                       location for
20061          * @return {Roo.lib.Region} a Region object representing the total area
20062          *                             the element occupies, including any padding
20063          *                             the instance is configured for.
20064          * @static
20065          */
20066         getLocation: function(oDD) {
20067             if (! this.isTypeOfDD(oDD)) {
20068                 return null;
20069             }
20070
20071             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20072
20073             try {
20074                 pos= Roo.lib.Dom.getXY(el);
20075             } catch (e) { }
20076
20077             if (!pos) {
20078                 return null;
20079             }
20080
20081             x1 = pos[0];
20082             x2 = x1 + el.offsetWidth;
20083             y1 = pos[1];
20084             y2 = y1 + el.offsetHeight;
20085
20086             t = y1 - oDD.padding[0];
20087             r = x2 + oDD.padding[1];
20088             b = y2 + oDD.padding[2];
20089             l = x1 - oDD.padding[3];
20090
20091             return new Roo.lib.Region( t, r, b, l );
20092         },
20093
20094         /**
20095          * Checks the cursor location to see if it over the target
20096          * @method isOverTarget
20097          * @param {Roo.lib.Point} pt The point to evaluate
20098          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20099          * @return {boolean} true if the mouse is over the target
20100          * @private
20101          * @static
20102          */
20103         isOverTarget: function(pt, oTarget, intersect) {
20104             // use cache if available
20105             var loc = this.locationCache[oTarget.id];
20106             if (!loc || !this.useCache) {
20107                 loc = this.getLocation(oTarget);
20108                 this.locationCache[oTarget.id] = loc;
20109
20110             }
20111
20112             if (!loc) {
20113                 return false;
20114             }
20115
20116             oTarget.cursorIsOver = loc.contains( pt );
20117
20118             // DragDrop is using this as a sanity check for the initial mousedown
20119             // in this case we are done.  In POINT mode, if the drag obj has no
20120             // contraints, we are also done. Otherwise we need to evaluate the
20121             // location of the target as related to the actual location of the
20122             // dragged element.
20123             var dc = this.dragCurrent;
20124             if (!dc || !dc.getTargetCoord ||
20125                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20126                 return oTarget.cursorIsOver;
20127             }
20128
20129             oTarget.overlap = null;
20130
20131             // Get the current location of the drag element, this is the
20132             // location of the mouse event less the delta that represents
20133             // where the original mousedown happened on the element.  We
20134             // need to consider constraints and ticks as well.
20135             var pos = dc.getTargetCoord(pt.x, pt.y);
20136
20137             var el = dc.getDragEl();
20138             var curRegion = new Roo.lib.Region( pos.y,
20139                                                    pos.x + el.offsetWidth,
20140                                                    pos.y + el.offsetHeight,
20141                                                    pos.x );
20142
20143             var overlap = curRegion.intersect(loc);
20144
20145             if (overlap) {
20146                 oTarget.overlap = overlap;
20147                 return (intersect) ? true : oTarget.cursorIsOver;
20148             } else {
20149                 return false;
20150             }
20151         },
20152
20153         /**
20154          * unload event handler
20155          * @method _onUnload
20156          * @private
20157          * @static
20158          */
20159         _onUnload: function(e, me) {
20160             Roo.dd.DragDropMgr.unregAll();
20161         },
20162
20163         /**
20164          * Cleans up the drag and drop events and objects.
20165          * @method unregAll
20166          * @private
20167          * @static
20168          */
20169         unregAll: function() {
20170
20171             if (this.dragCurrent) {
20172                 this.stopDrag();
20173                 this.dragCurrent = null;
20174             }
20175
20176             this._execOnAll("unreg", []);
20177
20178             for (i in this.elementCache) {
20179                 delete this.elementCache[i];
20180             }
20181
20182             this.elementCache = {};
20183             this.ids = {};
20184         },
20185
20186         /**
20187          * A cache of DOM elements
20188          * @property elementCache
20189          * @private
20190          * @static
20191          */
20192         elementCache: {},
20193
20194         /**
20195          * Get the wrapper for the DOM element specified
20196          * @method getElWrapper
20197          * @param {String} id the id of the element to get
20198          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20199          * @private
20200          * @deprecated This wrapper isn't that useful
20201          * @static
20202          */
20203         getElWrapper: function(id) {
20204             var oWrapper = this.elementCache[id];
20205             if (!oWrapper || !oWrapper.el) {
20206                 oWrapper = this.elementCache[id] =
20207                     new this.ElementWrapper(Roo.getDom(id));
20208             }
20209             return oWrapper;
20210         },
20211
20212         /**
20213          * Returns the actual DOM element
20214          * @method getElement
20215          * @param {String} id the id of the elment to get
20216          * @return {Object} The element
20217          * @deprecated use Roo.getDom instead
20218          * @static
20219          */
20220         getElement: function(id) {
20221             return Roo.getDom(id);
20222         },
20223
20224         /**
20225          * Returns the style property for the DOM element (i.e.,
20226          * document.getElById(id).style)
20227          * @method getCss
20228          * @param {String} id the id of the elment to get
20229          * @return {Object} The style property of the element
20230          * @deprecated use Roo.getDom instead
20231          * @static
20232          */
20233         getCss: function(id) {
20234             var el = Roo.getDom(id);
20235             return (el) ? el.style : null;
20236         },
20237
20238         /**
20239          * Inner class for cached elements
20240          * @class DragDropMgr.ElementWrapper
20241          * @for DragDropMgr
20242          * @private
20243          * @deprecated
20244          */
20245         ElementWrapper: function(el) {
20246                 /**
20247                  * The element
20248                  * @property el
20249                  */
20250                 this.el = el || null;
20251                 /**
20252                  * The element id
20253                  * @property id
20254                  */
20255                 this.id = this.el && el.id;
20256                 /**
20257                  * A reference to the style property
20258                  * @property css
20259                  */
20260                 this.css = this.el && el.style;
20261             },
20262
20263         /**
20264          * Returns the X position of an html element
20265          * @method getPosX
20266          * @param el the element for which to get the position
20267          * @return {int} the X coordinate
20268          * @for DragDropMgr
20269          * @deprecated use Roo.lib.Dom.getX instead
20270          * @static
20271          */
20272         getPosX: function(el) {
20273             return Roo.lib.Dom.getX(el);
20274         },
20275
20276         /**
20277          * Returns the Y position of an html element
20278          * @method getPosY
20279          * @param el the element for which to get the position
20280          * @return {int} the Y coordinate
20281          * @deprecated use Roo.lib.Dom.getY instead
20282          * @static
20283          */
20284         getPosY: function(el) {
20285             return Roo.lib.Dom.getY(el);
20286         },
20287
20288         /**
20289          * Swap two nodes.  In IE, we use the native method, for others we
20290          * emulate the IE behavior
20291          * @method swapNode
20292          * @param n1 the first node to swap
20293          * @param n2 the other node to swap
20294          * @static
20295          */
20296         swapNode: function(n1, n2) {
20297             if (n1.swapNode) {
20298                 n1.swapNode(n2);
20299             } else {
20300                 var p = n2.parentNode;
20301                 var s = n2.nextSibling;
20302
20303                 if (s == n1) {
20304                     p.insertBefore(n1, n2);
20305                 } else if (n2 == n1.nextSibling) {
20306                     p.insertBefore(n2, n1);
20307                 } else {
20308                     n1.parentNode.replaceChild(n2, n1);
20309                     p.insertBefore(n1, s);
20310                 }
20311             }
20312         },
20313
20314         /**
20315          * Returns the current scroll position
20316          * @method getScroll
20317          * @private
20318          * @static
20319          */
20320         getScroll: function () {
20321             var t, l, dde=document.documentElement, db=document.body;
20322             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20323                 t = dde.scrollTop;
20324                 l = dde.scrollLeft;
20325             } else if (db) {
20326                 t = db.scrollTop;
20327                 l = db.scrollLeft;
20328             } else {
20329
20330             }
20331             return { top: t, left: l };
20332         },
20333
20334         /**
20335          * Returns the specified element style property
20336          * @method getStyle
20337          * @param {HTMLElement} el          the element
20338          * @param {string}      styleProp   the style property
20339          * @return {string} The value of the style property
20340          * @deprecated use Roo.lib.Dom.getStyle
20341          * @static
20342          */
20343         getStyle: function(el, styleProp) {
20344             return Roo.fly(el).getStyle(styleProp);
20345         },
20346
20347         /**
20348          * Gets the scrollTop
20349          * @method getScrollTop
20350          * @return {int} the document's scrollTop
20351          * @static
20352          */
20353         getScrollTop: function () { return this.getScroll().top; },
20354
20355         /**
20356          * Gets the scrollLeft
20357          * @method getScrollLeft
20358          * @return {int} the document's scrollTop
20359          * @static
20360          */
20361         getScrollLeft: function () { return this.getScroll().left; },
20362
20363         /**
20364          * Sets the x/y position of an element to the location of the
20365          * target element.
20366          * @method moveToEl
20367          * @param {HTMLElement} moveEl      The element to move
20368          * @param {HTMLElement} targetEl    The position reference element
20369          * @static
20370          */
20371         moveToEl: function (moveEl, targetEl) {
20372             var aCoord = Roo.lib.Dom.getXY(targetEl);
20373             Roo.lib.Dom.setXY(moveEl, aCoord);
20374         },
20375
20376         /**
20377          * Numeric array sort function
20378          * @method numericSort
20379          * @static
20380          */
20381         numericSort: function(a, b) { return (a - b); },
20382
20383         /**
20384          * Internal counter
20385          * @property _timeoutCount
20386          * @private
20387          * @static
20388          */
20389         _timeoutCount: 0,
20390
20391         /**
20392          * Trying to make the load order less important.  Without this we get
20393          * an error if this file is loaded before the Event Utility.
20394          * @method _addListeners
20395          * @private
20396          * @static
20397          */
20398         _addListeners: function() {
20399             var DDM = Roo.dd.DDM;
20400             if ( Roo.lib.Event && document ) {
20401                 DDM._onLoad();
20402             } else {
20403                 if (DDM._timeoutCount > 2000) {
20404                 } else {
20405                     setTimeout(DDM._addListeners, 10);
20406                     if (document && document.body) {
20407                         DDM._timeoutCount += 1;
20408                     }
20409                 }
20410             }
20411         },
20412
20413         /**
20414          * Recursively searches the immediate parent and all child nodes for
20415          * the handle element in order to determine wheter or not it was
20416          * clicked.
20417          * @method handleWasClicked
20418          * @param node the html element to inspect
20419          * @static
20420          */
20421         handleWasClicked: function(node, id) {
20422             if (this.isHandle(id, node.id)) {
20423                 return true;
20424             } else {
20425                 // check to see if this is a text node child of the one we want
20426                 var p = node.parentNode;
20427
20428                 while (p) {
20429                     if (this.isHandle(id, p.id)) {
20430                         return true;
20431                     } else {
20432                         p = p.parentNode;
20433                     }
20434                 }
20435             }
20436
20437             return false;
20438         }
20439
20440     };
20441
20442 }();
20443
20444 // shorter alias, save a few bytes
20445 Roo.dd.DDM = Roo.dd.DragDropMgr;
20446 Roo.dd.DDM._addListeners();
20447
20448 }/*
20449  * Based on:
20450  * Ext JS Library 1.1.1
20451  * Copyright(c) 2006-2007, Ext JS, LLC.
20452  *
20453  * Originally Released Under LGPL - original licence link has changed is not relivant.
20454  *
20455  * Fork - LGPL
20456  * <script type="text/javascript">
20457  */
20458
20459 /**
20460  * @class Roo.dd.DD
20461  * A DragDrop implementation where the linked element follows the
20462  * mouse cursor during a drag.
20463  * @extends Roo.dd.DragDrop
20464  * @constructor
20465  * @param {String} id the id of the linked element
20466  * @param {String} sGroup the group of related DragDrop items
20467  * @param {object} config an object containing configurable attributes
20468  *                Valid properties for DD:
20469  *                    scroll
20470  */
20471 Roo.dd.DD = function(id, sGroup, config) {
20472     if (id) {
20473         this.init(id, sGroup, config);
20474     }
20475 };
20476
20477 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20478
20479     /**
20480      * When set to true, the utility automatically tries to scroll the browser
20481      * window wehn a drag and drop element is dragged near the viewport boundary.
20482      * Defaults to true.
20483      * @property scroll
20484      * @type boolean
20485      */
20486     scroll: true,
20487
20488     /**
20489      * Sets the pointer offset to the distance between the linked element's top
20490      * left corner and the location the element was clicked
20491      * @method autoOffset
20492      * @param {int} iPageX the X coordinate of the click
20493      * @param {int} iPageY the Y coordinate of the click
20494      */
20495     autoOffset: function(iPageX, iPageY) {
20496         var x = iPageX - this.startPageX;
20497         var y = iPageY - this.startPageY;
20498         this.setDelta(x, y);
20499     },
20500
20501     /**
20502      * Sets the pointer offset.  You can call this directly to force the
20503      * offset to be in a particular location (e.g., pass in 0,0 to set it
20504      * to the center of the object)
20505      * @method setDelta
20506      * @param {int} iDeltaX the distance from the left
20507      * @param {int} iDeltaY the distance from the top
20508      */
20509     setDelta: function(iDeltaX, iDeltaY) {
20510         this.deltaX = iDeltaX;
20511         this.deltaY = iDeltaY;
20512     },
20513
20514     /**
20515      * Sets the drag element to the location of the mousedown or click event,
20516      * maintaining the cursor location relative to the location on the element
20517      * that was clicked.  Override this if you want to place the element in a
20518      * location other than where the cursor is.
20519      * @method setDragElPos
20520      * @param {int} iPageX the X coordinate of the mousedown or drag event
20521      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20522      */
20523     setDragElPos: function(iPageX, iPageY) {
20524         // the first time we do this, we are going to check to make sure
20525         // the element has css positioning
20526
20527         var el = this.getDragEl();
20528         this.alignElWithMouse(el, iPageX, iPageY);
20529     },
20530
20531     /**
20532      * Sets the element to the location of the mousedown or click event,
20533      * maintaining the cursor location relative to the location on the element
20534      * that was clicked.  Override this if you want to place the element in a
20535      * location other than where the cursor is.
20536      * @method alignElWithMouse
20537      * @param {HTMLElement} el the element to move
20538      * @param {int} iPageX the X coordinate of the mousedown or drag event
20539      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20540      */
20541     alignElWithMouse: function(el, iPageX, iPageY) {
20542         var oCoord = this.getTargetCoord(iPageX, iPageY);
20543         var fly = el.dom ? el : Roo.fly(el);
20544         if (!this.deltaSetXY) {
20545             var aCoord = [oCoord.x, oCoord.y];
20546             fly.setXY(aCoord);
20547             var newLeft = fly.getLeft(true);
20548             var newTop  = fly.getTop(true);
20549             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20550         } else {
20551             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20552         }
20553
20554         this.cachePosition(oCoord.x, oCoord.y);
20555         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20556         return oCoord;
20557     },
20558
20559     /**
20560      * Saves the most recent position so that we can reset the constraints and
20561      * tick marks on-demand.  We need to know this so that we can calculate the
20562      * number of pixels the element is offset from its original position.
20563      * @method cachePosition
20564      * @param iPageX the current x position (optional, this just makes it so we
20565      * don't have to look it up again)
20566      * @param iPageY the current y position (optional, this just makes it so we
20567      * don't have to look it up again)
20568      */
20569     cachePosition: function(iPageX, iPageY) {
20570         if (iPageX) {
20571             this.lastPageX = iPageX;
20572             this.lastPageY = iPageY;
20573         } else {
20574             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20575             this.lastPageX = aCoord[0];
20576             this.lastPageY = aCoord[1];
20577         }
20578     },
20579
20580     /**
20581      * Auto-scroll the window if the dragged object has been moved beyond the
20582      * visible window boundary.
20583      * @method autoScroll
20584      * @param {int} x the drag element's x position
20585      * @param {int} y the drag element's y position
20586      * @param {int} h the height of the drag element
20587      * @param {int} w the width of the drag element
20588      * @private
20589      */
20590     autoScroll: function(x, y, h, w) {
20591
20592         if (this.scroll) {
20593             // The client height
20594             var clientH = Roo.lib.Dom.getViewWidth();
20595
20596             // The client width
20597             var clientW = Roo.lib.Dom.getViewHeight();
20598
20599             // The amt scrolled down
20600             var st = this.DDM.getScrollTop();
20601
20602             // The amt scrolled right
20603             var sl = this.DDM.getScrollLeft();
20604
20605             // Location of the bottom of the element
20606             var bot = h + y;
20607
20608             // Location of the right of the element
20609             var right = w + x;
20610
20611             // The distance from the cursor to the bottom of the visible area,
20612             // adjusted so that we don't scroll if the cursor is beyond the
20613             // element drag constraints
20614             var toBot = (clientH + st - y - this.deltaY);
20615
20616             // The distance from the cursor to the right of the visible area
20617             var toRight = (clientW + sl - x - this.deltaX);
20618
20619
20620             // How close to the edge the cursor must be before we scroll
20621             // var thresh = (document.all) ? 100 : 40;
20622             var thresh = 40;
20623
20624             // How many pixels to scroll per autoscroll op.  This helps to reduce
20625             // clunky scrolling. IE is more sensitive about this ... it needs this
20626             // value to be higher.
20627             var scrAmt = (document.all) ? 80 : 30;
20628
20629             // Scroll down if we are near the bottom of the visible page and the
20630             // obj extends below the crease
20631             if ( bot > clientH && toBot < thresh ) {
20632                 window.scrollTo(sl, st + scrAmt);
20633             }
20634
20635             // Scroll up if the window is scrolled down and the top of the object
20636             // goes above the top border
20637             if ( y < st && st > 0 && y - st < thresh ) {
20638                 window.scrollTo(sl, st - scrAmt);
20639             }
20640
20641             // Scroll right if the obj is beyond the right border and the cursor is
20642             // near the border.
20643             if ( right > clientW && toRight < thresh ) {
20644                 window.scrollTo(sl + scrAmt, st);
20645             }
20646
20647             // Scroll left if the window has been scrolled to the right and the obj
20648             // extends past the left border
20649             if ( x < sl && sl > 0 && x - sl < thresh ) {
20650                 window.scrollTo(sl - scrAmt, st);
20651             }
20652         }
20653     },
20654
20655     /**
20656      * Finds the location the element should be placed if we want to move
20657      * it to where the mouse location less the click offset would place us.
20658      * @method getTargetCoord
20659      * @param {int} iPageX the X coordinate of the click
20660      * @param {int} iPageY the Y coordinate of the click
20661      * @return an object that contains the coordinates (Object.x and Object.y)
20662      * @private
20663      */
20664     getTargetCoord: function(iPageX, iPageY) {
20665
20666
20667         var x = iPageX - this.deltaX;
20668         var y = iPageY - this.deltaY;
20669
20670         if (this.constrainX) {
20671             if (x < this.minX) { x = this.minX; }
20672             if (x > this.maxX) { x = this.maxX; }
20673         }
20674
20675         if (this.constrainY) {
20676             if (y < this.minY) { y = this.minY; }
20677             if (y > this.maxY) { y = this.maxY; }
20678         }
20679
20680         x = this.getTick(x, this.xTicks);
20681         y = this.getTick(y, this.yTicks);
20682
20683
20684         return {x:x, y:y};
20685     },
20686
20687     /*
20688      * Sets up config options specific to this class. Overrides
20689      * Roo.dd.DragDrop, but all versions of this method through the
20690      * inheritance chain are called
20691      */
20692     applyConfig: function() {
20693         Roo.dd.DD.superclass.applyConfig.call(this);
20694         this.scroll = (this.config.scroll !== false);
20695     },
20696
20697     /*
20698      * Event that fires prior to the onMouseDown event.  Overrides
20699      * Roo.dd.DragDrop.
20700      */
20701     b4MouseDown: function(e) {
20702         // this.resetConstraints();
20703         this.autoOffset(e.getPageX(),
20704                             e.getPageY());
20705     },
20706
20707     /*
20708      * Event that fires prior to the onDrag event.  Overrides
20709      * Roo.dd.DragDrop.
20710      */
20711     b4Drag: function(e) {
20712         this.setDragElPos(e.getPageX(),
20713                             e.getPageY());
20714     },
20715
20716     toString: function() {
20717         return ("DD " + this.id);
20718     }
20719
20720     //////////////////////////////////////////////////////////////////////////
20721     // Debugging ygDragDrop events that can be overridden
20722     //////////////////////////////////////////////////////////////////////////
20723     /*
20724     startDrag: function(x, y) {
20725     },
20726
20727     onDrag: function(e) {
20728     },
20729
20730     onDragEnter: function(e, id) {
20731     },
20732
20733     onDragOver: function(e, id) {
20734     },
20735
20736     onDragOut: function(e, id) {
20737     },
20738
20739     onDragDrop: function(e, id) {
20740     },
20741
20742     endDrag: function(e) {
20743     }
20744
20745     */
20746
20747 });/*
20748  * Based on:
20749  * Ext JS Library 1.1.1
20750  * Copyright(c) 2006-2007, Ext JS, LLC.
20751  *
20752  * Originally Released Under LGPL - original licence link has changed is not relivant.
20753  *
20754  * Fork - LGPL
20755  * <script type="text/javascript">
20756  */
20757
20758 /**
20759  * @class Roo.dd.DDProxy
20760  * A DragDrop implementation that inserts an empty, bordered div into
20761  * the document that follows the cursor during drag operations.  At the time of
20762  * the click, the frame div is resized to the dimensions of the linked html
20763  * element, and moved to the exact location of the linked element.
20764  *
20765  * References to the "frame" element refer to the single proxy element that
20766  * was created to be dragged in place of all DDProxy elements on the
20767  * page.
20768  *
20769  * @extends Roo.dd.DD
20770  * @constructor
20771  * @param {String} id the id of the linked html element
20772  * @param {String} sGroup the group of related DragDrop objects
20773  * @param {object} config an object containing configurable attributes
20774  *                Valid properties for DDProxy in addition to those in DragDrop:
20775  *                   resizeFrame, centerFrame, dragElId
20776  */
20777 Roo.dd.DDProxy = function(id, sGroup, config) {
20778     if (id) {
20779         this.init(id, sGroup, config);
20780         this.initFrame();
20781     }
20782 };
20783
20784 /**
20785  * The default drag frame div id
20786  * @property Roo.dd.DDProxy.dragElId
20787  * @type String
20788  * @static
20789  */
20790 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20791
20792 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20793
20794     /**
20795      * By default we resize the drag frame to be the same size as the element
20796      * we want to drag (this is to get the frame effect).  We can turn it off
20797      * if we want a different behavior.
20798      * @property resizeFrame
20799      * @type boolean
20800      */
20801     resizeFrame: true,
20802
20803     /**
20804      * By default the frame is positioned exactly where the drag element is, so
20805      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20806      * you do not have constraints on the obj is to have the drag frame centered
20807      * around the cursor.  Set centerFrame to true for this effect.
20808      * @property centerFrame
20809      * @type boolean
20810      */
20811     centerFrame: false,
20812
20813     /**
20814      * Creates the proxy element if it does not yet exist
20815      * @method createFrame
20816      */
20817     createFrame: function() {
20818         var self = this;
20819         var body = document.body;
20820
20821         if (!body || !body.firstChild) {
20822             setTimeout( function() { self.createFrame(); }, 50 );
20823             return;
20824         }
20825
20826         var div = this.getDragEl();
20827
20828         if (!div) {
20829             div    = document.createElement("div");
20830             div.id = this.dragElId;
20831             var s  = div.style;
20832
20833             s.position   = "absolute";
20834             s.visibility = "hidden";
20835             s.cursor     = "move";
20836             s.border     = "2px solid #aaa";
20837             s.zIndex     = 999;
20838
20839             // appendChild can blow up IE if invoked prior to the window load event
20840             // while rendering a table.  It is possible there are other scenarios
20841             // that would cause this to happen as well.
20842             body.insertBefore(div, body.firstChild);
20843         }
20844     },
20845
20846     /**
20847      * Initialization for the drag frame element.  Must be called in the
20848      * constructor of all subclasses
20849      * @method initFrame
20850      */
20851     initFrame: function() {
20852         this.createFrame();
20853     },
20854
20855     applyConfig: function() {
20856         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20857
20858         this.resizeFrame = (this.config.resizeFrame !== false);
20859         this.centerFrame = (this.config.centerFrame);
20860         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20861     },
20862
20863     /**
20864      * Resizes the drag frame to the dimensions of the clicked object, positions
20865      * it over the object, and finally displays it
20866      * @method showFrame
20867      * @param {int} iPageX X click position
20868      * @param {int} iPageY Y click position
20869      * @private
20870      */
20871     showFrame: function(iPageX, iPageY) {
20872         var el = this.getEl();
20873         var dragEl = this.getDragEl();
20874         var s = dragEl.style;
20875
20876         this._resizeProxy();
20877
20878         if (this.centerFrame) {
20879             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20880                            Math.round(parseInt(s.height, 10)/2) );
20881         }
20882
20883         this.setDragElPos(iPageX, iPageY);
20884
20885         Roo.fly(dragEl).show();
20886     },
20887
20888     /**
20889      * The proxy is automatically resized to the dimensions of the linked
20890      * element when a drag is initiated, unless resizeFrame is set to false
20891      * @method _resizeProxy
20892      * @private
20893      */
20894     _resizeProxy: function() {
20895         if (this.resizeFrame) {
20896             var el = this.getEl();
20897             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20898         }
20899     },
20900
20901     // overrides Roo.dd.DragDrop
20902     b4MouseDown: function(e) {
20903         var x = e.getPageX();
20904         var y = e.getPageY();
20905         this.autoOffset(x, y);
20906         this.setDragElPos(x, y);
20907     },
20908
20909     // overrides Roo.dd.DragDrop
20910     b4StartDrag: function(x, y) {
20911         // show the drag frame
20912         this.showFrame(x, y);
20913     },
20914
20915     // overrides Roo.dd.DragDrop
20916     b4EndDrag: function(e) {
20917         Roo.fly(this.getDragEl()).hide();
20918     },
20919
20920     // overrides Roo.dd.DragDrop
20921     // By default we try to move the element to the last location of the frame.
20922     // This is so that the default behavior mirrors that of Roo.dd.DD.
20923     endDrag: function(e) {
20924
20925         var lel = this.getEl();
20926         var del = this.getDragEl();
20927
20928         // Show the drag frame briefly so we can get its position
20929         del.style.visibility = "";
20930
20931         this.beforeMove();
20932         // Hide the linked element before the move to get around a Safari
20933         // rendering bug.
20934         lel.style.visibility = "hidden";
20935         Roo.dd.DDM.moveToEl(lel, del);
20936         del.style.visibility = "hidden";
20937         lel.style.visibility = "";
20938
20939         this.afterDrag();
20940     },
20941
20942     beforeMove : function(){
20943
20944     },
20945
20946     afterDrag : function(){
20947
20948     },
20949
20950     toString: function() {
20951         return ("DDProxy " + this.id);
20952     }
20953
20954 });
20955 /*
20956  * Based on:
20957  * Ext JS Library 1.1.1
20958  * Copyright(c) 2006-2007, Ext JS, LLC.
20959  *
20960  * Originally Released Under LGPL - original licence link has changed is not relivant.
20961  *
20962  * Fork - LGPL
20963  * <script type="text/javascript">
20964  */
20965
20966  /**
20967  * @class Roo.dd.DDTarget
20968  * A DragDrop implementation that does not move, but can be a drop
20969  * target.  You would get the same result by simply omitting implementation
20970  * for the event callbacks, but this way we reduce the processing cost of the
20971  * event listener and the callbacks.
20972  * @extends Roo.dd.DragDrop
20973  * @constructor
20974  * @param {String} id the id of the element that is a drop target
20975  * @param {String} sGroup the group of related DragDrop objects
20976  * @param {object} config an object containing configurable attributes
20977  *                 Valid properties for DDTarget in addition to those in
20978  *                 DragDrop:
20979  *                    none
20980  */
20981 Roo.dd.DDTarget = function(id, sGroup, config) {
20982     if (id) {
20983         this.initTarget(id, sGroup, config);
20984     }
20985     if (config.listeners || config.events) { 
20986        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20987             listeners : config.listeners || {}, 
20988             events : config.events || {} 
20989         });    
20990     }
20991 };
20992
20993 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20994 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20995     toString: function() {
20996         return ("DDTarget " + this.id);
20997     }
20998 });
20999 /*
21000  * Based on:
21001  * Ext JS Library 1.1.1
21002  * Copyright(c) 2006-2007, Ext JS, LLC.
21003  *
21004  * Originally Released Under LGPL - original licence link has changed is not relivant.
21005  *
21006  * Fork - LGPL
21007  * <script type="text/javascript">
21008  */
21009  
21010
21011 /**
21012  * @class Roo.dd.ScrollManager
21013  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21014  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21015  * @singleton
21016  */
21017 Roo.dd.ScrollManager = function(){
21018     var ddm = Roo.dd.DragDropMgr;
21019     var els = {};
21020     var dragEl = null;
21021     var proc = {};
21022     
21023     
21024     
21025     var onStop = function(e){
21026         dragEl = null;
21027         clearProc();
21028     };
21029     
21030     var triggerRefresh = function(){
21031         if(ddm.dragCurrent){
21032              ddm.refreshCache(ddm.dragCurrent.groups);
21033         }
21034     };
21035     
21036     var doScroll = function(){
21037         if(ddm.dragCurrent){
21038             var dds = Roo.dd.ScrollManager;
21039             if(!dds.animate){
21040                 if(proc.el.scroll(proc.dir, dds.increment)){
21041                     triggerRefresh();
21042                 }
21043             }else{
21044                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21045             }
21046         }
21047     };
21048     
21049     var clearProc = function(){
21050         if(proc.id){
21051             clearInterval(proc.id);
21052         }
21053         proc.id = 0;
21054         proc.el = null;
21055         proc.dir = "";
21056     };
21057     
21058     var startProc = function(el, dir){
21059          Roo.log('scroll startproc');
21060         clearProc();
21061         proc.el = el;
21062         proc.dir = dir;
21063         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21064     };
21065     
21066     var onFire = function(e, isDrop){
21067        
21068         if(isDrop || !ddm.dragCurrent){ return; }
21069         var dds = Roo.dd.ScrollManager;
21070         if(!dragEl || dragEl != ddm.dragCurrent){
21071             dragEl = ddm.dragCurrent;
21072             // refresh regions on drag start
21073             dds.refreshCache();
21074         }
21075         
21076         var xy = Roo.lib.Event.getXY(e);
21077         var pt = new Roo.lib.Point(xy[0], xy[1]);
21078         for(var id in els){
21079             var el = els[id], r = el._region;
21080             if(r && r.contains(pt) && el.isScrollable()){
21081                 if(r.bottom - pt.y <= dds.thresh){
21082                     if(proc.el != el){
21083                         startProc(el, "down");
21084                     }
21085                     return;
21086                 }else if(r.right - pt.x <= dds.thresh){
21087                     if(proc.el != el){
21088                         startProc(el, "left");
21089                     }
21090                     return;
21091                 }else if(pt.y - r.top <= dds.thresh){
21092                     if(proc.el != el){
21093                         startProc(el, "up");
21094                     }
21095                     return;
21096                 }else if(pt.x - r.left <= dds.thresh){
21097                     if(proc.el != el){
21098                         startProc(el, "right");
21099                     }
21100                     return;
21101                 }
21102             }
21103         }
21104         clearProc();
21105     };
21106     
21107     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21108     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21109     
21110     return {
21111         /**
21112          * Registers new overflow element(s) to auto scroll
21113          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21114          */
21115         register : function(el){
21116             if(el instanceof Array){
21117                 for(var i = 0, len = el.length; i < len; i++) {
21118                         this.register(el[i]);
21119                 }
21120             }else{
21121                 el = Roo.get(el);
21122                 els[el.id] = el;
21123             }
21124             Roo.dd.ScrollManager.els = els;
21125         },
21126         
21127         /**
21128          * Unregisters overflow element(s) so they are no longer scrolled
21129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21130          */
21131         unregister : function(el){
21132             if(el instanceof Array){
21133                 for(var i = 0, len = el.length; i < len; i++) {
21134                         this.unregister(el[i]);
21135                 }
21136             }else{
21137                 el = Roo.get(el);
21138                 delete els[el.id];
21139             }
21140         },
21141         
21142         /**
21143          * The number of pixels from the edge of a container the pointer needs to be to 
21144          * trigger scrolling (defaults to 25)
21145          * @type Number
21146          */
21147         thresh : 25,
21148         
21149         /**
21150          * The number of pixels to scroll in each scroll increment (defaults to 50)
21151          * @type Number
21152          */
21153         increment : 100,
21154         
21155         /**
21156          * The frequency of scrolls in milliseconds (defaults to 500)
21157          * @type Number
21158          */
21159         frequency : 500,
21160         
21161         /**
21162          * True to animate the scroll (defaults to true)
21163          * @type Boolean
21164          */
21165         animate: true,
21166         
21167         /**
21168          * The animation duration in seconds - 
21169          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21170          * @type Number
21171          */
21172         animDuration: .4,
21173         
21174         /**
21175          * Manually trigger a cache refresh.
21176          */
21177         refreshCache : function(){
21178             for(var id in els){
21179                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21180                     els[id]._region = els[id].getRegion();
21181                 }
21182             }
21183         }
21184     };
21185 }();/*
21186  * Based on:
21187  * Ext JS Library 1.1.1
21188  * Copyright(c) 2006-2007, Ext JS, LLC.
21189  *
21190  * Originally Released Under LGPL - original licence link has changed is not relivant.
21191  *
21192  * Fork - LGPL
21193  * <script type="text/javascript">
21194  */
21195  
21196
21197 /**
21198  * @class Roo.dd.Registry
21199  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21200  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21201  * @singleton
21202  */
21203 Roo.dd.Registry = function(){
21204     var elements = {}; 
21205     var handles = {}; 
21206     var autoIdSeed = 0;
21207
21208     var getId = function(el, autogen){
21209         if(typeof el == "string"){
21210             return el;
21211         }
21212         var id = el.id;
21213         if(!id && autogen !== false){
21214             id = "roodd-" + (++autoIdSeed);
21215             el.id = id;
21216         }
21217         return id;
21218     };
21219     
21220     return {
21221     /**
21222      * Register a drag drop element
21223      * @param {String|HTMLElement} element The id or DOM node to register
21224      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21225      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21226      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21227      * populated in the data object (if applicable):
21228      * <pre>
21229 Value      Description<br />
21230 ---------  ------------------------------------------<br />
21231 handles    Array of DOM nodes that trigger dragging<br />
21232            for the element being registered<br />
21233 isHandle   True if the element passed in triggers<br />
21234            dragging itself, else false
21235 </pre>
21236      */
21237         register : function(el, data){
21238             data = data || {};
21239             if(typeof el == "string"){
21240                 el = document.getElementById(el);
21241             }
21242             data.ddel = el;
21243             elements[getId(el)] = data;
21244             if(data.isHandle !== false){
21245                 handles[data.ddel.id] = data;
21246             }
21247             if(data.handles){
21248                 var hs = data.handles;
21249                 for(var i = 0, len = hs.length; i < len; i++){
21250                         handles[getId(hs[i])] = data;
21251                 }
21252             }
21253         },
21254
21255     /**
21256      * Unregister a drag drop element
21257      * @param {String|HTMLElement}  element The id or DOM node to unregister
21258      */
21259         unregister : function(el){
21260             var id = getId(el, false);
21261             var data = elements[id];
21262             if(data){
21263                 delete elements[id];
21264                 if(data.handles){
21265                     var hs = data.handles;
21266                     for(var i = 0, len = hs.length; i < len; i++){
21267                         delete handles[getId(hs[i], false)];
21268                     }
21269                 }
21270             }
21271         },
21272
21273     /**
21274      * Returns the handle registered for a DOM Node by id
21275      * @param {String|HTMLElement} id The DOM node or id to look up
21276      * @return {Object} handle The custom handle data
21277      */
21278         getHandle : function(id){
21279             if(typeof id != "string"){ // must be element?
21280                 id = id.id;
21281             }
21282             return handles[id];
21283         },
21284
21285     /**
21286      * Returns the handle that is registered for the DOM node that is the target of the event
21287      * @param {Event} e The event
21288      * @return {Object} handle The custom handle data
21289      */
21290         getHandleFromEvent : function(e){
21291             var t = Roo.lib.Event.getTarget(e);
21292             return t ? handles[t.id] : null;
21293         },
21294
21295     /**
21296      * Returns a custom data object that is registered for a DOM node by id
21297      * @param {String|HTMLElement} id The DOM node or id to look up
21298      * @return {Object} data The custom data
21299      */
21300         getTarget : function(id){
21301             if(typeof id != "string"){ // must be element?
21302                 id = id.id;
21303             }
21304             return elements[id];
21305         },
21306
21307     /**
21308      * Returns a custom data object that is registered for the DOM node that is the target of the event
21309      * @param {Event} e The event
21310      * @return {Object} data The custom data
21311      */
21312         getTargetFromEvent : function(e){
21313             var t = Roo.lib.Event.getTarget(e);
21314             return t ? elements[t.id] || handles[t.id] : null;
21315         }
21316     };
21317 }();/*
21318  * Based on:
21319  * Ext JS Library 1.1.1
21320  * Copyright(c) 2006-2007, Ext JS, LLC.
21321  *
21322  * Originally Released Under LGPL - original licence link has changed is not relivant.
21323  *
21324  * Fork - LGPL
21325  * <script type="text/javascript">
21326  */
21327  
21328
21329 /**
21330  * @class Roo.dd.StatusProxy
21331  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21332  * default drag proxy used by all Roo.dd components.
21333  * @constructor
21334  * @param {Object} config
21335  */
21336 Roo.dd.StatusProxy = function(config){
21337     Roo.apply(this, config);
21338     this.id = this.id || Roo.id();
21339     this.el = new Roo.Layer({
21340         dh: {
21341             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21342                 {tag: "div", cls: "x-dd-drop-icon"},
21343                 {tag: "div", cls: "x-dd-drag-ghost"}
21344             ]
21345         }, 
21346         shadow: !config || config.shadow !== false
21347     });
21348     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21349     this.dropStatus = this.dropNotAllowed;
21350 };
21351
21352 Roo.dd.StatusProxy.prototype = {
21353     /**
21354      * @cfg {String} dropAllowed
21355      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21356      */
21357     dropAllowed : "x-dd-drop-ok",
21358     /**
21359      * @cfg {String} dropNotAllowed
21360      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21361      */
21362     dropNotAllowed : "x-dd-drop-nodrop",
21363
21364     /**
21365      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21366      * over the current target element.
21367      * @param {String} cssClass The css class for the new drop status indicator image
21368      */
21369     setStatus : function(cssClass){
21370         cssClass = cssClass || this.dropNotAllowed;
21371         if(this.dropStatus != cssClass){
21372             this.el.replaceClass(this.dropStatus, cssClass);
21373             this.dropStatus = cssClass;
21374         }
21375     },
21376
21377     /**
21378      * Resets the status indicator to the default dropNotAllowed value
21379      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21380      */
21381     reset : function(clearGhost){
21382         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21383         this.dropStatus = this.dropNotAllowed;
21384         if(clearGhost){
21385             this.ghost.update("");
21386         }
21387     },
21388
21389     /**
21390      * Updates the contents of the ghost element
21391      * @param {String} html The html that will replace the current innerHTML of the ghost element
21392      */
21393     update : function(html){
21394         if(typeof html == "string"){
21395             this.ghost.update(html);
21396         }else{
21397             this.ghost.update("");
21398             html.style.margin = "0";
21399             this.ghost.dom.appendChild(html);
21400         }
21401         // ensure float = none set?? cant remember why though.
21402         var el = this.ghost.dom.firstChild;
21403                 if(el){
21404                         Roo.fly(el).setStyle('float', 'none');
21405                 }
21406     },
21407     
21408     /**
21409      * Returns the underlying proxy {@link Roo.Layer}
21410      * @return {Roo.Layer} el
21411     */
21412     getEl : function(){
21413         return this.el;
21414     },
21415
21416     /**
21417      * Returns the ghost element
21418      * @return {Roo.Element} el
21419      */
21420     getGhost : function(){
21421         return this.ghost;
21422     },
21423
21424     /**
21425      * Hides the proxy
21426      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21427      */
21428     hide : function(clear){
21429         this.el.hide();
21430         if(clear){
21431             this.reset(true);
21432         }
21433     },
21434
21435     /**
21436      * Stops the repair animation if it's currently running
21437      */
21438     stop : function(){
21439         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21440             this.anim.stop();
21441         }
21442     },
21443
21444     /**
21445      * Displays this proxy
21446      */
21447     show : function(){
21448         this.el.show();
21449     },
21450
21451     /**
21452      * Force the Layer to sync its shadow and shim positions to the element
21453      */
21454     sync : function(){
21455         this.el.sync();
21456     },
21457
21458     /**
21459      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21460      * invalid drop operation by the item being dragged.
21461      * @param {Array} xy The XY position of the element ([x, y])
21462      * @param {Function} callback The function to call after the repair is complete
21463      * @param {Object} scope The scope in which to execute the callback
21464      */
21465     repair : function(xy, callback, scope){
21466         this.callback = callback;
21467         this.scope = scope;
21468         if(xy && this.animRepair !== false){
21469             this.el.addClass("x-dd-drag-repair");
21470             this.el.hideUnders(true);
21471             this.anim = this.el.shift({
21472                 duration: this.repairDuration || .5,
21473                 easing: 'easeOut',
21474                 xy: xy,
21475                 stopFx: true,
21476                 callback: this.afterRepair,
21477                 scope: this
21478             });
21479         }else{
21480             this.afterRepair();
21481         }
21482     },
21483
21484     // private
21485     afterRepair : function(){
21486         this.hide(true);
21487         if(typeof this.callback == "function"){
21488             this.callback.call(this.scope || this);
21489         }
21490         this.callback = null;
21491         this.scope = null;
21492     }
21493 };/*
21494  * Based on:
21495  * Ext JS Library 1.1.1
21496  * Copyright(c) 2006-2007, Ext JS, LLC.
21497  *
21498  * Originally Released Under LGPL - original licence link has changed is not relivant.
21499  *
21500  * Fork - LGPL
21501  * <script type="text/javascript">
21502  */
21503
21504 /**
21505  * @class Roo.dd.DragSource
21506  * @extends Roo.dd.DDProxy
21507  * A simple class that provides the basic implementation needed to make any element draggable.
21508  * @constructor
21509  * @param {String/HTMLElement/Element} el The container element
21510  * @param {Object} config
21511  */
21512 Roo.dd.DragSource = function(el, config){
21513     this.el = Roo.get(el);
21514     this.dragData = {};
21515     
21516     Roo.apply(this, config);
21517     
21518     if(!this.proxy){
21519         this.proxy = new Roo.dd.StatusProxy();
21520     }
21521
21522     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21523           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21524     
21525     this.dragging = false;
21526 };
21527
21528 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21529     /**
21530      * @cfg {String} dropAllowed
21531      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21532      */
21533     dropAllowed : "x-dd-drop-ok",
21534     /**
21535      * @cfg {String} dropNotAllowed
21536      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21537      */
21538     dropNotAllowed : "x-dd-drop-nodrop",
21539
21540     /**
21541      * Returns the data object associated with this drag source
21542      * @return {Object} data An object containing arbitrary data
21543      */
21544     getDragData : function(e){
21545         return this.dragData;
21546     },
21547
21548     // private
21549     onDragEnter : function(e, id){
21550         var target = Roo.dd.DragDropMgr.getDDById(id);
21551         this.cachedTarget = target;
21552         if(this.beforeDragEnter(target, e, id) !== false){
21553             if(target.isNotifyTarget){
21554                 var status = target.notifyEnter(this, e, this.dragData);
21555                 this.proxy.setStatus(status);
21556             }else{
21557                 this.proxy.setStatus(this.dropAllowed);
21558             }
21559             
21560             if(this.afterDragEnter){
21561                 /**
21562                  * An empty function by default, but provided so that you can perform a custom action
21563                  * when the dragged item enters the drop target by providing an implementation.
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                  * @method afterDragEnter
21568                  */
21569                 this.afterDragEnter(target, e, id);
21570             }
21571         }
21572     },
21573
21574     /**
21575      * An empty function by default, but provided so that you can perform a custom action
21576      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21577      * @param {Roo.dd.DragDrop} target The drop target
21578      * @param {Event} e The event object
21579      * @param {String} id The id of the dragged element
21580      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21581      */
21582     beforeDragEnter : function(target, e, id){
21583         return true;
21584     },
21585
21586     // private
21587     alignElWithMouse: function() {
21588         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21589         this.proxy.sync();
21590     },
21591
21592     // private
21593     onDragOver : function(e, id){
21594         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21595         if(this.beforeDragOver(target, e, id) !== false){
21596             if(target.isNotifyTarget){
21597                 var status = target.notifyOver(this, e, this.dragData);
21598                 this.proxy.setStatus(status);
21599             }
21600
21601             if(this.afterDragOver){
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 by providing an implementation.
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                  * @method afterDragOver
21609                  */
21610                 this.afterDragOver(target, e, id);
21611             }
21612         }
21613     },
21614
21615     /**
21616      * An empty function by default, but provided so that you can perform a custom action
21617      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21618      * @param {Roo.dd.DragDrop} target The drop target
21619      * @param {Event} e The event object
21620      * @param {String} id The id of the dragged element
21621      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21622      */
21623     beforeDragOver : function(target, e, id){
21624         return true;
21625     },
21626
21627     // private
21628     onDragOut : function(e, id){
21629         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21630         if(this.beforeDragOut(target, e, id) !== false){
21631             if(target.isNotifyTarget){
21632                 target.notifyOut(this, e, this.dragData);
21633             }
21634             this.proxy.reset();
21635             if(this.afterDragOut){
21636                 /**
21637                  * An empty function by default, but provided so that you can perform a custom action
21638                  * after the dragged item is dragged out of the target without dropping.
21639                  * @param {Roo.dd.DragDrop} target The drop target
21640                  * @param {Event} e The event object
21641                  * @param {String} id The id of the dragged element
21642                  * @method afterDragOut
21643                  */
21644                 this.afterDragOut(target, e, id);
21645             }
21646         }
21647         this.cachedTarget = null;
21648     },
21649
21650     /**
21651      * An empty function by default, but provided so that you can perform a custom action before the dragged
21652      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21653      * @param {Roo.dd.DragDrop} target The drop target
21654      * @param {Event} e The event object
21655      * @param {String} id The id of the dragged element
21656      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21657      */
21658     beforeDragOut : function(target, e, id){
21659         return true;
21660     },
21661     
21662     // private
21663     onDragDrop : function(e, id){
21664         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21665         if(this.beforeDragDrop(target, e, id) !== false){
21666             if(target.isNotifyTarget){
21667                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21668                     this.onValidDrop(target, e, id);
21669                 }else{
21670                     this.onInvalidDrop(target, e, id);
21671                 }
21672             }else{
21673                 this.onValidDrop(target, e, id);
21674             }
21675             
21676             if(this.afterDragDrop){
21677                 /**
21678                  * An empty function by default, but provided so that you can perform a custom action
21679                  * after a valid drag drop has occurred by providing an implementation.
21680                  * @param {Roo.dd.DragDrop} target The drop target
21681                  * @param {Event} e The event object
21682                  * @param {String} id The id of the dropped element
21683                  * @method afterDragDrop
21684                  */
21685                 this.afterDragDrop(target, e, id);
21686             }
21687         }
21688         delete this.cachedTarget;
21689     },
21690
21691     /**
21692      * An empty function by default, but provided so that you can perform a custom action before the dragged
21693      * item is dropped onto the target and optionally cancel the onDragDrop.
21694      * @param {Roo.dd.DragDrop} target The drop target
21695      * @param {Event} e The event object
21696      * @param {String} id The id of the dragged element
21697      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21698      */
21699     beforeDragDrop : function(target, e, id){
21700         return true;
21701     },
21702
21703     // private
21704     onValidDrop : function(target, e, id){
21705         this.hideProxy();
21706         if(this.afterValidDrop){
21707             /**
21708              * An empty function by default, but provided so that you can perform a custom action
21709              * after a valid drop has occurred by providing an implementation.
21710              * @param {Object} target The target DD 
21711              * @param {Event} e The event object
21712              * @param {String} id The id of the dropped element
21713              * @method afterInvalidDrop
21714              */
21715             this.afterValidDrop(target, e, id);
21716         }
21717     },
21718
21719     // private
21720     getRepairXY : function(e, data){
21721         return this.el.getXY();  
21722     },
21723
21724     // private
21725     onInvalidDrop : function(target, e, id){
21726         this.beforeInvalidDrop(target, e, id);
21727         if(this.cachedTarget){
21728             if(this.cachedTarget.isNotifyTarget){
21729                 this.cachedTarget.notifyOut(this, e, this.dragData);
21730             }
21731             this.cacheTarget = null;
21732         }
21733         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21734
21735         if(this.afterInvalidDrop){
21736             /**
21737              * An empty function by default, but provided so that you can perform a custom action
21738              * after an invalid drop has occurred by providing an implementation.
21739              * @param {Event} e The event object
21740              * @param {String} id The id of the dropped element
21741              * @method afterInvalidDrop
21742              */
21743             this.afterInvalidDrop(e, id);
21744         }
21745     },
21746
21747     // private
21748     afterRepair : function(){
21749         if(Roo.enableFx){
21750             this.el.highlight(this.hlColor || "c3daf9");
21751         }
21752         this.dragging = false;
21753     },
21754
21755     /**
21756      * An empty function by default, but provided so that you can perform a custom action after an invalid
21757      * drop has occurred.
21758      * @param {Roo.dd.DragDrop} target The drop target
21759      * @param {Event} e The event object
21760      * @param {String} id The id of the dragged element
21761      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21762      */
21763     beforeInvalidDrop : function(target, e, id){
21764         return true;
21765     },
21766
21767     // private
21768     handleMouseDown : function(e){
21769         if(this.dragging) {
21770             return;
21771         }
21772         var data = this.getDragData(e);
21773         if(data && this.onBeforeDrag(data, e) !== false){
21774             this.dragData = data;
21775             this.proxy.stop();
21776             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21777         } 
21778     },
21779
21780     /**
21781      * An empty function by default, but provided so that you can perform a custom action before the initial
21782      * drag event begins and optionally cancel it.
21783      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21784      * @param {Event} e The event object
21785      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21786      */
21787     onBeforeDrag : function(data, e){
21788         return true;
21789     },
21790
21791     /**
21792      * An empty function by default, but provided so that you can perform a custom action once the initial
21793      * drag event has begun.  The drag cannot be canceled from this function.
21794      * @param {Number} x The x position of the click on the dragged object
21795      * @param {Number} y The y position of the click on the dragged object
21796      */
21797     onStartDrag : Roo.emptyFn,
21798
21799     // private - YUI override
21800     startDrag : function(x, y){
21801         this.proxy.reset();
21802         this.dragging = true;
21803         this.proxy.update("");
21804         this.onInitDrag(x, y);
21805         this.proxy.show();
21806     },
21807
21808     // private
21809     onInitDrag : function(x, y){
21810         var clone = this.el.dom.cloneNode(true);
21811         clone.id = Roo.id(); // prevent duplicate ids
21812         this.proxy.update(clone);
21813         this.onStartDrag(x, y);
21814         return true;
21815     },
21816
21817     /**
21818      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21819      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21820      */
21821     getProxy : function(){
21822         return this.proxy;  
21823     },
21824
21825     /**
21826      * Hides the drag source's {@link Roo.dd.StatusProxy}
21827      */
21828     hideProxy : function(){
21829         this.proxy.hide();  
21830         this.proxy.reset(true);
21831         this.dragging = false;
21832     },
21833
21834     // private
21835     triggerCacheRefresh : function(){
21836         Roo.dd.DDM.refreshCache(this.groups);
21837     },
21838
21839     // private - override to prevent hiding
21840     b4EndDrag: function(e) {
21841     },
21842
21843     // private - override to prevent moving
21844     endDrag : function(e){
21845         this.onEndDrag(this.dragData, e);
21846     },
21847
21848     // private
21849     onEndDrag : function(data, e){
21850     },
21851     
21852     // private - pin to cursor
21853     autoOffset : function(x, y) {
21854         this.setDelta(-12, -20);
21855     }    
21856 });/*
21857  * Based on:
21858  * Ext JS Library 1.1.1
21859  * Copyright(c) 2006-2007, Ext JS, LLC.
21860  *
21861  * Originally Released Under LGPL - original licence link has changed is not relivant.
21862  *
21863  * Fork - LGPL
21864  * <script type="text/javascript">
21865  */
21866
21867
21868 /**
21869  * @class Roo.dd.DropTarget
21870  * @extends Roo.dd.DDTarget
21871  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21872  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21873  * @constructor
21874  * @param {String/HTMLElement/Element} el The container element
21875  * @param {Object} config
21876  */
21877 Roo.dd.DropTarget = function(el, config){
21878     this.el = Roo.get(el);
21879     
21880     var listeners = false; ;
21881     if (config && config.listeners) {
21882         listeners= config.listeners;
21883         delete config.listeners;
21884     }
21885     Roo.apply(this, config);
21886     
21887     if(this.containerScroll){
21888         Roo.dd.ScrollManager.register(this.el);
21889     }
21890     this.addEvents( {
21891          /**
21892          * @scope Roo.dd.DropTarget
21893          */
21894          
21895          /**
21896          * @event enter
21897          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21898          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21899          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21900          * 
21901          * IMPORTANT : it should set this.overClass and this.dropAllowed
21902          * 
21903          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21904          * @param {Event} e The event
21905          * @param {Object} data An object containing arbitrary data supplied by the drag source
21906          */
21907         "enter" : true,
21908         
21909          /**
21910          * @event over
21911          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21912          * This method will be called on every mouse movement while the drag source is over the drop target.
21913          * This default implementation simply returns the dropAllowed config value.
21914          * 
21915          * IMPORTANT : it should set this.dropAllowed
21916          * 
21917          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21918          * @param {Event} e The event
21919          * @param {Object} data An object containing arbitrary data supplied by the drag source
21920          
21921          */
21922         "over" : true,
21923         /**
21924          * @event out
21925          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21926          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21927          * overClass (if any) from the drop element.
21928          * 
21929          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21930          * @param {Event} e The event
21931          * @param {Object} data An object containing arbitrary data supplied by the drag source
21932          */
21933          "out" : true,
21934          
21935         /**
21936          * @event drop
21937          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21938          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21939          * implementation that does something to process the drop event and returns true so that the drag source's
21940          * repair action does not run.
21941          * 
21942          * IMPORTANT : it should set this.success
21943          * 
21944          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21945          * @param {Event} e The event
21946          * @param {Object} data An object containing arbitrary data supplied by the drag source
21947         */
21948          "drop" : true
21949     });
21950             
21951      
21952     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21953         this.el.dom, 
21954         this.ddGroup || this.group,
21955         {
21956             isTarget: true,
21957             listeners : listeners || {} 
21958            
21959         
21960         }
21961     );
21962
21963 };
21964
21965 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21966     /**
21967      * @cfg {String} overClass
21968      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21969      */
21970      /**
21971      * @cfg {String} ddGroup
21972      * The drag drop group to handle drop events for
21973      */
21974      
21975     /**
21976      * @cfg {String} dropAllowed
21977      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21978      */
21979     dropAllowed : "x-dd-drop-ok",
21980     /**
21981      * @cfg {String} dropNotAllowed
21982      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21983      */
21984     dropNotAllowed : "x-dd-drop-nodrop",
21985     /**
21986      * @cfg {boolean} success
21987      * set this after drop listener.. 
21988      */
21989     success : false,
21990     /**
21991      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21992      * if the drop point is valid for over/enter..
21993      */
21994     valid : false,
21995     // private
21996     isTarget : true,
21997
21998     // private
21999     isNotifyTarget : true,
22000     
22001     /**
22002      * @hide
22003      */
22004     notifyEnter : function(dd, e, data)
22005     {
22006         this.valid = true;
22007         this.fireEvent('enter', dd, e, data);
22008         if(this.overClass){
22009             this.el.addClass(this.overClass);
22010         }
22011         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22012             this.valid ? this.dropAllowed : this.dropNotAllowed
22013         );
22014     },
22015
22016     /**
22017      * @hide
22018      */
22019     notifyOver : function(dd, e, data)
22020     {
22021         this.valid = true;
22022         this.fireEvent('over', dd, e, data);
22023         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22024             this.valid ? this.dropAllowed : this.dropNotAllowed
22025         );
22026     },
22027
22028     /**
22029      * @hide
22030      */
22031     notifyOut : function(dd, e, data)
22032     {
22033         this.fireEvent('out', dd, e, data);
22034         if(this.overClass){
22035             this.el.removeClass(this.overClass);
22036         }
22037     },
22038
22039     /**
22040      * @hide
22041      */
22042     notifyDrop : function(dd, e, data)
22043     {
22044         this.success = false;
22045         this.fireEvent('drop', dd, e, data);
22046         return this.success;
22047     }
22048 });/*
22049  * Based on:
22050  * Ext JS Library 1.1.1
22051  * Copyright(c) 2006-2007, Ext JS, LLC.
22052  *
22053  * Originally Released Under LGPL - original licence link has changed is not relivant.
22054  *
22055  * Fork - LGPL
22056  * <script type="text/javascript">
22057  */
22058
22059
22060 /**
22061  * @class Roo.dd.DragZone
22062  * @extends Roo.dd.DragSource
22063  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22064  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22065  * @constructor
22066  * @param {String/HTMLElement/Element} el The container element
22067  * @param {Object} config
22068  */
22069 Roo.dd.DragZone = function(el, config){
22070     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22071     if(this.containerScroll){
22072         Roo.dd.ScrollManager.register(this.el);
22073     }
22074 };
22075
22076 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22077     /**
22078      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22079      * for auto scrolling during drag operations.
22080      */
22081     /**
22082      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22083      * method after a failed drop (defaults to "c3daf9" - light blue)
22084      */
22085
22086     /**
22087      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22088      * for a valid target to drag based on the mouse down. Override this method
22089      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22090      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22091      * @param {EventObject} e The mouse down event
22092      * @return {Object} The dragData
22093      */
22094     getDragData : function(e){
22095         return Roo.dd.Registry.getHandleFromEvent(e);
22096     },
22097     
22098     /**
22099      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22100      * this.dragData.ddel
22101      * @param {Number} x The x position of the click on the dragged object
22102      * @param {Number} y The y position of the click on the dragged object
22103      * @return {Boolean} true to continue the drag, false to cancel
22104      */
22105     onInitDrag : function(x, y){
22106         this.proxy.update(this.dragData.ddel.cloneNode(true));
22107         this.onStartDrag(x, y);
22108         return true;
22109     },
22110     
22111     /**
22112      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22113      */
22114     afterRepair : function(){
22115         if(Roo.enableFx){
22116             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22117         }
22118         this.dragging = false;
22119     },
22120
22121     /**
22122      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22123      * the XY of this.dragData.ddel
22124      * @param {EventObject} e The mouse up event
22125      * @return {Array} The xy location (e.g. [100, 200])
22126      */
22127     getRepairXY : function(e){
22128         return Roo.Element.fly(this.dragData.ddel).getXY();  
22129     }
22130 });/*
22131  * Based on:
22132  * Ext JS Library 1.1.1
22133  * Copyright(c) 2006-2007, Ext JS, LLC.
22134  *
22135  * Originally Released Under LGPL - original licence link has changed is not relivant.
22136  *
22137  * Fork - LGPL
22138  * <script type="text/javascript">
22139  */
22140 /**
22141  * @class Roo.dd.DropZone
22142  * @extends Roo.dd.DropTarget
22143  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22144  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22145  * @constructor
22146  * @param {String/HTMLElement/Element} el The container element
22147  * @param {Object} config
22148  */
22149 Roo.dd.DropZone = function(el, config){
22150     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22151 };
22152
22153 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22154     /**
22155      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22156      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22157      * provide your own custom lookup.
22158      * @param {Event} e The event
22159      * @return {Object} data The custom data
22160      */
22161     getTargetFromEvent : function(e){
22162         return Roo.dd.Registry.getTargetFromEvent(e);
22163     },
22164
22165     /**
22166      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22167      * that it has registered.  This method has no default implementation and should be overridden to provide
22168      * node-specific processing if necessary.
22169      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22170      * {@link #getTargetFromEvent} for this node)
22171      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22172      * @param {Event} e The event
22173      * @param {Object} data An object containing arbitrary data supplied by the drag source
22174      */
22175     onNodeEnter : function(n, dd, e, data){
22176         
22177     },
22178
22179     /**
22180      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22181      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22182      * overridden to provide the proper feedback.
22183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22184      * {@link #getTargetFromEvent} for this node)
22185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22186      * @param {Event} e The event
22187      * @param {Object} data An object containing arbitrary data supplied by the drag source
22188      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22189      * underlying {@link Roo.dd.StatusProxy} can be updated
22190      */
22191     onNodeOver : function(n, dd, e, data){
22192         return this.dropAllowed;
22193     },
22194
22195     /**
22196      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22197      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22198      * node-specific processing if necessary.
22199      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22200      * {@link #getTargetFromEvent} for this node)
22201      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22202      * @param {Event} e The event
22203      * @param {Object} data An object containing arbitrary data supplied by the drag source
22204      */
22205     onNodeOut : function(n, dd, e, data){
22206         
22207     },
22208
22209     /**
22210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22211      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22212      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22214      * {@link #getTargetFromEvent} for this node)
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 {Boolean} True if the drop was valid, else false
22219      */
22220     onNodeDrop : function(n, dd, e, data){
22221         return false;
22222     },
22223
22224     /**
22225      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22226      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22227      * it should be overridden to provide the proper feedback if necessary.
22228      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22229      * @param {Event} e The event
22230      * @param {Object} data An object containing arbitrary data supplied by the drag source
22231      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22232      * underlying {@link Roo.dd.StatusProxy} can be updated
22233      */
22234     onContainerOver : function(dd, e, data){
22235         return this.dropNotAllowed;
22236     },
22237
22238     /**
22239      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22240      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22241      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22242      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22243      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22244      * @param {Event} e The event
22245      * @param {Object} data An object containing arbitrary data supplied by the drag source
22246      * @return {Boolean} True if the drop was valid, else false
22247      */
22248     onContainerDrop : function(dd, e, data){
22249         return false;
22250     },
22251
22252     /**
22253      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22254      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22255      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22256      * you should override this method and provide a custom implementation.
22257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22258      * @param {Event} e The event
22259      * @param {Object} data An object containing arbitrary data supplied by the drag source
22260      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22261      * underlying {@link Roo.dd.StatusProxy} can be updated
22262      */
22263     notifyEnter : function(dd, e, data){
22264         return this.dropNotAllowed;
22265     },
22266
22267     /**
22268      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22269      * This method will be called on every mouse movement while the drag source is over the drop zone.
22270      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22271      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22272      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22273      * registered node, it will call {@link #onContainerOver}.
22274      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22275      * @param {Event} e The event
22276      * @param {Object} data An object containing arbitrary data supplied by the drag source
22277      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22278      * underlying {@link Roo.dd.StatusProxy} can be updated
22279      */
22280     notifyOver : function(dd, e, data){
22281         var n = this.getTargetFromEvent(e);
22282         if(!n){ // not over valid drop target
22283             if(this.lastOverNode){
22284                 this.onNodeOut(this.lastOverNode, dd, e, data);
22285                 this.lastOverNode = null;
22286             }
22287             return this.onContainerOver(dd, e, data);
22288         }
22289         if(this.lastOverNode != n){
22290             if(this.lastOverNode){
22291                 this.onNodeOut(this.lastOverNode, dd, e, data);
22292             }
22293             this.onNodeEnter(n, dd, e, data);
22294             this.lastOverNode = n;
22295         }
22296         return this.onNodeOver(n, dd, e, data);
22297     },
22298
22299     /**
22300      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22301      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22302      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22303      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22304      * @param {Event} e The event
22305      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22306      */
22307     notifyOut : function(dd, e, data){
22308         if(this.lastOverNode){
22309             this.onNodeOut(this.lastOverNode, dd, e, data);
22310             this.lastOverNode = null;
22311         }
22312     },
22313
22314     /**
22315      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22316      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22317      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22318      * otherwise it will call {@link #onContainerDrop}.
22319      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22320      * @param {Event} e The event
22321      * @param {Object} data An object containing arbitrary data supplied by the drag source
22322      * @return {Boolean} True if the drop was valid, else false
22323      */
22324     notifyDrop : function(dd, e, data){
22325         if(this.lastOverNode){
22326             this.onNodeOut(this.lastOverNode, dd, e, data);
22327             this.lastOverNode = null;
22328         }
22329         var n = this.getTargetFromEvent(e);
22330         return n ?
22331             this.onNodeDrop(n, dd, e, data) :
22332             this.onContainerDrop(dd, e, data);
22333     },
22334
22335     // private
22336     triggerCacheRefresh : function(){
22337         Roo.dd.DDM.refreshCache(this.groups);
22338     }  
22339 });/*
22340  * Based on:
22341  * Ext JS Library 1.1.1
22342  * Copyright(c) 2006-2007, Ext JS, LLC.
22343  *
22344  * Originally Released Under LGPL - original licence link has changed is not relivant.
22345  *
22346  * Fork - LGPL
22347  * <script type="text/javascript">
22348  */
22349
22350
22351 /**
22352  * @class Roo.data.SortTypes
22353  * @singleton
22354  * Defines the default sorting (casting?) comparison functions used when sorting data.
22355  */
22356 Roo.data.SortTypes = {
22357     /**
22358      * Default sort that does nothing
22359      * @param {Mixed} s The value being converted
22360      * @return {Mixed} The comparison value
22361      */
22362     none : function(s){
22363         return s;
22364     },
22365     
22366     /**
22367      * The regular expression used to strip tags
22368      * @type {RegExp}
22369      * @property
22370      */
22371     stripTagsRE : /<\/?[^>]+>/gi,
22372     
22373     /**
22374      * Strips all HTML tags to sort on text only
22375      * @param {Mixed} s The value being converted
22376      * @return {String} The comparison value
22377      */
22378     asText : function(s){
22379         return String(s).replace(this.stripTagsRE, "");
22380     },
22381     
22382     /**
22383      * Strips all HTML tags to sort on text only - Case insensitive
22384      * @param {Mixed} s The value being converted
22385      * @return {String} The comparison value
22386      */
22387     asUCText : function(s){
22388         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22389     },
22390     
22391     /**
22392      * Case insensitive string
22393      * @param {Mixed} s The value being converted
22394      * @return {String} The comparison value
22395      */
22396     asUCString : function(s) {
22397         return String(s).toUpperCase();
22398     },
22399     
22400     /**
22401      * Date sorting
22402      * @param {Mixed} s The value being converted
22403      * @return {Number} The comparison value
22404      */
22405     asDate : function(s) {
22406         if(!s){
22407             return 0;
22408         }
22409         if(s instanceof Date){
22410             return s.getTime();
22411         }
22412         return Date.parse(String(s));
22413     },
22414     
22415     /**
22416      * Float sorting
22417      * @param {Mixed} s The value being converted
22418      * @return {Float} The comparison value
22419      */
22420     asFloat : function(s) {
22421         var val = parseFloat(String(s).replace(/,/g, ""));
22422         if(isNaN(val)) {
22423             val = 0;
22424         }
22425         return val;
22426     },
22427     
22428     /**
22429      * Integer sorting
22430      * @param {Mixed} s The value being converted
22431      * @return {Number} The comparison value
22432      */
22433     asInt : function(s) {
22434         var val = parseInt(String(s).replace(/,/g, ""));
22435         if(isNaN(val)) {
22436             val = 0;
22437         }
22438         return val;
22439     }
22440 };/*
22441  * Based on:
22442  * Ext JS Library 1.1.1
22443  * Copyright(c) 2006-2007, Ext JS, LLC.
22444  *
22445  * Originally Released Under LGPL - original licence link has changed is not relivant.
22446  *
22447  * Fork - LGPL
22448  * <script type="text/javascript">
22449  */
22450
22451 /**
22452 * @class Roo.data.Record
22453  * Instances of this class encapsulate both record <em>definition</em> information, and record
22454  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22455  * to access Records cached in an {@link Roo.data.Store} object.<br>
22456  * <p>
22457  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22458  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22459  * objects.<br>
22460  * <p>
22461  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22462  * @constructor
22463  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22464  * {@link #create}. The parameters are the same.
22465  * @param {Array} data An associative Array of data values keyed by the field name.
22466  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22467  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22468  * not specified an integer id is generated.
22469  */
22470 Roo.data.Record = function(data, id){
22471     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22472     this.data = data;
22473 };
22474
22475 /**
22476  * Generate a constructor for a specific record layout.
22477  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22478  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22479  * Each field definition object may contain the following properties: <ul>
22480  * <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,
22481  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22482  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22483  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22484  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22485  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22486  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22487  * this may be omitted.</p></li>
22488  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22489  * <ul><li>auto (Default, implies no conversion)</li>
22490  * <li>string</li>
22491  * <li>int</li>
22492  * <li>float</li>
22493  * <li>boolean</li>
22494  * <li>date</li></ul></p></li>
22495  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22496  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22497  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22498  * by the Reader into an object that will be stored in the Record. It is passed the
22499  * following parameters:<ul>
22500  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22501  * </ul></p></li>
22502  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22503  * </ul>
22504  * <br>usage:<br><pre><code>
22505 var TopicRecord = Roo.data.Record.create(
22506     {name: 'title', mapping: 'topic_title'},
22507     {name: 'author', mapping: 'username'},
22508     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22509     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22510     {name: 'lastPoster', mapping: 'user2'},
22511     {name: 'excerpt', mapping: 'post_text'}
22512 );
22513
22514 var myNewRecord = new TopicRecord({
22515     title: 'Do my job please',
22516     author: 'noobie',
22517     totalPosts: 1,
22518     lastPost: new Date(),
22519     lastPoster: 'Animal',
22520     excerpt: 'No way dude!'
22521 });
22522 myStore.add(myNewRecord);
22523 </code></pre>
22524  * @method create
22525  * @static
22526  */
22527 Roo.data.Record.create = function(o){
22528     var f = function(){
22529         f.superclass.constructor.apply(this, arguments);
22530     };
22531     Roo.extend(f, Roo.data.Record);
22532     var p = f.prototype;
22533     p.fields = new Roo.util.MixedCollection(false, function(field){
22534         return field.name;
22535     });
22536     for(var i = 0, len = o.length; i < len; i++){
22537         p.fields.add(new Roo.data.Field(o[i]));
22538     }
22539     f.getField = function(name){
22540         return p.fields.get(name);  
22541     };
22542     return f;
22543 };
22544
22545 Roo.data.Record.AUTO_ID = 1000;
22546 Roo.data.Record.EDIT = 'edit';
22547 Roo.data.Record.REJECT = 'reject';
22548 Roo.data.Record.COMMIT = 'commit';
22549
22550 Roo.data.Record.prototype = {
22551     /**
22552      * Readonly flag - true if this record has been modified.
22553      * @type Boolean
22554      */
22555     dirty : false,
22556     editing : false,
22557     error: null,
22558     modified: null,
22559
22560     // private
22561     join : function(store){
22562         this.store = store;
22563     },
22564
22565     /**
22566      * Set the named field to the specified value.
22567      * @param {String} name The name of the field to set.
22568      * @param {Object} value The value to set the field to.
22569      */
22570     set : function(name, value){
22571         if(this.data[name] == value){
22572             return;
22573         }
22574         this.dirty = true;
22575         if(!this.modified){
22576             this.modified = {};
22577         }
22578         if(typeof this.modified[name] == 'undefined'){
22579             this.modified[name] = this.data[name];
22580         }
22581         this.data[name] = value;
22582         if(!this.editing && this.store){
22583             this.store.afterEdit(this);
22584         }       
22585     },
22586
22587     /**
22588      * Get the value of the named field.
22589      * @param {String} name The name of the field to get the value of.
22590      * @return {Object} The value of the field.
22591      */
22592     get : function(name){
22593         return this.data[name]; 
22594     },
22595
22596     // private
22597     beginEdit : function(){
22598         this.editing = true;
22599         this.modified = {}; 
22600     },
22601
22602     // private
22603     cancelEdit : function(){
22604         this.editing = false;
22605         delete this.modified;
22606     },
22607
22608     // private
22609     endEdit : function(){
22610         this.editing = false;
22611         if(this.dirty && this.store){
22612             this.store.afterEdit(this);
22613         }
22614     },
22615
22616     /**
22617      * Usually called by the {@link Roo.data.Store} which owns the Record.
22618      * Rejects all changes made to the Record since either creation, or the last commit operation.
22619      * Modified fields are reverted to their original values.
22620      * <p>
22621      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22622      * of reject operations.
22623      */
22624     reject : function(){
22625         var m = this.modified;
22626         for(var n in m){
22627             if(typeof m[n] != "function"){
22628                 this.data[n] = m[n];
22629             }
22630         }
22631         this.dirty = false;
22632         delete this.modified;
22633         this.editing = false;
22634         if(this.store){
22635             this.store.afterReject(this);
22636         }
22637     },
22638
22639     /**
22640      * Usually called by the {@link Roo.data.Store} which owns the Record.
22641      * Commits all changes made to the Record since either creation, or the last commit operation.
22642      * <p>
22643      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22644      * of commit operations.
22645      */
22646     commit : function(){
22647         this.dirty = false;
22648         delete this.modified;
22649         this.editing = false;
22650         if(this.store){
22651             this.store.afterCommit(this);
22652         }
22653     },
22654
22655     // private
22656     hasError : function(){
22657         return this.error != null;
22658     },
22659
22660     // private
22661     clearError : function(){
22662         this.error = null;
22663     },
22664
22665     /**
22666      * Creates a copy of this record.
22667      * @param {String} id (optional) A new record id if you don't want to use this record's id
22668      * @return {Record}
22669      */
22670     copy : function(newId) {
22671         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22672     }
22673 };/*
22674  * Based on:
22675  * Ext JS Library 1.1.1
22676  * Copyright(c) 2006-2007, Ext JS, LLC.
22677  *
22678  * Originally Released Under LGPL - original licence link has changed is not relivant.
22679  *
22680  * Fork - LGPL
22681  * <script type="text/javascript">
22682  */
22683
22684
22685
22686 /**
22687  * @class Roo.data.Store
22688  * @extends Roo.util.Observable
22689  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22690  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22691  * <p>
22692  * 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
22693  * has no knowledge of the format of the data returned by the Proxy.<br>
22694  * <p>
22695  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22696  * instances from the data object. These records are cached and made available through accessor functions.
22697  * @constructor
22698  * Creates a new Store.
22699  * @param {Object} config A config object containing the objects needed for the Store to access data,
22700  * and read the data into Records.
22701  */
22702 Roo.data.Store = function(config){
22703     this.data = new Roo.util.MixedCollection(false);
22704     this.data.getKey = function(o){
22705         return o.id;
22706     };
22707     this.baseParams = {};
22708     // private
22709     this.paramNames = {
22710         "start" : "start",
22711         "limit" : "limit",
22712         "sort" : "sort",
22713         "dir" : "dir",
22714         "multisort" : "_multisort"
22715     };
22716
22717     if(config && config.data){
22718         this.inlineData = config.data;
22719         delete config.data;
22720     }
22721
22722     Roo.apply(this, config);
22723     
22724     if(this.reader){ // reader passed
22725         this.reader = Roo.factory(this.reader, Roo.data);
22726         this.reader.xmodule = this.xmodule || false;
22727         if(!this.recordType){
22728             this.recordType = this.reader.recordType;
22729         }
22730         if(this.reader.onMetaChange){
22731             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22732         }
22733     }
22734
22735     if(this.recordType){
22736         this.fields = this.recordType.prototype.fields;
22737     }
22738     this.modified = [];
22739
22740     this.addEvents({
22741         /**
22742          * @event datachanged
22743          * Fires when the data cache has changed, and a widget which is using this Store
22744          * as a Record cache should refresh its view.
22745          * @param {Store} this
22746          */
22747         datachanged : true,
22748         /**
22749          * @event metachange
22750          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22751          * @param {Store} this
22752          * @param {Object} meta The JSON metadata
22753          */
22754         metachange : true,
22755         /**
22756          * @event add
22757          * Fires when Records have been added to the Store
22758          * @param {Store} this
22759          * @param {Roo.data.Record[]} records The array of Records added
22760          * @param {Number} index The index at which the record(s) were added
22761          */
22762         add : true,
22763         /**
22764          * @event remove
22765          * Fires when a Record has been removed from the Store
22766          * @param {Store} this
22767          * @param {Roo.data.Record} record The Record that was removed
22768          * @param {Number} index The index at which the record was removed
22769          */
22770         remove : true,
22771         /**
22772          * @event update
22773          * Fires when a Record has been updated
22774          * @param {Store} this
22775          * @param {Roo.data.Record} record The Record that was updated
22776          * @param {String} operation The update operation being performed.  Value may be one of:
22777          * <pre><code>
22778  Roo.data.Record.EDIT
22779  Roo.data.Record.REJECT
22780  Roo.data.Record.COMMIT
22781          * </code></pre>
22782          */
22783         update : true,
22784         /**
22785          * @event clear
22786          * Fires when the data cache has been cleared.
22787          * @param {Store} this
22788          */
22789         clear : true,
22790         /**
22791          * @event beforeload
22792          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22793          * the load action will be canceled.
22794          * @param {Store} this
22795          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22796          */
22797         beforeload : true,
22798         /**
22799          * @event beforeloadadd
22800          * Fires after a new set of Records has been loaded.
22801          * @param {Store} this
22802          * @param {Roo.data.Record[]} records The Records that were loaded
22803          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22804          */
22805         beforeloadadd : true,
22806         /**
22807          * @event load
22808          * Fires after a new set of Records has been loaded, before they are added to the store.
22809          * @param {Store} this
22810          * @param {Roo.data.Record[]} records The Records that were loaded
22811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22812          * @params {Object} return from reader
22813          */
22814         load : true,
22815         /**
22816          * @event loadexception
22817          * Fires if an exception occurs in the Proxy during loading.
22818          * Called with the signature of the Proxy's "loadexception" event.
22819          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22820          * 
22821          * @param {Proxy} 
22822          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22823          * @param {Object} load options 
22824          * @param {Object} jsonData from your request (normally this contains the Exception)
22825          */
22826         loadexception : true
22827     });
22828     
22829     if(this.proxy){
22830         this.proxy = Roo.factory(this.proxy, Roo.data);
22831         this.proxy.xmodule = this.xmodule || false;
22832         this.relayEvents(this.proxy,  ["loadexception"]);
22833     }
22834     this.sortToggle = {};
22835     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22836
22837     Roo.data.Store.superclass.constructor.call(this);
22838
22839     if(this.inlineData){
22840         this.loadData(this.inlineData);
22841         delete this.inlineData;
22842     }
22843 };
22844
22845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22846      /**
22847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22848     * without a remote query - used by combo/forms at present.
22849     */
22850     
22851     /**
22852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22853     */
22854     /**
22855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22856     */
22857     /**
22858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22860     */
22861     /**
22862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22863     * on any HTTP request
22864     */
22865     /**
22866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22867     */
22868     /**
22869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22870     */
22871     multiSort: false,
22872     /**
22873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22875     */
22876     remoteSort : false,
22877
22878     /**
22879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22880      * loaded or when a record is removed. (defaults to false).
22881     */
22882     pruneModifiedRecords : false,
22883
22884     // private
22885     lastOptions : null,
22886
22887     /**
22888      * Add Records to the Store and fires the add event.
22889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22890      */
22891     add : function(records){
22892         records = [].concat(records);
22893         for(var i = 0, len = records.length; i < len; i++){
22894             records[i].join(this);
22895         }
22896         var index = this.data.length;
22897         this.data.addAll(records);
22898         this.fireEvent("add", this, records, index);
22899     },
22900
22901     /**
22902      * Remove a Record from the Store and fires the remove event.
22903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22904      */
22905     remove : function(record){
22906         var index = this.data.indexOf(record);
22907         this.data.removeAt(index);
22908         if(this.pruneModifiedRecords){
22909             this.modified.remove(record);
22910         }
22911         this.fireEvent("remove", this, record, index);
22912     },
22913
22914     /**
22915      * Remove all Records from the Store and fires the clear event.
22916      */
22917     removeAll : function(){
22918         this.data.clear();
22919         if(this.pruneModifiedRecords){
22920             this.modified = [];
22921         }
22922         this.fireEvent("clear", this);
22923     },
22924
22925     /**
22926      * Inserts Records to the Store at the given index and fires the add event.
22927      * @param {Number} index The start index at which to insert the passed Records.
22928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22929      */
22930     insert : function(index, records){
22931         records = [].concat(records);
22932         for(var i = 0, len = records.length; i < len; i++){
22933             this.data.insert(index, records[i]);
22934             records[i].join(this);
22935         }
22936         this.fireEvent("add", this, records, index);
22937     },
22938
22939     /**
22940      * Get the index within the cache of the passed Record.
22941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22942      * @return {Number} The index of the passed Record. Returns -1 if not found.
22943      */
22944     indexOf : function(record){
22945         return this.data.indexOf(record);
22946     },
22947
22948     /**
22949      * Get the index within the cache of the Record with the passed id.
22950      * @param {String} id The id of the Record to find.
22951      * @return {Number} The index of the Record. Returns -1 if not found.
22952      */
22953     indexOfId : function(id){
22954         return this.data.indexOfKey(id);
22955     },
22956
22957     /**
22958      * Get the Record with the specified id.
22959      * @param {String} id The id of the Record to find.
22960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22961      */
22962     getById : function(id){
22963         return this.data.key(id);
22964     },
22965
22966     /**
22967      * Get the Record at the specified index.
22968      * @param {Number} index The index of the Record to find.
22969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22970      */
22971     getAt : function(index){
22972         return this.data.itemAt(index);
22973     },
22974
22975     /**
22976      * Returns a range of Records between specified indices.
22977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22979      * @return {Roo.data.Record[]} An array of Records
22980      */
22981     getRange : function(start, end){
22982         return this.data.getRange(start, end);
22983     },
22984
22985     // private
22986     storeOptions : function(o){
22987         o = Roo.apply({}, o);
22988         delete o.callback;
22989         delete o.scope;
22990         this.lastOptions = o;
22991     },
22992
22993     /**
22994      * Loads the Record cache from the configured Proxy using the configured Reader.
22995      * <p>
22996      * If using remote paging, then the first load call must specify the <em>start</em>
22997      * and <em>limit</em> properties in the options.params property to establish the initial
22998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22999      * <p>
23000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23001      * and this call will return before the new data has been loaded. Perform any post-processing
23002      * in a callback function, or in a "load" event handler.</strong>
23003      * <p>
23004      * @param {Object} options An object containing properties which control loading options:<ul>
23005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23007      * passed the following arguments:<ul>
23008      * <li>r : Roo.data.Record[]</li>
23009      * <li>options: Options object from the load call</li>
23010      * <li>success: Boolean success indicator</li></ul></li>
23011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23013      * </ul>
23014      */
23015     load : function(options){
23016         options = options || {};
23017         if(this.fireEvent("beforeload", this, options) !== false){
23018             this.storeOptions(options);
23019             var p = Roo.apply(options.params || {}, this.baseParams);
23020             // if meta was not loaded from remote source.. try requesting it.
23021             if (!this.reader.metaFromRemote) {
23022                 p._requestMeta = 1;
23023             }
23024             if(this.sortInfo && this.remoteSort){
23025                 var pn = this.paramNames;
23026                 p[pn["sort"]] = this.sortInfo.field;
23027                 p[pn["dir"]] = this.sortInfo.direction;
23028             }
23029             if (this.multiSort) {
23030                 var pn = this.paramNames;
23031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23032             }
23033             
23034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23035         }
23036     },
23037
23038     /**
23039      * Reloads the Record cache from the configured Proxy using the configured Reader and
23040      * the options from the last load operation performed.
23041      * @param {Object} options (optional) An object containing properties which may override the options
23042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23043      * the most recently used options are reused).
23044      */
23045     reload : function(options){
23046         this.load(Roo.applyIf(options||{}, this.lastOptions));
23047     },
23048
23049     // private
23050     // Called as a callback by the Reader during a load operation.
23051     loadRecords : function(o, options, success){
23052         if(!o || success === false){
23053             if(success !== false){
23054                 this.fireEvent("load", this, [], options, o);
23055             }
23056             if(options.callback){
23057                 options.callback.call(options.scope || this, [], options, false);
23058             }
23059             return;
23060         }
23061         // if data returned failure - throw an exception.
23062         if (o.success === false) {
23063             // show a message if no listener is registered.
23064             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23065                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23066             }
23067             // loadmask wil be hooked into this..
23068             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23069             return;
23070         }
23071         var r = o.records, t = o.totalRecords || r.length;
23072         
23073         this.fireEvent("beforeloadadd", this, r, options, o);
23074         
23075         if(!options || options.add !== true){
23076             if(this.pruneModifiedRecords){
23077                 this.modified = [];
23078             }
23079             for(var i = 0, len = r.length; i < len; i++){
23080                 r[i].join(this);
23081             }
23082             if(this.snapshot){
23083                 this.data = this.snapshot;
23084                 delete this.snapshot;
23085             }
23086             this.data.clear();
23087             this.data.addAll(r);
23088             this.totalLength = t;
23089             this.applySort();
23090             this.fireEvent("datachanged", this);
23091         }else{
23092             this.totalLength = Math.max(t, this.data.length+r.length);
23093             this.add(r);
23094         }
23095         
23096         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23097                 
23098             var e = new Roo.data.Record({});
23099
23100             e.set(this.parent.displayField, this.parent.emptyTitle);
23101             e.set(this.parent.valueField, '');
23102
23103             this.insert(0, e);
23104         }
23105             
23106         this.fireEvent("load", this, r, options, o);
23107         if(options.callback){
23108             options.callback.call(options.scope || this, r, options, true);
23109         }
23110     },
23111
23112
23113     /**
23114      * Loads data from a passed data block. A Reader which understands the format of the data
23115      * must have been configured in the constructor.
23116      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23117      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23118      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23119      */
23120     loadData : function(o, append){
23121         var r = this.reader.readRecords(o);
23122         this.loadRecords(r, {add: append}, true);
23123     },
23124
23125     /**
23126      * Gets the number of cached records.
23127      * <p>
23128      * <em>If using paging, this may not be the total size of the dataset. If the data object
23129      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23130      * the data set size</em>
23131      */
23132     getCount : function(){
23133         return this.data.length || 0;
23134     },
23135
23136     /**
23137      * Gets the total number of records in the dataset as returned by the server.
23138      * <p>
23139      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23140      * the dataset size</em>
23141      */
23142     getTotalCount : function(){
23143         return this.totalLength || 0;
23144     },
23145
23146     /**
23147      * Returns the sort state of the Store as an object with two properties:
23148      * <pre><code>
23149  field {String} The name of the field by which the Records are sorted
23150  direction {String} The sort order, "ASC" or "DESC"
23151      * </code></pre>
23152      */
23153     getSortState : function(){
23154         return this.sortInfo;
23155     },
23156
23157     // private
23158     applySort : function(){
23159         if(this.sortInfo && !this.remoteSort){
23160             var s = this.sortInfo, f = s.field;
23161             var st = this.fields.get(f).sortType;
23162             var fn = function(r1, r2){
23163                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23164                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23165             };
23166             this.data.sort(s.direction, fn);
23167             if(this.snapshot && this.snapshot != this.data){
23168                 this.snapshot.sort(s.direction, fn);
23169             }
23170         }
23171     },
23172
23173     /**
23174      * Sets the default sort column and order to be used by the next load operation.
23175      * @param {String} fieldName The name of the field to sort by.
23176      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23177      */
23178     setDefaultSort : function(field, dir){
23179         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23180     },
23181
23182     /**
23183      * Sort the Records.
23184      * If remote sorting is used, the sort is performed on the server, and the cache is
23185      * reloaded. If local sorting is used, the cache is sorted internally.
23186      * @param {String} fieldName The name of the field to sort by.
23187      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23188      */
23189     sort : function(fieldName, dir){
23190         var f = this.fields.get(fieldName);
23191         if(!dir){
23192             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23193             
23194             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23195                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23196             }else{
23197                 dir = f.sortDir;
23198             }
23199         }
23200         this.sortToggle[f.name] = dir;
23201         this.sortInfo = {field: f.name, direction: dir};
23202         if(!this.remoteSort){
23203             this.applySort();
23204             this.fireEvent("datachanged", this);
23205         }else{
23206             this.load(this.lastOptions);
23207         }
23208     },
23209
23210     /**
23211      * Calls the specified function for each of the Records in the cache.
23212      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23213      * Returning <em>false</em> aborts and exits the iteration.
23214      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23215      */
23216     each : function(fn, scope){
23217         this.data.each(fn, scope);
23218     },
23219
23220     /**
23221      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23222      * (e.g., during paging).
23223      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23224      */
23225     getModifiedRecords : function(){
23226         return this.modified;
23227     },
23228
23229     // private
23230     createFilterFn : function(property, value, anyMatch){
23231         if(!value.exec){ // not a regex
23232             value = String(value);
23233             if(value.length == 0){
23234                 return false;
23235             }
23236             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23237         }
23238         return function(r){
23239             return value.test(r.data[property]);
23240         };
23241     },
23242
23243     /**
23244      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23245      * @param {String} property A field on your records
23246      * @param {Number} start The record index to start at (defaults to 0)
23247      * @param {Number} end The last record index to include (defaults to length - 1)
23248      * @return {Number} The sum
23249      */
23250     sum : function(property, start, end){
23251         var rs = this.data.items, v = 0;
23252         start = start || 0;
23253         end = (end || end === 0) ? end : rs.length-1;
23254
23255         for(var i = start; i <= end; i++){
23256             v += (rs[i].data[property] || 0);
23257         }
23258         return v;
23259     },
23260
23261     /**
23262      * Filter the records by a specified property.
23263      * @param {String} field A field on your records
23264      * @param {String/RegExp} value Either a string that the field
23265      * should start with or a RegExp to test against the field
23266      * @param {Boolean} anyMatch True to match any part not just the beginning
23267      */
23268     filter : function(property, value, anyMatch){
23269         var fn = this.createFilterFn(property, value, anyMatch);
23270         return fn ? this.filterBy(fn) : this.clearFilter();
23271     },
23272
23273     /**
23274      * Filter by a function. The specified function will be called with each
23275      * record in this data source. If the function returns true the record is included,
23276      * otherwise it is filtered.
23277      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23278      * @param {Object} scope (optional) The scope of the function (defaults to this)
23279      */
23280     filterBy : function(fn, scope){
23281         this.snapshot = this.snapshot || this.data;
23282         this.data = this.queryBy(fn, scope||this);
23283         this.fireEvent("datachanged", this);
23284     },
23285
23286     /**
23287      * Query the records by a specified property.
23288      * @param {String} field A field on your records
23289      * @param {String/RegExp} value Either a string that the field
23290      * should start with or a RegExp to test against the field
23291      * @param {Boolean} anyMatch True to match any part not just the beginning
23292      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23293      */
23294     query : function(property, value, anyMatch){
23295         var fn = this.createFilterFn(property, value, anyMatch);
23296         return fn ? this.queryBy(fn) : this.data.clone();
23297     },
23298
23299     /**
23300      * Query by a function. The specified function will be called with each
23301      * record in this data source. If the function returns true the record is included
23302      * in the results.
23303      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23304      * @param {Object} scope (optional) The scope of the function (defaults to this)
23305       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23306      **/
23307     queryBy : function(fn, scope){
23308         var data = this.snapshot || this.data;
23309         return data.filterBy(fn, scope||this);
23310     },
23311
23312     /**
23313      * Collects unique values for a particular dataIndex from this store.
23314      * @param {String} dataIndex The property to collect
23315      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23316      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23317      * @return {Array} An array of the unique values
23318      **/
23319     collect : function(dataIndex, allowNull, bypassFilter){
23320         var d = (bypassFilter === true && this.snapshot) ?
23321                 this.snapshot.items : this.data.items;
23322         var v, sv, r = [], l = {};
23323         for(var i = 0, len = d.length; i < len; i++){
23324             v = d[i].data[dataIndex];
23325             sv = String(v);
23326             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23327                 l[sv] = true;
23328                 r[r.length] = v;
23329             }
23330         }
23331         return r;
23332     },
23333
23334     /**
23335      * Revert to a view of the Record cache with no filtering applied.
23336      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23337      */
23338     clearFilter : function(suppressEvent){
23339         if(this.snapshot && this.snapshot != this.data){
23340             this.data = this.snapshot;
23341             delete this.snapshot;
23342             if(suppressEvent !== true){
23343                 this.fireEvent("datachanged", this);
23344             }
23345         }
23346     },
23347
23348     // private
23349     afterEdit : function(record){
23350         if(this.modified.indexOf(record) == -1){
23351             this.modified.push(record);
23352         }
23353         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23354     },
23355     
23356     // private
23357     afterReject : function(record){
23358         this.modified.remove(record);
23359         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23360     },
23361
23362     // private
23363     afterCommit : function(record){
23364         this.modified.remove(record);
23365         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23366     },
23367
23368     /**
23369      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23370      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23371      */
23372     commitChanges : function(){
23373         var m = this.modified.slice(0);
23374         this.modified = [];
23375         for(var i = 0, len = m.length; i < len; i++){
23376             m[i].commit();
23377         }
23378     },
23379
23380     /**
23381      * Cancel outstanding changes on all changed records.
23382      */
23383     rejectChanges : function(){
23384         var m = this.modified.slice(0);
23385         this.modified = [];
23386         for(var i = 0, len = m.length; i < len; i++){
23387             m[i].reject();
23388         }
23389     },
23390
23391     onMetaChange : function(meta, rtype, o){
23392         this.recordType = rtype;
23393         this.fields = rtype.prototype.fields;
23394         delete this.snapshot;
23395         this.sortInfo = meta.sortInfo || this.sortInfo;
23396         this.modified = [];
23397         this.fireEvent('metachange', this, this.reader.meta);
23398     },
23399     
23400     moveIndex : function(data, type)
23401     {
23402         var index = this.indexOf(data);
23403         
23404         var newIndex = index + type;
23405         
23406         this.remove(data);
23407         
23408         this.insert(newIndex, data);
23409         
23410     }
23411 });/*
23412  * Based on:
23413  * Ext JS Library 1.1.1
23414  * Copyright(c) 2006-2007, Ext JS, LLC.
23415  *
23416  * Originally Released Under LGPL - original licence link has changed is not relivant.
23417  *
23418  * Fork - LGPL
23419  * <script type="text/javascript">
23420  */
23421
23422 /**
23423  * @class Roo.data.SimpleStore
23424  * @extends Roo.data.Store
23425  * Small helper class to make creating Stores from Array data easier.
23426  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23427  * @cfg {Array} fields An array of field definition objects, or field name strings.
23428  * @cfg {Array} data The multi-dimensional array of data
23429  * @constructor
23430  * @param {Object} config
23431  */
23432 Roo.data.SimpleStore = function(config){
23433     Roo.data.SimpleStore.superclass.constructor.call(this, {
23434         isLocal : true,
23435         reader: new Roo.data.ArrayReader({
23436                 id: config.id
23437             },
23438             Roo.data.Record.create(config.fields)
23439         ),
23440         proxy : new Roo.data.MemoryProxy(config.data)
23441     });
23442     this.load();
23443 };
23444 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23445  * Based on:
23446  * Ext JS Library 1.1.1
23447  * Copyright(c) 2006-2007, Ext JS, LLC.
23448  *
23449  * Originally Released Under LGPL - original licence link has changed is not relivant.
23450  *
23451  * Fork - LGPL
23452  * <script type="text/javascript">
23453  */
23454
23455 /**
23456 /**
23457  * @extends Roo.data.Store
23458  * @class Roo.data.JsonStore
23459  * Small helper class to make creating Stores for JSON data easier. <br/>
23460 <pre><code>
23461 var store = new Roo.data.JsonStore({
23462     url: 'get-images.php',
23463     root: 'images',
23464     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23465 });
23466 </code></pre>
23467  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23468  * JsonReader and HttpProxy (unless inline data is provided).</b>
23469  * @cfg {Array} fields An array of field definition objects, or field name strings.
23470  * @constructor
23471  * @param {Object} config
23472  */
23473 Roo.data.JsonStore = function(c){
23474     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23475         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23476         reader: new Roo.data.JsonReader(c, c.fields)
23477     }));
23478 };
23479 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23480  * Based on:
23481  * Ext JS Library 1.1.1
23482  * Copyright(c) 2006-2007, Ext JS, LLC.
23483  *
23484  * Originally Released Under LGPL - original licence link has changed is not relivant.
23485  *
23486  * Fork - LGPL
23487  * <script type="text/javascript">
23488  */
23489
23490  
23491 Roo.data.Field = function(config){
23492     if(typeof config == "string"){
23493         config = {name: config};
23494     }
23495     Roo.apply(this, config);
23496     
23497     if(!this.type){
23498         this.type = "auto";
23499     }
23500     
23501     var st = Roo.data.SortTypes;
23502     // named sortTypes are supported, here we look them up
23503     if(typeof this.sortType == "string"){
23504         this.sortType = st[this.sortType];
23505     }
23506     
23507     // set default sortType for strings and dates
23508     if(!this.sortType){
23509         switch(this.type){
23510             case "string":
23511                 this.sortType = st.asUCString;
23512                 break;
23513             case "date":
23514                 this.sortType = st.asDate;
23515                 break;
23516             default:
23517                 this.sortType = st.none;
23518         }
23519     }
23520
23521     // define once
23522     var stripRe = /[\$,%]/g;
23523
23524     // prebuilt conversion function for this field, instead of
23525     // switching every time we're reading a value
23526     if(!this.convert){
23527         var cv, dateFormat = this.dateFormat;
23528         switch(this.type){
23529             case "":
23530             case "auto":
23531             case undefined:
23532                 cv = function(v){ return v; };
23533                 break;
23534             case "string":
23535                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23536                 break;
23537             case "int":
23538                 cv = function(v){
23539                     return v !== undefined && v !== null && v !== '' ?
23540                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23541                     };
23542                 break;
23543             case "float":
23544                 cv = function(v){
23545                     return v !== undefined && v !== null && v !== '' ?
23546                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23547                     };
23548                 break;
23549             case "bool":
23550             case "boolean":
23551                 cv = function(v){ return v === true || v === "true" || v == 1; };
23552                 break;
23553             case "date":
23554                 cv = function(v){
23555                     if(!v){
23556                         return '';
23557                     }
23558                     if(v instanceof Date){
23559                         return v;
23560                     }
23561                     if(dateFormat){
23562                         if(dateFormat == "timestamp"){
23563                             return new Date(v*1000);
23564                         }
23565                         return Date.parseDate(v, dateFormat);
23566                     }
23567                     var parsed = Date.parse(v);
23568                     return parsed ? new Date(parsed) : null;
23569                 };
23570              break;
23571             
23572         }
23573         this.convert = cv;
23574     }
23575 };
23576
23577 Roo.data.Field.prototype = {
23578     dateFormat: null,
23579     defaultValue: "",
23580     mapping: null,
23581     sortType : null,
23582     sortDir : "ASC"
23583 };/*
23584  * Based on:
23585  * Ext JS Library 1.1.1
23586  * Copyright(c) 2006-2007, Ext JS, LLC.
23587  *
23588  * Originally Released Under LGPL - original licence link has changed is not relivant.
23589  *
23590  * Fork - LGPL
23591  * <script type="text/javascript">
23592  */
23593  
23594 // Base class for reading structured data from a data source.  This class is intended to be
23595 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23596
23597 /**
23598  * @class Roo.data.DataReader
23599  * Base class for reading structured data from a data source.  This class is intended to be
23600  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23601  */
23602
23603 Roo.data.DataReader = function(meta, recordType){
23604     
23605     this.meta = meta;
23606     
23607     this.recordType = recordType instanceof Array ? 
23608         Roo.data.Record.create(recordType) : recordType;
23609 };
23610
23611 Roo.data.DataReader.prototype = {
23612      /**
23613      * Create an empty record
23614      * @param {Object} data (optional) - overlay some values
23615      * @return {Roo.data.Record} record created.
23616      */
23617     newRow :  function(d) {
23618         var da =  {};
23619         this.recordType.prototype.fields.each(function(c) {
23620             switch( c.type) {
23621                 case 'int' : da[c.name] = 0; break;
23622                 case 'date' : da[c.name] = new Date(); break;
23623                 case 'float' : da[c.name] = 0.0; break;
23624                 case 'boolean' : da[c.name] = false; break;
23625                 default : da[c.name] = ""; break;
23626             }
23627             
23628         });
23629         return new this.recordType(Roo.apply(da, d));
23630     }
23631     
23632 };/*
23633  * Based on:
23634  * Ext JS Library 1.1.1
23635  * Copyright(c) 2006-2007, Ext JS, LLC.
23636  *
23637  * Originally Released Under LGPL - original licence link has changed is not relivant.
23638  *
23639  * Fork - LGPL
23640  * <script type="text/javascript">
23641  */
23642
23643 /**
23644  * @class Roo.data.DataProxy
23645  * @extends Roo.data.Observable
23646  * This class is an abstract base class for implementations which provide retrieval of
23647  * unformatted data objects.<br>
23648  * <p>
23649  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23650  * (of the appropriate type which knows how to parse the data object) to provide a block of
23651  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23652  * <p>
23653  * Custom implementations must implement the load method as described in
23654  * {@link Roo.data.HttpProxy#load}.
23655  */
23656 Roo.data.DataProxy = function(){
23657     this.addEvents({
23658         /**
23659          * @event beforeload
23660          * Fires before a network request is made to retrieve a data object.
23661          * @param {Object} This DataProxy object.
23662          * @param {Object} params The params parameter to the load function.
23663          */
23664         beforeload : true,
23665         /**
23666          * @event load
23667          * Fires before the load method's callback is called.
23668          * @param {Object} This DataProxy object.
23669          * @param {Object} o The data object.
23670          * @param {Object} arg The callback argument object passed to the load function.
23671          */
23672         load : true,
23673         /**
23674          * @event loadexception
23675          * Fires if an Exception occurs during data retrieval.
23676          * @param {Object} This DataProxy object.
23677          * @param {Object} o The data object.
23678          * @param {Object} arg The callback argument object passed to the load function.
23679          * @param {Object} e The Exception.
23680          */
23681         loadexception : true
23682     });
23683     Roo.data.DataProxy.superclass.constructor.call(this);
23684 };
23685
23686 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23687
23688     /**
23689      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23690      */
23691 /*
23692  * Based on:
23693  * Ext JS Library 1.1.1
23694  * Copyright(c) 2006-2007, Ext JS, LLC.
23695  *
23696  * Originally Released Under LGPL - original licence link has changed is not relivant.
23697  *
23698  * Fork - LGPL
23699  * <script type="text/javascript">
23700  */
23701 /**
23702  * @class Roo.data.MemoryProxy
23703  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23704  * to the Reader when its load method is called.
23705  * @constructor
23706  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23707  */
23708 Roo.data.MemoryProxy = function(data){
23709     if (data.data) {
23710         data = data.data;
23711     }
23712     Roo.data.MemoryProxy.superclass.constructor.call(this);
23713     this.data = data;
23714 };
23715
23716 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23717     
23718     /**
23719      * Load data from the requested source (in this case an in-memory
23720      * data object passed to the constructor), read the data object into
23721      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23722      * process that block using the passed callback.
23723      * @param {Object} params This parameter is not used by the MemoryProxy class.
23724      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23725      * object into a block of Roo.data.Records.
23726      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23727      * The function must be passed <ul>
23728      * <li>The Record block object</li>
23729      * <li>The "arg" argument from the load function</li>
23730      * <li>A boolean success indicator</li>
23731      * </ul>
23732      * @param {Object} scope The scope in which to call the callback
23733      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23734      */
23735     load : function(params, reader, callback, scope, arg){
23736         params = params || {};
23737         var result;
23738         try {
23739             result = reader.readRecords(this.data);
23740         }catch(e){
23741             this.fireEvent("loadexception", this, arg, null, e);
23742             callback.call(scope, null, arg, false);
23743             return;
23744         }
23745         callback.call(scope, result, arg, true);
23746     },
23747     
23748     // private
23749     update : function(params, records){
23750         
23751     }
23752 });/*
23753  * Based on:
23754  * Ext JS Library 1.1.1
23755  * Copyright(c) 2006-2007, Ext JS, LLC.
23756  *
23757  * Originally Released Under LGPL - original licence link has changed is not relivant.
23758  *
23759  * Fork - LGPL
23760  * <script type="text/javascript">
23761  */
23762 /**
23763  * @class Roo.data.HttpProxy
23764  * @extends Roo.data.DataProxy
23765  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23766  * configured to reference a certain URL.<br><br>
23767  * <p>
23768  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23769  * from which the running page was served.<br><br>
23770  * <p>
23771  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23772  * <p>
23773  * Be aware that to enable the browser to parse an XML document, the server must set
23774  * the Content-Type header in the HTTP response to "text/xml".
23775  * @constructor
23776  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23777  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23778  * will be used to make the request.
23779  */
23780 Roo.data.HttpProxy = function(conn){
23781     Roo.data.HttpProxy.superclass.constructor.call(this);
23782     // is conn a conn config or a real conn?
23783     this.conn = conn;
23784     this.useAjax = !conn || !conn.events;
23785   
23786 };
23787
23788 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23789     // thse are take from connection...
23790     
23791     /**
23792      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23793      */
23794     /**
23795      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23796      * extra parameters to each request made by this object. (defaults to undefined)
23797      */
23798     /**
23799      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23800      *  to each request made by this object. (defaults to undefined)
23801      */
23802     /**
23803      * @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)
23804      */
23805     /**
23806      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23807      */
23808      /**
23809      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23810      * @type Boolean
23811      */
23812   
23813
23814     /**
23815      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23816      * @type Boolean
23817      */
23818     /**
23819      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23820      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23821      * a finer-grained basis than the DataProxy events.
23822      */
23823     getConnection : function(){
23824         return this.useAjax ? Roo.Ajax : this.conn;
23825     },
23826
23827     /**
23828      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23829      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23830      * process that block using the passed callback.
23831      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23832      * for the request to the remote server.
23833      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23834      * object into a block of Roo.data.Records.
23835      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23836      * The function must be passed <ul>
23837      * <li>The Record block object</li>
23838      * <li>The "arg" argument from the load function</li>
23839      * <li>A boolean success indicator</li>
23840      * </ul>
23841      * @param {Object} scope The scope in which to call the callback
23842      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23843      */
23844     load : function(params, reader, callback, scope, arg){
23845         if(this.fireEvent("beforeload", this, params) !== false){
23846             var  o = {
23847                 params : params || {},
23848                 request: {
23849                     callback : callback,
23850                     scope : scope,
23851                     arg : arg
23852                 },
23853                 reader: reader,
23854                 callback : this.loadResponse,
23855                 scope: this
23856             };
23857             if(this.useAjax){
23858                 Roo.applyIf(o, this.conn);
23859                 if(this.activeRequest){
23860                     Roo.Ajax.abort(this.activeRequest);
23861                 }
23862                 this.activeRequest = Roo.Ajax.request(o);
23863             }else{
23864                 this.conn.request(o);
23865             }
23866         }else{
23867             callback.call(scope||this, null, arg, false);
23868         }
23869     },
23870
23871     // private
23872     loadResponse : function(o, success, response){
23873         delete this.activeRequest;
23874         if(!success){
23875             this.fireEvent("loadexception", this, o, response);
23876             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23877             return;
23878         }
23879         var result;
23880         try {
23881             result = o.reader.read(response);
23882         }catch(e){
23883             this.fireEvent("loadexception", this, o, response, e);
23884             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23885             return;
23886         }
23887         
23888         this.fireEvent("load", this, o, o.request.arg);
23889         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23890     },
23891
23892     // private
23893     update : function(dataSet){
23894
23895     },
23896
23897     // private
23898     updateResponse : function(dataSet){
23899
23900     }
23901 });/*
23902  * Based on:
23903  * Ext JS Library 1.1.1
23904  * Copyright(c) 2006-2007, Ext JS, LLC.
23905  *
23906  * Originally Released Under LGPL - original licence link has changed is not relivant.
23907  *
23908  * Fork - LGPL
23909  * <script type="text/javascript">
23910  */
23911
23912 /**
23913  * @class Roo.data.ScriptTagProxy
23914  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23915  * other than the originating domain of the running page.<br><br>
23916  * <p>
23917  * <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
23918  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23919  * <p>
23920  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23921  * source code that is used as the source inside a &lt;script> tag.<br><br>
23922  * <p>
23923  * In order for the browser to process the returned data, the server must wrap the data object
23924  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23925  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23926  * depending on whether the callback name was passed:
23927  * <p>
23928  * <pre><code>
23929 boolean scriptTag = false;
23930 String cb = request.getParameter("callback");
23931 if (cb != null) {
23932     scriptTag = true;
23933     response.setContentType("text/javascript");
23934 } else {
23935     response.setContentType("application/x-json");
23936 }
23937 Writer out = response.getWriter();
23938 if (scriptTag) {
23939     out.write(cb + "(");
23940 }
23941 out.print(dataBlock.toJsonString());
23942 if (scriptTag) {
23943     out.write(");");
23944 }
23945 </pre></code>
23946  *
23947  * @constructor
23948  * @param {Object} config A configuration object.
23949  */
23950 Roo.data.ScriptTagProxy = function(config){
23951     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23952     Roo.apply(this, config);
23953     this.head = document.getElementsByTagName("head")[0];
23954 };
23955
23956 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23957
23958 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23959     /**
23960      * @cfg {String} url The URL from which to request the data object.
23961      */
23962     /**
23963      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23964      */
23965     timeout : 30000,
23966     /**
23967      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23968      * the server the name of the callback function set up by the load call to process the returned data object.
23969      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23970      * javascript output which calls this named function passing the data object as its only parameter.
23971      */
23972     callbackParam : "callback",
23973     /**
23974      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23975      * name to the request.
23976      */
23977     nocache : true,
23978
23979     /**
23980      * Load data from the configured URL, read the data object into
23981      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23982      * process that block using the passed callback.
23983      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23984      * for the request to the remote server.
23985      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23986      * object into a block of Roo.data.Records.
23987      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23988      * The function must be passed <ul>
23989      * <li>The Record block object</li>
23990      * <li>The "arg" argument from the load function</li>
23991      * <li>A boolean success indicator</li>
23992      * </ul>
23993      * @param {Object} scope The scope in which to call the callback
23994      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23995      */
23996     load : function(params, reader, callback, scope, arg){
23997         if(this.fireEvent("beforeload", this, params) !== false){
23998
23999             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24000
24001             var url = this.url;
24002             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24003             if(this.nocache){
24004                 url += "&_dc=" + (new Date().getTime());
24005             }
24006             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24007             var trans = {
24008                 id : transId,
24009                 cb : "stcCallback"+transId,
24010                 scriptId : "stcScript"+transId,
24011                 params : params,
24012                 arg : arg,
24013                 url : url,
24014                 callback : callback,
24015                 scope : scope,
24016                 reader : reader
24017             };
24018             var conn = this;
24019
24020             window[trans.cb] = function(o){
24021                 conn.handleResponse(o, trans);
24022             };
24023
24024             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24025
24026             if(this.autoAbort !== false){
24027                 this.abort();
24028             }
24029
24030             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24031
24032             var script = document.createElement("script");
24033             script.setAttribute("src", url);
24034             script.setAttribute("type", "text/javascript");
24035             script.setAttribute("id", trans.scriptId);
24036             this.head.appendChild(script);
24037
24038             this.trans = trans;
24039         }else{
24040             callback.call(scope||this, null, arg, false);
24041         }
24042     },
24043
24044     // private
24045     isLoading : function(){
24046         return this.trans ? true : false;
24047     },
24048
24049     /**
24050      * Abort the current server request.
24051      */
24052     abort : function(){
24053         if(this.isLoading()){
24054             this.destroyTrans(this.trans);
24055         }
24056     },
24057
24058     // private
24059     destroyTrans : function(trans, isLoaded){
24060         this.head.removeChild(document.getElementById(trans.scriptId));
24061         clearTimeout(trans.timeoutId);
24062         if(isLoaded){
24063             window[trans.cb] = undefined;
24064             try{
24065                 delete window[trans.cb];
24066             }catch(e){}
24067         }else{
24068             // if hasn't been loaded, wait for load to remove it to prevent script error
24069             window[trans.cb] = function(){
24070                 window[trans.cb] = undefined;
24071                 try{
24072                     delete window[trans.cb];
24073                 }catch(e){}
24074             };
24075         }
24076     },
24077
24078     // private
24079     handleResponse : function(o, trans){
24080         this.trans = false;
24081         this.destroyTrans(trans, true);
24082         var result;
24083         try {
24084             result = trans.reader.readRecords(o);
24085         }catch(e){
24086             this.fireEvent("loadexception", this, o, trans.arg, e);
24087             trans.callback.call(trans.scope||window, null, trans.arg, false);
24088             return;
24089         }
24090         this.fireEvent("load", this, o, trans.arg);
24091         trans.callback.call(trans.scope||window, result, trans.arg, true);
24092     },
24093
24094     // private
24095     handleFailure : function(trans){
24096         this.trans = false;
24097         this.destroyTrans(trans, false);
24098         this.fireEvent("loadexception", this, null, trans.arg);
24099         trans.callback.call(trans.scope||window, null, trans.arg, false);
24100     }
24101 });/*
24102  * Based on:
24103  * Ext JS Library 1.1.1
24104  * Copyright(c) 2006-2007, Ext JS, LLC.
24105  *
24106  * Originally Released Under LGPL - original licence link has changed is not relivant.
24107  *
24108  * Fork - LGPL
24109  * <script type="text/javascript">
24110  */
24111
24112 /**
24113  * @class Roo.data.JsonReader
24114  * @extends Roo.data.DataReader
24115  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24116  * based on mappings in a provided Roo.data.Record constructor.
24117  * 
24118  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24119  * in the reply previously. 
24120  * 
24121  * <p>
24122  * Example code:
24123  * <pre><code>
24124 var RecordDef = Roo.data.Record.create([
24125     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24126     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24127 ]);
24128 var myReader = new Roo.data.JsonReader({
24129     totalProperty: "results",    // The property which contains the total dataset size (optional)
24130     root: "rows",                // The property which contains an Array of row objects
24131     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24132 }, RecordDef);
24133 </code></pre>
24134  * <p>
24135  * This would consume a JSON file like this:
24136  * <pre><code>
24137 { 'results': 2, 'rows': [
24138     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24139     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24140 }
24141 </code></pre>
24142  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24143  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24144  * paged from the remote server.
24145  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24146  * @cfg {String} root name of the property which contains the Array of row objects.
24147  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24148  * @cfg {Array} fields Array of field definition objects
24149  * @constructor
24150  * Create a new JsonReader
24151  * @param {Object} meta Metadata configuration options
24152  * @param {Object} recordType Either an Array of field definition objects,
24153  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24154  */
24155 Roo.data.JsonReader = function(meta, recordType){
24156     
24157     meta = meta || {};
24158     // set some defaults:
24159     Roo.applyIf(meta, {
24160         totalProperty: 'total',
24161         successProperty : 'success',
24162         root : 'data',
24163         id : 'id'
24164     });
24165     
24166     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24167 };
24168 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24169     
24170     /**
24171      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24172      * Used by Store query builder to append _requestMeta to params.
24173      * 
24174      */
24175     metaFromRemote : false,
24176     /**
24177      * This method is only used by a DataProxy which has retrieved data from a remote server.
24178      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24179      * @return {Object} data A data block which is used by an Roo.data.Store object as
24180      * a cache of Roo.data.Records.
24181      */
24182     read : function(response){
24183         var json = response.responseText;
24184        
24185         var o = /* eval:var:o */ eval("("+json+")");
24186         if(!o) {
24187             throw {message: "JsonReader.read: Json object not found"};
24188         }
24189         
24190         if(o.metaData){
24191             
24192             delete this.ef;
24193             this.metaFromRemote = true;
24194             this.meta = o.metaData;
24195             this.recordType = Roo.data.Record.create(o.metaData.fields);
24196             this.onMetaChange(this.meta, this.recordType, o);
24197         }
24198         return this.readRecords(o);
24199     },
24200
24201     // private function a store will implement
24202     onMetaChange : function(meta, recordType, o){
24203
24204     },
24205
24206     /**
24207          * @ignore
24208          */
24209     simpleAccess: function(obj, subsc) {
24210         return obj[subsc];
24211     },
24212
24213         /**
24214          * @ignore
24215          */
24216     getJsonAccessor: function(){
24217         var re = /[\[\.]/;
24218         return function(expr) {
24219             try {
24220                 return(re.test(expr))
24221                     ? new Function("obj", "return obj." + expr)
24222                     : function(obj){
24223                         return obj[expr];
24224                     };
24225             } catch(e){}
24226             return Roo.emptyFn;
24227         };
24228     }(),
24229
24230     /**
24231      * Create a data block containing Roo.data.Records from an XML document.
24232      * @param {Object} o An object which contains an Array of row objects in the property specified
24233      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24234      * which contains the total size of the dataset.
24235      * @return {Object} data A data block which is used by an Roo.data.Store object as
24236      * a cache of Roo.data.Records.
24237      */
24238     readRecords : function(o){
24239         /**
24240          * After any data loads, the raw JSON data is available for further custom processing.
24241          * @type Object
24242          */
24243         this.o = o;
24244         var s = this.meta, Record = this.recordType,
24245             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24246
24247 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24248         if (!this.ef) {
24249             if(s.totalProperty) {
24250                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24251                 }
24252                 if(s.successProperty) {
24253                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24254                 }
24255                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24256                 if (s.id) {
24257                         var g = this.getJsonAccessor(s.id);
24258                         this.getId = function(rec) {
24259                                 var r = g(rec);  
24260                                 return (r === undefined || r === "") ? null : r;
24261                         };
24262                 } else {
24263                         this.getId = function(){return null;};
24264                 }
24265             this.ef = [];
24266             for(var jj = 0; jj < fl; jj++){
24267                 f = fi[jj];
24268                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24269                 this.ef[jj] = this.getJsonAccessor(map);
24270             }
24271         }
24272
24273         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24274         if(s.totalProperty){
24275             var vt = parseInt(this.getTotal(o), 10);
24276             if(!isNaN(vt)){
24277                 totalRecords = vt;
24278             }
24279         }
24280         if(s.successProperty){
24281             var vs = this.getSuccess(o);
24282             if(vs === false || vs === 'false'){
24283                 success = false;
24284             }
24285         }
24286         var records = [];
24287         for(var i = 0; i < c; i++){
24288                 var n = root[i];
24289             var values = {};
24290             var id = this.getId(n);
24291             for(var j = 0; j < fl; j++){
24292                 f = fi[j];
24293             var v = this.ef[j](n);
24294             if (!f.convert) {
24295                 Roo.log('missing convert for ' + f.name);
24296                 Roo.log(f);
24297                 continue;
24298             }
24299             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24300             }
24301             var record = new Record(values, id);
24302             record.json = n;
24303             records[i] = record;
24304         }
24305         return {
24306             raw : o,
24307             success : success,
24308             records : records,
24309             totalRecords : totalRecords
24310         };
24311     }
24312 });/*
24313  * Based on:
24314  * Ext JS Library 1.1.1
24315  * Copyright(c) 2006-2007, Ext JS, LLC.
24316  *
24317  * Originally Released Under LGPL - original licence link has changed is not relivant.
24318  *
24319  * Fork - LGPL
24320  * <script type="text/javascript">
24321  */
24322
24323 /**
24324  * @class Roo.data.XmlReader
24325  * @extends Roo.data.DataReader
24326  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24327  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24328  * <p>
24329  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24330  * header in the HTTP response must be set to "text/xml".</em>
24331  * <p>
24332  * Example code:
24333  * <pre><code>
24334 var RecordDef = Roo.data.Record.create([
24335    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24336    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24337 ]);
24338 var myReader = new Roo.data.XmlReader({
24339    totalRecords: "results", // The element which contains the total dataset size (optional)
24340    record: "row",           // The repeated element which contains row information
24341    id: "id"                 // The element within the row that provides an ID for the record (optional)
24342 }, RecordDef);
24343 </code></pre>
24344  * <p>
24345  * This would consume an XML file like this:
24346  * <pre><code>
24347 &lt;?xml?>
24348 &lt;dataset>
24349  &lt;results>2&lt;/results>
24350  &lt;row>
24351    &lt;id>1&lt;/id>
24352    &lt;name>Bill&lt;/name>
24353    &lt;occupation>Gardener&lt;/occupation>
24354  &lt;/row>
24355  &lt;row>
24356    &lt;id>2&lt;/id>
24357    &lt;name>Ben&lt;/name>
24358    &lt;occupation>Horticulturalist&lt;/occupation>
24359  &lt;/row>
24360 &lt;/dataset>
24361 </code></pre>
24362  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24363  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24364  * paged from the remote server.
24365  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24366  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24367  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24368  * a record identifier value.
24369  * @constructor
24370  * Create a new XmlReader
24371  * @param {Object} meta Metadata configuration options
24372  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24373  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24374  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24375  */
24376 Roo.data.XmlReader = function(meta, recordType){
24377     meta = meta || {};
24378     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24379 };
24380 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24381     /**
24382      * This method is only used by a DataProxy which has retrieved data from a remote server.
24383          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24384          * to contain a method called 'responseXML' that returns an XML document object.
24385      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24386      * a cache of Roo.data.Records.
24387      */
24388     read : function(response){
24389         var doc = response.responseXML;
24390         if(!doc) {
24391             throw {message: "XmlReader.read: XML Document not available"};
24392         }
24393         return this.readRecords(doc);
24394     },
24395
24396     /**
24397      * Create a data block containing Roo.data.Records from an XML document.
24398          * @param {Object} doc A parsed XML document.
24399      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24400      * a cache of Roo.data.Records.
24401      */
24402     readRecords : function(doc){
24403         /**
24404          * After any data loads/reads, the raw XML Document is available for further custom processing.
24405          * @type XMLDocument
24406          */
24407         this.xmlData = doc;
24408         var root = doc.documentElement || doc;
24409         var q = Roo.DomQuery;
24410         var recordType = this.recordType, fields = recordType.prototype.fields;
24411         var sid = this.meta.id;
24412         var totalRecords = 0, success = true;
24413         if(this.meta.totalRecords){
24414             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24415         }
24416         
24417         if(this.meta.success){
24418             var sv = q.selectValue(this.meta.success, root, true);
24419             success = sv !== false && sv !== 'false';
24420         }
24421         var records = [];
24422         var ns = q.select(this.meta.record, root);
24423         for(var i = 0, len = ns.length; i < len; i++) {
24424                 var n = ns[i];
24425                 var values = {};
24426                 var id = sid ? q.selectValue(sid, n) : undefined;
24427                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24428                     var f = fields.items[j];
24429                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24430                     v = f.convert(v);
24431                     values[f.name] = v;
24432                 }
24433                 var record = new recordType(values, id);
24434                 record.node = n;
24435                 records[records.length] = record;
24436             }
24437
24438             return {
24439                 success : success,
24440                 records : records,
24441                 totalRecords : totalRecords || records.length
24442             };
24443     }
24444 });/*
24445  * Based on:
24446  * Ext JS Library 1.1.1
24447  * Copyright(c) 2006-2007, Ext JS, LLC.
24448  *
24449  * Originally Released Under LGPL - original licence link has changed is not relivant.
24450  *
24451  * Fork - LGPL
24452  * <script type="text/javascript">
24453  */
24454
24455 /**
24456  * @class Roo.data.ArrayReader
24457  * @extends Roo.data.DataReader
24458  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24459  * Each element of that Array represents a row of data fields. The
24460  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24461  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24462  * <p>
24463  * Example code:.
24464  * <pre><code>
24465 var RecordDef = Roo.data.Record.create([
24466     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24467     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24468 ]);
24469 var myReader = new Roo.data.ArrayReader({
24470     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24471 }, RecordDef);
24472 </code></pre>
24473  * <p>
24474  * This would consume an Array like this:
24475  * <pre><code>
24476 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24477   </code></pre>
24478  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24479  * @constructor
24480  * Create a new JsonReader
24481  * @param {Object} meta Metadata configuration options.
24482  * @param {Object} recordType Either an Array of field definition objects
24483  * as specified to {@link Roo.data.Record#create},
24484  * or an {@link Roo.data.Record} object
24485  * created using {@link Roo.data.Record#create}.
24486  */
24487 Roo.data.ArrayReader = function(meta, recordType){
24488     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24489 };
24490
24491 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24492     /**
24493      * Create a data block containing Roo.data.Records from an XML document.
24494      * @param {Object} o An Array of row objects which represents the dataset.
24495      * @return {Object} data A data block which is used by an Roo.data.Store object as
24496      * a cache of Roo.data.Records.
24497      */
24498     readRecords : function(o){
24499         var sid = this.meta ? this.meta.id : null;
24500         var recordType = this.recordType, fields = recordType.prototype.fields;
24501         var records = [];
24502         var root = o;
24503             for(var i = 0; i < root.length; i++){
24504                     var n = root[i];
24505                 var values = {};
24506                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24507                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24508                 var f = fields.items[j];
24509                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24510                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24511                 v = f.convert(v);
24512                 values[f.name] = v;
24513             }
24514                 var record = new recordType(values, id);
24515                 record.json = n;
24516                 records[records.length] = record;
24517             }
24518             return {
24519                 records : records,
24520                 totalRecords : records.length
24521             };
24522     }
24523 });/*
24524  * Based on:
24525  * Ext JS Library 1.1.1
24526  * Copyright(c) 2006-2007, Ext JS, LLC.
24527  *
24528  * Originally Released Under LGPL - original licence link has changed is not relivant.
24529  *
24530  * Fork - LGPL
24531  * <script type="text/javascript">
24532  */
24533
24534
24535 /**
24536  * @class Roo.data.Tree
24537  * @extends Roo.util.Observable
24538  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24539  * in the tree have most standard DOM functionality.
24540  * @constructor
24541  * @param {Node} root (optional) The root node
24542  */
24543 Roo.data.Tree = function(root){
24544    this.nodeHash = {};
24545    /**
24546     * The root node for this tree
24547     * @type Node
24548     */
24549    this.root = null;
24550    if(root){
24551        this.setRootNode(root);
24552    }
24553    this.addEvents({
24554        /**
24555         * @event append
24556         * Fires when a new child node is appended to a node in this tree.
24557         * @param {Tree} tree The owner tree
24558         * @param {Node} parent The parent node
24559         * @param {Node} node The newly appended node
24560         * @param {Number} index The index of the newly appended node
24561         */
24562        "append" : true,
24563        /**
24564         * @event remove
24565         * Fires when a child node is removed from a node in this tree.
24566         * @param {Tree} tree The owner tree
24567         * @param {Node} parent The parent node
24568         * @param {Node} node The child node removed
24569         */
24570        "remove" : true,
24571        /**
24572         * @event move
24573         * Fires when a node is moved to a new location in the tree
24574         * @param {Tree} tree The owner tree
24575         * @param {Node} node The node moved
24576         * @param {Node} oldParent The old parent of this node
24577         * @param {Node} newParent The new parent of this node
24578         * @param {Number} index The index it was moved to
24579         */
24580        "move" : true,
24581        /**
24582         * @event insert
24583         * Fires when a new child node is inserted in a node in this tree.
24584         * @param {Tree} tree The owner tree
24585         * @param {Node} parent The parent node
24586         * @param {Node} node The child node inserted
24587         * @param {Node} refNode The child node the node was inserted before
24588         */
24589        "insert" : true,
24590        /**
24591         * @event beforeappend
24592         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24593         * @param {Tree} tree The owner tree
24594         * @param {Node} parent The parent node
24595         * @param {Node} node The child node to be appended
24596         */
24597        "beforeappend" : true,
24598        /**
24599         * @event beforeremove
24600         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24601         * @param {Tree} tree The owner tree
24602         * @param {Node} parent The parent node
24603         * @param {Node} node The child node to be removed
24604         */
24605        "beforeremove" : true,
24606        /**
24607         * @event beforemove
24608         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24609         * @param {Tree} tree The owner tree
24610         * @param {Node} node The node being moved
24611         * @param {Node} oldParent The parent of the node
24612         * @param {Node} newParent The new parent the node is moving to
24613         * @param {Number} index The index it is being moved to
24614         */
24615        "beforemove" : true,
24616        /**
24617         * @event beforeinsert
24618         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24619         * @param {Tree} tree The owner tree
24620         * @param {Node} parent The parent node
24621         * @param {Node} node The child node to be inserted
24622         * @param {Node} refNode The child node the node is being inserted before
24623         */
24624        "beforeinsert" : true
24625    });
24626
24627     Roo.data.Tree.superclass.constructor.call(this);
24628 };
24629
24630 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24631     pathSeparator: "/",
24632
24633     proxyNodeEvent : function(){
24634         return this.fireEvent.apply(this, arguments);
24635     },
24636
24637     /**
24638      * Returns the root node for this tree.
24639      * @return {Node}
24640      */
24641     getRootNode : function(){
24642         return this.root;
24643     },
24644
24645     /**
24646      * Sets the root node for this tree.
24647      * @param {Node} node
24648      * @return {Node}
24649      */
24650     setRootNode : function(node){
24651         this.root = node;
24652         node.ownerTree = this;
24653         node.isRoot = true;
24654         this.registerNode(node);
24655         return node;
24656     },
24657
24658     /**
24659      * Gets a node in this tree by its id.
24660      * @param {String} id
24661      * @return {Node}
24662      */
24663     getNodeById : function(id){
24664         return this.nodeHash[id];
24665     },
24666
24667     registerNode : function(node){
24668         this.nodeHash[node.id] = node;
24669     },
24670
24671     unregisterNode : function(node){
24672         delete this.nodeHash[node.id];
24673     },
24674
24675     toString : function(){
24676         return "[Tree"+(this.id?" "+this.id:"")+"]";
24677     }
24678 });
24679
24680 /**
24681  * @class Roo.data.Node
24682  * @extends Roo.util.Observable
24683  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24684  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24685  * @constructor
24686  * @param {Object} attributes The attributes/config for the node
24687  */
24688 Roo.data.Node = function(attributes){
24689     /**
24690      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24691      * @type {Object}
24692      */
24693     this.attributes = attributes || {};
24694     this.leaf = this.attributes.leaf;
24695     /**
24696      * The node id. @type String
24697      */
24698     this.id = this.attributes.id;
24699     if(!this.id){
24700         this.id = Roo.id(null, "ynode-");
24701         this.attributes.id = this.id;
24702     }
24703      
24704     
24705     /**
24706      * All child nodes of this node. @type Array
24707      */
24708     this.childNodes = [];
24709     if(!this.childNodes.indexOf){ // indexOf is a must
24710         this.childNodes.indexOf = function(o){
24711             for(var i = 0, len = this.length; i < len; i++){
24712                 if(this[i] == o) {
24713                     return i;
24714                 }
24715             }
24716             return -1;
24717         };
24718     }
24719     /**
24720      * The parent node for this node. @type Node
24721      */
24722     this.parentNode = null;
24723     /**
24724      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24725      */
24726     this.firstChild = null;
24727     /**
24728      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24729      */
24730     this.lastChild = null;
24731     /**
24732      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24733      */
24734     this.previousSibling = null;
24735     /**
24736      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24737      */
24738     this.nextSibling = null;
24739
24740     this.addEvents({
24741        /**
24742         * @event append
24743         * Fires when a new child node is appended
24744         * @param {Tree} tree The owner tree
24745         * @param {Node} this This node
24746         * @param {Node} node The newly appended node
24747         * @param {Number} index The index of the newly appended node
24748         */
24749        "append" : true,
24750        /**
24751         * @event remove
24752         * Fires when a child node is removed
24753         * @param {Tree} tree The owner tree
24754         * @param {Node} this This node
24755         * @param {Node} node The removed node
24756         */
24757        "remove" : true,
24758        /**
24759         * @event move
24760         * Fires when this node is moved to a new location in the tree
24761         * @param {Tree} tree The owner tree
24762         * @param {Node} this This node
24763         * @param {Node} oldParent The old parent of this node
24764         * @param {Node} newParent The new parent of this node
24765         * @param {Number} index The index it was moved to
24766         */
24767        "move" : true,
24768        /**
24769         * @event insert
24770         * Fires when a new child node is inserted.
24771         * @param {Tree} tree The owner tree
24772         * @param {Node} this This node
24773         * @param {Node} node The child node inserted
24774         * @param {Node} refNode The child node the node was inserted before
24775         */
24776        "insert" : true,
24777        /**
24778         * @event beforeappend
24779         * Fires before a new child is appended, return false to cancel the append.
24780         * @param {Tree} tree The owner tree
24781         * @param {Node} this This node
24782         * @param {Node} node The child node to be appended
24783         */
24784        "beforeappend" : true,
24785        /**
24786         * @event beforeremove
24787         * Fires before a child is removed, return false to cancel the remove.
24788         * @param {Tree} tree The owner tree
24789         * @param {Node} this This node
24790         * @param {Node} node The child node to be removed
24791         */
24792        "beforeremove" : true,
24793        /**
24794         * @event beforemove
24795         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24796         * @param {Tree} tree The owner tree
24797         * @param {Node} this This node
24798         * @param {Node} oldParent The parent of this node
24799         * @param {Node} newParent The new parent this node is moving to
24800         * @param {Number} index The index it is being moved to
24801         */
24802        "beforemove" : true,
24803        /**
24804         * @event beforeinsert
24805         * Fires before a new child is inserted, return false to cancel the insert.
24806         * @param {Tree} tree The owner tree
24807         * @param {Node} this This node
24808         * @param {Node} node The child node to be inserted
24809         * @param {Node} refNode The child node the node is being inserted before
24810         */
24811        "beforeinsert" : true
24812    });
24813     this.listeners = this.attributes.listeners;
24814     Roo.data.Node.superclass.constructor.call(this);
24815 };
24816
24817 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24818     fireEvent : function(evtName){
24819         // first do standard event for this node
24820         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24821             return false;
24822         }
24823         // then bubble it up to the tree if the event wasn't cancelled
24824         var ot = this.getOwnerTree();
24825         if(ot){
24826             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24827                 return false;
24828             }
24829         }
24830         return true;
24831     },
24832
24833     /**
24834      * Returns true if this node is a leaf
24835      * @return {Boolean}
24836      */
24837     isLeaf : function(){
24838         return this.leaf === true;
24839     },
24840
24841     // private
24842     setFirstChild : function(node){
24843         this.firstChild = node;
24844     },
24845
24846     //private
24847     setLastChild : function(node){
24848         this.lastChild = node;
24849     },
24850
24851
24852     /**
24853      * Returns true if this node is the last child of its parent
24854      * @return {Boolean}
24855      */
24856     isLast : function(){
24857        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24858     },
24859
24860     /**
24861      * Returns true if this node is the first child of its parent
24862      * @return {Boolean}
24863      */
24864     isFirst : function(){
24865        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24866     },
24867
24868     hasChildNodes : function(){
24869         return !this.isLeaf() && this.childNodes.length > 0;
24870     },
24871
24872     /**
24873      * Insert node(s) as the last child node of this node.
24874      * @param {Node/Array} node The node or Array of nodes to append
24875      * @return {Node} The appended node if single append, or null if an array was passed
24876      */
24877     appendChild : function(node){
24878         var multi = false;
24879         if(node instanceof Array){
24880             multi = node;
24881         }else if(arguments.length > 1){
24882             multi = arguments;
24883         }
24884         // if passed an array or multiple args do them one by one
24885         if(multi){
24886             for(var i = 0, len = multi.length; i < len; i++) {
24887                 this.appendChild(multi[i]);
24888             }
24889         }else{
24890             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24891                 return false;
24892             }
24893             var index = this.childNodes.length;
24894             var oldParent = node.parentNode;
24895             // it's a move, make sure we move it cleanly
24896             if(oldParent){
24897                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24898                     return false;
24899                 }
24900                 oldParent.removeChild(node);
24901             }
24902             index = this.childNodes.length;
24903             if(index == 0){
24904                 this.setFirstChild(node);
24905             }
24906             this.childNodes.push(node);
24907             node.parentNode = this;
24908             var ps = this.childNodes[index-1];
24909             if(ps){
24910                 node.previousSibling = ps;
24911                 ps.nextSibling = node;
24912             }else{
24913                 node.previousSibling = null;
24914             }
24915             node.nextSibling = null;
24916             this.setLastChild(node);
24917             node.setOwnerTree(this.getOwnerTree());
24918             this.fireEvent("append", this.ownerTree, this, node, index);
24919             if(oldParent){
24920                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24921             }
24922             return node;
24923         }
24924     },
24925
24926     /**
24927      * Removes a child node from this node.
24928      * @param {Node} node The node to remove
24929      * @return {Node} The removed node
24930      */
24931     removeChild : function(node){
24932         var index = this.childNodes.indexOf(node);
24933         if(index == -1){
24934             return false;
24935         }
24936         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24937             return false;
24938         }
24939
24940         // remove it from childNodes collection
24941         this.childNodes.splice(index, 1);
24942
24943         // update siblings
24944         if(node.previousSibling){
24945             node.previousSibling.nextSibling = node.nextSibling;
24946         }
24947         if(node.nextSibling){
24948             node.nextSibling.previousSibling = node.previousSibling;
24949         }
24950
24951         // update child refs
24952         if(this.firstChild == node){
24953             this.setFirstChild(node.nextSibling);
24954         }
24955         if(this.lastChild == node){
24956             this.setLastChild(node.previousSibling);
24957         }
24958
24959         node.setOwnerTree(null);
24960         // clear any references from the node
24961         node.parentNode = null;
24962         node.previousSibling = null;
24963         node.nextSibling = null;
24964         this.fireEvent("remove", this.ownerTree, this, node);
24965         return node;
24966     },
24967
24968     /**
24969      * Inserts the first node before the second node in this nodes childNodes collection.
24970      * @param {Node} node The node to insert
24971      * @param {Node} refNode The node to insert before (if null the node is appended)
24972      * @return {Node} The inserted node
24973      */
24974     insertBefore : function(node, refNode){
24975         if(!refNode){ // like standard Dom, refNode can be null for append
24976             return this.appendChild(node);
24977         }
24978         // nothing to do
24979         if(node == refNode){
24980             return false;
24981         }
24982
24983         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24984             return false;
24985         }
24986         var index = this.childNodes.indexOf(refNode);
24987         var oldParent = node.parentNode;
24988         var refIndex = index;
24989
24990         // when moving internally, indexes will change after remove
24991         if(oldParent == this && this.childNodes.indexOf(node) < index){
24992             refIndex--;
24993         }
24994
24995         // it's a move, make sure we move it cleanly
24996         if(oldParent){
24997             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24998                 return false;
24999             }
25000             oldParent.removeChild(node);
25001         }
25002         if(refIndex == 0){
25003             this.setFirstChild(node);
25004         }
25005         this.childNodes.splice(refIndex, 0, node);
25006         node.parentNode = this;
25007         var ps = this.childNodes[refIndex-1];
25008         if(ps){
25009             node.previousSibling = ps;
25010             ps.nextSibling = node;
25011         }else{
25012             node.previousSibling = null;
25013         }
25014         node.nextSibling = refNode;
25015         refNode.previousSibling = node;
25016         node.setOwnerTree(this.getOwnerTree());
25017         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25018         if(oldParent){
25019             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25020         }
25021         return node;
25022     },
25023
25024     /**
25025      * Returns the child node at the specified index.
25026      * @param {Number} index
25027      * @return {Node}
25028      */
25029     item : function(index){
25030         return this.childNodes[index];
25031     },
25032
25033     /**
25034      * Replaces one child node in this node with another.
25035      * @param {Node} newChild The replacement node
25036      * @param {Node} oldChild The node to replace
25037      * @return {Node} The replaced node
25038      */
25039     replaceChild : function(newChild, oldChild){
25040         this.insertBefore(newChild, oldChild);
25041         this.removeChild(oldChild);
25042         return oldChild;
25043     },
25044
25045     /**
25046      * Returns the index of a child node
25047      * @param {Node} node
25048      * @return {Number} The index of the node or -1 if it was not found
25049      */
25050     indexOf : function(child){
25051         return this.childNodes.indexOf(child);
25052     },
25053
25054     /**
25055      * Returns the tree this node is in.
25056      * @return {Tree}
25057      */
25058     getOwnerTree : function(){
25059         // if it doesn't have one, look for one
25060         if(!this.ownerTree){
25061             var p = this;
25062             while(p){
25063                 if(p.ownerTree){
25064                     this.ownerTree = p.ownerTree;
25065                     break;
25066                 }
25067                 p = p.parentNode;
25068             }
25069         }
25070         return this.ownerTree;
25071     },
25072
25073     /**
25074      * Returns depth of this node (the root node has a depth of 0)
25075      * @return {Number}
25076      */
25077     getDepth : function(){
25078         var depth = 0;
25079         var p = this;
25080         while(p.parentNode){
25081             ++depth;
25082             p = p.parentNode;
25083         }
25084         return depth;
25085     },
25086
25087     // private
25088     setOwnerTree : function(tree){
25089         // if it's move, we need to update everyone
25090         if(tree != this.ownerTree){
25091             if(this.ownerTree){
25092                 this.ownerTree.unregisterNode(this);
25093             }
25094             this.ownerTree = tree;
25095             var cs = this.childNodes;
25096             for(var i = 0, len = cs.length; i < len; i++) {
25097                 cs[i].setOwnerTree(tree);
25098             }
25099             if(tree){
25100                 tree.registerNode(this);
25101             }
25102         }
25103     },
25104
25105     /**
25106      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25107      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25108      * @return {String} The path
25109      */
25110     getPath : function(attr){
25111         attr = attr || "id";
25112         var p = this.parentNode;
25113         var b = [this.attributes[attr]];
25114         while(p){
25115             b.unshift(p.attributes[attr]);
25116             p = p.parentNode;
25117         }
25118         var sep = this.getOwnerTree().pathSeparator;
25119         return sep + b.join(sep);
25120     },
25121
25122     /**
25123      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25124      * function call will be the scope provided or the current node. The arguments to the function
25125      * will be the args provided or the current node. If the function returns false at any point,
25126      * the bubble is stopped.
25127      * @param {Function} fn The function to call
25128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25130      */
25131     bubble : function(fn, scope, args){
25132         var p = this;
25133         while(p){
25134             if(fn.call(scope || p, args || p) === false){
25135                 break;
25136             }
25137             p = p.parentNode;
25138         }
25139     },
25140
25141     /**
25142      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25143      * function call will be the scope provided or the current node. The arguments to the function
25144      * will be the args provided or the current node. If the function returns false at any point,
25145      * the cascade is stopped on that branch.
25146      * @param {Function} fn The function to call
25147      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25148      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25149      */
25150     cascade : function(fn, scope, args){
25151         if(fn.call(scope || this, args || this) !== false){
25152             var cs = this.childNodes;
25153             for(var i = 0, len = cs.length; i < len; i++) {
25154                 cs[i].cascade(fn, scope, args);
25155             }
25156         }
25157     },
25158
25159     /**
25160      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25161      * function call will be the scope provided or the current node. The arguments to the function
25162      * will be the args provided or the current node. If the function returns false at any point,
25163      * the iteration stops.
25164      * @param {Function} fn The function to call
25165      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25166      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25167      */
25168     eachChild : function(fn, scope, args){
25169         var cs = this.childNodes;
25170         for(var i = 0, len = cs.length; i < len; i++) {
25171                 if(fn.call(scope || this, args || cs[i]) === false){
25172                     break;
25173                 }
25174         }
25175     },
25176
25177     /**
25178      * Finds the first child that has the attribute with the specified value.
25179      * @param {String} attribute The attribute name
25180      * @param {Mixed} value The value to search for
25181      * @return {Node} The found child or null if none was found
25182      */
25183     findChild : function(attribute, value){
25184         var cs = this.childNodes;
25185         for(var i = 0, len = cs.length; i < len; i++) {
25186                 if(cs[i].attributes[attribute] == value){
25187                     return cs[i];
25188                 }
25189         }
25190         return null;
25191     },
25192
25193     /**
25194      * Finds the first child by a custom function. The child matches if the function passed
25195      * returns true.
25196      * @param {Function} fn
25197      * @param {Object} scope (optional)
25198      * @return {Node} The found child or null if none was found
25199      */
25200     findChildBy : function(fn, scope){
25201         var cs = this.childNodes;
25202         for(var i = 0, len = cs.length; i < len; i++) {
25203                 if(fn.call(scope||cs[i], cs[i]) === true){
25204                     return cs[i];
25205                 }
25206         }
25207         return null;
25208     },
25209
25210     /**
25211      * Sorts this nodes children using the supplied sort function
25212      * @param {Function} fn
25213      * @param {Object} scope (optional)
25214      */
25215     sort : function(fn, scope){
25216         var cs = this.childNodes;
25217         var len = cs.length;
25218         if(len > 0){
25219             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25220             cs.sort(sortFn);
25221             for(var i = 0; i < len; i++){
25222                 var n = cs[i];
25223                 n.previousSibling = cs[i-1];
25224                 n.nextSibling = cs[i+1];
25225                 if(i == 0){
25226                     this.setFirstChild(n);
25227                 }
25228                 if(i == len-1){
25229                     this.setLastChild(n);
25230                 }
25231             }
25232         }
25233     },
25234
25235     /**
25236      * Returns true if this node is an ancestor (at any point) of the passed node.
25237      * @param {Node} node
25238      * @return {Boolean}
25239      */
25240     contains : function(node){
25241         return node.isAncestor(this);
25242     },
25243
25244     /**
25245      * Returns true if the passed node is an ancestor (at any point) of this node.
25246      * @param {Node} node
25247      * @return {Boolean}
25248      */
25249     isAncestor : function(node){
25250         var p = this.parentNode;
25251         while(p){
25252             if(p == node){
25253                 return true;
25254             }
25255             p = p.parentNode;
25256         }
25257         return false;
25258     },
25259
25260     toString : function(){
25261         return "[Node"+(this.id?" "+this.id:"")+"]";
25262     }
25263 });/*
25264  * Based on:
25265  * Ext JS Library 1.1.1
25266  * Copyright(c) 2006-2007, Ext JS, LLC.
25267  *
25268  * Originally Released Under LGPL - original licence link has changed is not relivant.
25269  *
25270  * Fork - LGPL
25271  * <script type="text/javascript">
25272  */
25273  (function(){ 
25274 /**
25275  * @class Roo.Layer
25276  * @extends Roo.Element
25277  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25278  * automatic maintaining of shadow/shim positions.
25279  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25280  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25281  * you can pass a string with a CSS class name. False turns off the shadow.
25282  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25283  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25284  * @cfg {String} cls CSS class to add to the element
25285  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25286  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25287  * @constructor
25288  * @param {Object} config An object with config options.
25289  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25290  */
25291
25292 Roo.Layer = function(config, existingEl){
25293     config = config || {};
25294     var dh = Roo.DomHelper;
25295     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25296     if(existingEl){
25297         this.dom = Roo.getDom(existingEl);
25298     }
25299     if(!this.dom){
25300         var o = config.dh || {tag: "div", cls: "x-layer"};
25301         this.dom = dh.append(pel, o);
25302     }
25303     if(config.cls){
25304         this.addClass(config.cls);
25305     }
25306     this.constrain = config.constrain !== false;
25307     this.visibilityMode = Roo.Element.VISIBILITY;
25308     if(config.id){
25309         this.id = this.dom.id = config.id;
25310     }else{
25311         this.id = Roo.id(this.dom);
25312     }
25313     this.zindex = config.zindex || this.getZIndex();
25314     this.position("absolute", this.zindex);
25315     if(config.shadow){
25316         this.shadowOffset = config.shadowOffset || 4;
25317         this.shadow = new Roo.Shadow({
25318             offset : this.shadowOffset,
25319             mode : config.shadow
25320         });
25321     }else{
25322         this.shadowOffset = 0;
25323     }
25324     this.useShim = config.shim !== false && Roo.useShims;
25325     this.useDisplay = config.useDisplay;
25326     this.hide();
25327 };
25328
25329 var supr = Roo.Element.prototype;
25330
25331 // shims are shared among layer to keep from having 100 iframes
25332 var shims = [];
25333
25334 Roo.extend(Roo.Layer, Roo.Element, {
25335
25336     getZIndex : function(){
25337         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25338     },
25339
25340     getShim : function(){
25341         if(!this.useShim){
25342             return null;
25343         }
25344         if(this.shim){
25345             return this.shim;
25346         }
25347         var shim = shims.shift();
25348         if(!shim){
25349             shim = this.createShim();
25350             shim.enableDisplayMode('block');
25351             shim.dom.style.display = 'none';
25352             shim.dom.style.visibility = 'visible';
25353         }
25354         var pn = this.dom.parentNode;
25355         if(shim.dom.parentNode != pn){
25356             pn.insertBefore(shim.dom, this.dom);
25357         }
25358         shim.setStyle('z-index', this.getZIndex()-2);
25359         this.shim = shim;
25360         return shim;
25361     },
25362
25363     hideShim : function(){
25364         if(this.shim){
25365             this.shim.setDisplayed(false);
25366             shims.push(this.shim);
25367             delete this.shim;
25368         }
25369     },
25370
25371     disableShadow : function(){
25372         if(this.shadow){
25373             this.shadowDisabled = true;
25374             this.shadow.hide();
25375             this.lastShadowOffset = this.shadowOffset;
25376             this.shadowOffset = 0;
25377         }
25378     },
25379
25380     enableShadow : function(show){
25381         if(this.shadow){
25382             this.shadowDisabled = false;
25383             this.shadowOffset = this.lastShadowOffset;
25384             delete this.lastShadowOffset;
25385             if(show){
25386                 this.sync(true);
25387             }
25388         }
25389     },
25390
25391     // private
25392     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25393     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25394     sync : function(doShow){
25395         var sw = this.shadow;
25396         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25397             var sh = this.getShim();
25398
25399             var w = this.getWidth(),
25400                 h = this.getHeight();
25401
25402             var l = this.getLeft(true),
25403                 t = this.getTop(true);
25404
25405             if(sw && !this.shadowDisabled){
25406                 if(doShow && !sw.isVisible()){
25407                     sw.show(this);
25408                 }else{
25409                     sw.realign(l, t, w, h);
25410                 }
25411                 if(sh){
25412                     if(doShow){
25413                        sh.show();
25414                     }
25415                     // fit the shim behind the shadow, so it is shimmed too
25416                     var a = sw.adjusts, s = sh.dom.style;
25417                     s.left = (Math.min(l, l+a.l))+"px";
25418                     s.top = (Math.min(t, t+a.t))+"px";
25419                     s.width = (w+a.w)+"px";
25420                     s.height = (h+a.h)+"px";
25421                 }
25422             }else if(sh){
25423                 if(doShow){
25424                    sh.show();
25425                 }
25426                 sh.setSize(w, h);
25427                 sh.setLeftTop(l, t);
25428             }
25429             
25430         }
25431     },
25432
25433     // private
25434     destroy : function(){
25435         this.hideShim();
25436         if(this.shadow){
25437             this.shadow.hide();
25438         }
25439         this.removeAllListeners();
25440         var pn = this.dom.parentNode;
25441         if(pn){
25442             pn.removeChild(this.dom);
25443         }
25444         Roo.Element.uncache(this.id);
25445     },
25446
25447     remove : function(){
25448         this.destroy();
25449     },
25450
25451     // private
25452     beginUpdate : function(){
25453         this.updating = true;
25454     },
25455
25456     // private
25457     endUpdate : function(){
25458         this.updating = false;
25459         this.sync(true);
25460     },
25461
25462     // private
25463     hideUnders : function(negOffset){
25464         if(this.shadow){
25465             this.shadow.hide();
25466         }
25467         this.hideShim();
25468     },
25469
25470     // private
25471     constrainXY : function(){
25472         if(this.constrain){
25473             var vw = Roo.lib.Dom.getViewWidth(),
25474                 vh = Roo.lib.Dom.getViewHeight();
25475             var s = Roo.get(document).getScroll();
25476
25477             var xy = this.getXY();
25478             var x = xy[0], y = xy[1];   
25479             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25480             // only move it if it needs it
25481             var moved = false;
25482             // first validate right/bottom
25483             if((x + w) > vw+s.left){
25484                 x = vw - w - this.shadowOffset;
25485                 moved = true;
25486             }
25487             if((y + h) > vh+s.top){
25488                 y = vh - h - this.shadowOffset;
25489                 moved = true;
25490             }
25491             // then make sure top/left isn't negative
25492             if(x < s.left){
25493                 x = s.left;
25494                 moved = true;
25495             }
25496             if(y < s.top){
25497                 y = s.top;
25498                 moved = true;
25499             }
25500             if(moved){
25501                 if(this.avoidY){
25502                     var ay = this.avoidY;
25503                     if(y <= ay && (y+h) >= ay){
25504                         y = ay-h-5;   
25505                     }
25506                 }
25507                 xy = [x, y];
25508                 this.storeXY(xy);
25509                 supr.setXY.call(this, xy);
25510                 this.sync();
25511             }
25512         }
25513     },
25514
25515     isVisible : function(){
25516         return this.visible;    
25517     },
25518
25519     // private
25520     showAction : function(){
25521         this.visible = true; // track visibility to prevent getStyle calls
25522         if(this.useDisplay === true){
25523             this.setDisplayed("");
25524         }else if(this.lastXY){
25525             supr.setXY.call(this, this.lastXY);
25526         }else if(this.lastLT){
25527             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25528         }
25529     },
25530
25531     // private
25532     hideAction : function(){
25533         this.visible = false;
25534         if(this.useDisplay === true){
25535             this.setDisplayed(false);
25536         }else{
25537             this.setLeftTop(-10000,-10000);
25538         }
25539     },
25540
25541     // overridden Element method
25542     setVisible : function(v, a, d, c, e){
25543         if(v){
25544             this.showAction();
25545         }
25546         if(a && v){
25547             var cb = function(){
25548                 this.sync(true);
25549                 if(c){
25550                     c();
25551                 }
25552             }.createDelegate(this);
25553             supr.setVisible.call(this, true, true, d, cb, e);
25554         }else{
25555             if(!v){
25556                 this.hideUnders(true);
25557             }
25558             var cb = c;
25559             if(a){
25560                 cb = function(){
25561                     this.hideAction();
25562                     if(c){
25563                         c();
25564                     }
25565                 }.createDelegate(this);
25566             }
25567             supr.setVisible.call(this, v, a, d, cb, e);
25568             if(v){
25569                 this.sync(true);
25570             }else if(!a){
25571                 this.hideAction();
25572             }
25573         }
25574     },
25575
25576     storeXY : function(xy){
25577         delete this.lastLT;
25578         this.lastXY = xy;
25579     },
25580
25581     storeLeftTop : function(left, top){
25582         delete this.lastXY;
25583         this.lastLT = [left, top];
25584     },
25585
25586     // private
25587     beforeFx : function(){
25588         this.beforeAction();
25589         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25590     },
25591
25592     // private
25593     afterFx : function(){
25594         Roo.Layer.superclass.afterFx.apply(this, arguments);
25595         this.sync(this.isVisible());
25596     },
25597
25598     // private
25599     beforeAction : function(){
25600         if(!this.updating && this.shadow){
25601             this.shadow.hide();
25602         }
25603     },
25604
25605     // overridden Element method
25606     setLeft : function(left){
25607         this.storeLeftTop(left, this.getTop(true));
25608         supr.setLeft.apply(this, arguments);
25609         this.sync();
25610     },
25611
25612     setTop : function(top){
25613         this.storeLeftTop(this.getLeft(true), top);
25614         supr.setTop.apply(this, arguments);
25615         this.sync();
25616     },
25617
25618     setLeftTop : function(left, top){
25619         this.storeLeftTop(left, top);
25620         supr.setLeftTop.apply(this, arguments);
25621         this.sync();
25622     },
25623
25624     setXY : function(xy, a, d, c, e){
25625         this.fixDisplay();
25626         this.beforeAction();
25627         this.storeXY(xy);
25628         var cb = this.createCB(c);
25629         supr.setXY.call(this, xy, a, d, cb, e);
25630         if(!a){
25631             cb();
25632         }
25633     },
25634
25635     // private
25636     createCB : function(c){
25637         var el = this;
25638         return function(){
25639             el.constrainXY();
25640             el.sync(true);
25641             if(c){
25642                 c();
25643             }
25644         };
25645     },
25646
25647     // overridden Element method
25648     setX : function(x, a, d, c, e){
25649         this.setXY([x, this.getY()], a, d, c, e);
25650     },
25651
25652     // overridden Element method
25653     setY : function(y, a, d, c, e){
25654         this.setXY([this.getX(), y], a, d, c, e);
25655     },
25656
25657     // overridden Element method
25658     setSize : function(w, h, a, d, c, e){
25659         this.beforeAction();
25660         var cb = this.createCB(c);
25661         supr.setSize.call(this, w, h, a, d, cb, e);
25662         if(!a){
25663             cb();
25664         }
25665     },
25666
25667     // overridden Element method
25668     setWidth : function(w, a, d, c, e){
25669         this.beforeAction();
25670         var cb = this.createCB(c);
25671         supr.setWidth.call(this, w, a, d, cb, e);
25672         if(!a){
25673             cb();
25674         }
25675     },
25676
25677     // overridden Element method
25678     setHeight : function(h, a, d, c, e){
25679         this.beforeAction();
25680         var cb = this.createCB(c);
25681         supr.setHeight.call(this, h, a, d, cb, e);
25682         if(!a){
25683             cb();
25684         }
25685     },
25686
25687     // overridden Element method
25688     setBounds : function(x, y, w, h, a, d, c, e){
25689         this.beforeAction();
25690         var cb = this.createCB(c);
25691         if(!a){
25692             this.storeXY([x, y]);
25693             supr.setXY.call(this, [x, y]);
25694             supr.setSize.call(this, w, h, a, d, cb, e);
25695             cb();
25696         }else{
25697             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25698         }
25699         return this;
25700     },
25701     
25702     /**
25703      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25704      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25705      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25706      * @param {Number} zindex The new z-index to set
25707      * @return {this} The Layer
25708      */
25709     setZIndex : function(zindex){
25710         this.zindex = zindex;
25711         this.setStyle("z-index", zindex + 2);
25712         if(this.shadow){
25713             this.shadow.setZIndex(zindex + 1);
25714         }
25715         if(this.shim){
25716             this.shim.setStyle("z-index", zindex);
25717         }
25718     }
25719 });
25720 })();/*
25721  * Based on:
25722  * Ext JS Library 1.1.1
25723  * Copyright(c) 2006-2007, Ext JS, LLC.
25724  *
25725  * Originally Released Under LGPL - original licence link has changed is not relivant.
25726  *
25727  * Fork - LGPL
25728  * <script type="text/javascript">
25729  */
25730
25731
25732 /**
25733  * @class Roo.Shadow
25734  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25735  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25736  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25737  * @constructor
25738  * Create a new Shadow
25739  * @param {Object} config The config object
25740  */
25741 Roo.Shadow = function(config){
25742     Roo.apply(this, config);
25743     if(typeof this.mode != "string"){
25744         this.mode = this.defaultMode;
25745     }
25746     var o = this.offset, a = {h: 0};
25747     var rad = Math.floor(this.offset/2);
25748     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25749         case "drop":
25750             a.w = 0;
25751             a.l = a.t = o;
25752             a.t -= 1;
25753             if(Roo.isIE){
25754                 a.l -= this.offset + rad;
25755                 a.t -= this.offset + rad;
25756                 a.w -= rad;
25757                 a.h -= rad;
25758                 a.t += 1;
25759             }
25760         break;
25761         case "sides":
25762             a.w = (o*2);
25763             a.l = -o;
25764             a.t = o-1;
25765             if(Roo.isIE){
25766                 a.l -= (this.offset - rad);
25767                 a.t -= this.offset + rad;
25768                 a.l += 1;
25769                 a.w -= (this.offset - rad)*2;
25770                 a.w -= rad + 1;
25771                 a.h -= 1;
25772             }
25773         break;
25774         case "frame":
25775             a.w = a.h = (o*2);
25776             a.l = a.t = -o;
25777             a.t += 1;
25778             a.h -= 2;
25779             if(Roo.isIE){
25780                 a.l -= (this.offset - rad);
25781                 a.t -= (this.offset - rad);
25782                 a.l += 1;
25783                 a.w -= (this.offset + rad + 1);
25784                 a.h -= (this.offset + rad);
25785                 a.h += 1;
25786             }
25787         break;
25788     };
25789
25790     this.adjusts = a;
25791 };
25792
25793 Roo.Shadow.prototype = {
25794     /**
25795      * @cfg {String} mode
25796      * The shadow display mode.  Supports the following options:<br />
25797      * sides: Shadow displays on both sides and bottom only<br />
25798      * frame: Shadow displays equally on all four sides<br />
25799      * drop: Traditional bottom-right drop shadow (default)
25800      */
25801     /**
25802      * @cfg {String} offset
25803      * The number of pixels to offset the shadow from the element (defaults to 4)
25804      */
25805     offset: 4,
25806
25807     // private
25808     defaultMode: "drop",
25809
25810     /**
25811      * Displays the shadow under the target element
25812      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25813      */
25814     show : function(target){
25815         target = Roo.get(target);
25816         if(!this.el){
25817             this.el = Roo.Shadow.Pool.pull();
25818             if(this.el.dom.nextSibling != target.dom){
25819                 this.el.insertBefore(target);
25820             }
25821         }
25822         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25823         if(Roo.isIE){
25824             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25825         }
25826         this.realign(
25827             target.getLeft(true),
25828             target.getTop(true),
25829             target.getWidth(),
25830             target.getHeight()
25831         );
25832         this.el.dom.style.display = "block";
25833     },
25834
25835     /**
25836      * Returns true if the shadow is visible, else false
25837      */
25838     isVisible : function(){
25839         return this.el ? true : false;  
25840     },
25841
25842     /**
25843      * Direct alignment when values are already available. Show must be called at least once before
25844      * calling this method to ensure it is initialized.
25845      * @param {Number} left The target element left position
25846      * @param {Number} top The target element top position
25847      * @param {Number} width The target element width
25848      * @param {Number} height The target element height
25849      */
25850     realign : function(l, t, w, h){
25851         if(!this.el){
25852             return;
25853         }
25854         var a = this.adjusts, d = this.el.dom, s = d.style;
25855         var iea = 0;
25856         s.left = (l+a.l)+"px";
25857         s.top = (t+a.t)+"px";
25858         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25859  
25860         if(s.width != sws || s.height != shs){
25861             s.width = sws;
25862             s.height = shs;
25863             if(!Roo.isIE){
25864                 var cn = d.childNodes;
25865                 var sww = Math.max(0, (sw-12))+"px";
25866                 cn[0].childNodes[1].style.width = sww;
25867                 cn[1].childNodes[1].style.width = sww;
25868                 cn[2].childNodes[1].style.width = sww;
25869                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25870             }
25871         }
25872     },
25873
25874     /**
25875      * Hides this shadow
25876      */
25877     hide : function(){
25878         if(this.el){
25879             this.el.dom.style.display = "none";
25880             Roo.Shadow.Pool.push(this.el);
25881             delete this.el;
25882         }
25883     },
25884
25885     /**
25886      * Adjust the z-index of this shadow
25887      * @param {Number} zindex The new z-index
25888      */
25889     setZIndex : function(z){
25890         this.zIndex = z;
25891         if(this.el){
25892             this.el.setStyle("z-index", z);
25893         }
25894     }
25895 };
25896
25897 // Private utility class that manages the internal Shadow cache
25898 Roo.Shadow.Pool = function(){
25899     var p = [];
25900     var markup = Roo.isIE ?
25901                  '<div class="x-ie-shadow"></div>' :
25902                  '<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>';
25903     return {
25904         pull : function(){
25905             var sh = p.shift();
25906             if(!sh){
25907                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25908                 sh.autoBoxAdjust = false;
25909             }
25910             return sh;
25911         },
25912
25913         push : function(sh){
25914             p.push(sh);
25915         }
25916     };
25917 }();/*
25918  * Based on:
25919  * Ext JS Library 1.1.1
25920  * Copyright(c) 2006-2007, Ext JS, LLC.
25921  *
25922  * Originally Released Under LGPL - original licence link has changed is not relivant.
25923  *
25924  * Fork - LGPL
25925  * <script type="text/javascript">
25926  */
25927
25928
25929 /**
25930  * @class Roo.SplitBar
25931  * @extends Roo.util.Observable
25932  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25933  * <br><br>
25934  * Usage:
25935  * <pre><code>
25936 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25937                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25938 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25939 split.minSize = 100;
25940 split.maxSize = 600;
25941 split.animate = true;
25942 split.on('moved', splitterMoved);
25943 </code></pre>
25944  * @constructor
25945  * Create a new SplitBar
25946  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25947  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25948  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25949  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25950                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25951                         position of the SplitBar).
25952  */
25953 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25954     
25955     /** @private */
25956     this.el = Roo.get(dragElement, true);
25957     this.el.dom.unselectable = "on";
25958     /** @private */
25959     this.resizingEl = Roo.get(resizingElement, true);
25960
25961     /**
25962      * @private
25963      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25964      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25965      * @type Number
25966      */
25967     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25968     
25969     /**
25970      * The minimum size of the resizing element. (Defaults to 0)
25971      * @type Number
25972      */
25973     this.minSize = 0;
25974     
25975     /**
25976      * The maximum size of the resizing element. (Defaults to 2000)
25977      * @type Number
25978      */
25979     this.maxSize = 2000;
25980     
25981     /**
25982      * Whether to animate the transition to the new size
25983      * @type Boolean
25984      */
25985     this.animate = false;
25986     
25987     /**
25988      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25989      * @type Boolean
25990      */
25991     this.useShim = false;
25992     
25993     /** @private */
25994     this.shim = null;
25995     
25996     if(!existingProxy){
25997         /** @private */
25998         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25999     }else{
26000         this.proxy = Roo.get(existingProxy).dom;
26001     }
26002     /** @private */
26003     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26004     
26005     /** @private */
26006     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26007     
26008     /** @private */
26009     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26010     
26011     /** @private */
26012     this.dragSpecs = {};
26013     
26014     /**
26015      * @private The adapter to use to positon and resize elements
26016      */
26017     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26018     this.adapter.init(this);
26019     
26020     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26021         /** @private */
26022         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26023         this.el.addClass("x-splitbar-h");
26024     }else{
26025         /** @private */
26026         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26027         this.el.addClass("x-splitbar-v");
26028     }
26029     
26030     this.addEvents({
26031         /**
26032          * @event resize
26033          * Fires when the splitter is moved (alias for {@link #event-moved})
26034          * @param {Roo.SplitBar} this
26035          * @param {Number} newSize the new width or height
26036          */
26037         "resize" : true,
26038         /**
26039          * @event moved
26040          * Fires when the splitter is moved
26041          * @param {Roo.SplitBar} this
26042          * @param {Number} newSize the new width or height
26043          */
26044         "moved" : true,
26045         /**
26046          * @event beforeresize
26047          * Fires before the splitter is dragged
26048          * @param {Roo.SplitBar} this
26049          */
26050         "beforeresize" : true,
26051
26052         "beforeapply" : true
26053     });
26054
26055     Roo.util.Observable.call(this);
26056 };
26057
26058 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26059     onStartProxyDrag : function(x, y){
26060         this.fireEvent("beforeresize", this);
26061         if(!this.overlay){
26062             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26063             o.unselectable();
26064             o.enableDisplayMode("block");
26065             // all splitbars share the same overlay
26066             Roo.SplitBar.prototype.overlay = o;
26067         }
26068         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26069         this.overlay.show();
26070         Roo.get(this.proxy).setDisplayed("block");
26071         var size = this.adapter.getElementSize(this);
26072         this.activeMinSize = this.getMinimumSize();;
26073         this.activeMaxSize = this.getMaximumSize();;
26074         var c1 = size - this.activeMinSize;
26075         var c2 = Math.max(this.activeMaxSize - size, 0);
26076         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26077             this.dd.resetConstraints();
26078             this.dd.setXConstraint(
26079                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26080                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26081             );
26082             this.dd.setYConstraint(0, 0);
26083         }else{
26084             this.dd.resetConstraints();
26085             this.dd.setXConstraint(0, 0);
26086             this.dd.setYConstraint(
26087                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26088                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26089             );
26090          }
26091         this.dragSpecs.startSize = size;
26092         this.dragSpecs.startPoint = [x, y];
26093         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26094     },
26095     
26096     /** 
26097      * @private Called after the drag operation by the DDProxy
26098      */
26099     onEndProxyDrag : function(e){
26100         Roo.get(this.proxy).setDisplayed(false);
26101         var endPoint = Roo.lib.Event.getXY(e);
26102         if(this.overlay){
26103             this.overlay.hide();
26104         }
26105         var newSize;
26106         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26107             newSize = this.dragSpecs.startSize + 
26108                 (this.placement == Roo.SplitBar.LEFT ?
26109                     endPoint[0] - this.dragSpecs.startPoint[0] :
26110                     this.dragSpecs.startPoint[0] - endPoint[0]
26111                 );
26112         }else{
26113             newSize = this.dragSpecs.startSize + 
26114                 (this.placement == Roo.SplitBar.TOP ?
26115                     endPoint[1] - this.dragSpecs.startPoint[1] :
26116                     this.dragSpecs.startPoint[1] - endPoint[1]
26117                 );
26118         }
26119         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26120         if(newSize != this.dragSpecs.startSize){
26121             if(this.fireEvent('beforeapply', this, newSize) !== false){
26122                 this.adapter.setElementSize(this, newSize);
26123                 this.fireEvent("moved", this, newSize);
26124                 this.fireEvent("resize", this, newSize);
26125             }
26126         }
26127     },
26128     
26129     /**
26130      * Get the adapter this SplitBar uses
26131      * @return The adapter object
26132      */
26133     getAdapter : function(){
26134         return this.adapter;
26135     },
26136     
26137     /**
26138      * Set the adapter this SplitBar uses
26139      * @param {Object} adapter A SplitBar adapter object
26140      */
26141     setAdapter : function(adapter){
26142         this.adapter = adapter;
26143         this.adapter.init(this);
26144     },
26145     
26146     /**
26147      * Gets the minimum size for the resizing element
26148      * @return {Number} The minimum size
26149      */
26150     getMinimumSize : function(){
26151         return this.minSize;
26152     },
26153     
26154     /**
26155      * Sets the minimum size for the resizing element
26156      * @param {Number} minSize The minimum size
26157      */
26158     setMinimumSize : function(minSize){
26159         this.minSize = minSize;
26160     },
26161     
26162     /**
26163      * Gets the maximum size for the resizing element
26164      * @return {Number} The maximum size
26165      */
26166     getMaximumSize : function(){
26167         return this.maxSize;
26168     },
26169     
26170     /**
26171      * Sets the maximum size for the resizing element
26172      * @param {Number} maxSize The maximum size
26173      */
26174     setMaximumSize : function(maxSize){
26175         this.maxSize = maxSize;
26176     },
26177     
26178     /**
26179      * Sets the initialize size for the resizing element
26180      * @param {Number} size The initial size
26181      */
26182     setCurrentSize : function(size){
26183         var oldAnimate = this.animate;
26184         this.animate = false;
26185         this.adapter.setElementSize(this, size);
26186         this.animate = oldAnimate;
26187     },
26188     
26189     /**
26190      * Destroy this splitbar. 
26191      * @param {Boolean} removeEl True to remove the element
26192      */
26193     destroy : function(removeEl){
26194         if(this.shim){
26195             this.shim.remove();
26196         }
26197         this.dd.unreg();
26198         this.proxy.parentNode.removeChild(this.proxy);
26199         if(removeEl){
26200             this.el.remove();
26201         }
26202     }
26203 });
26204
26205 /**
26206  * @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.
26207  */
26208 Roo.SplitBar.createProxy = function(dir){
26209     var proxy = new Roo.Element(document.createElement("div"));
26210     proxy.unselectable();
26211     var cls = 'x-splitbar-proxy';
26212     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26213     document.body.appendChild(proxy.dom);
26214     return proxy.dom;
26215 };
26216
26217 /** 
26218  * @class Roo.SplitBar.BasicLayoutAdapter
26219  * Default Adapter. It assumes the splitter and resizing element are not positioned
26220  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26221  */
26222 Roo.SplitBar.BasicLayoutAdapter = function(){
26223 };
26224
26225 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26226     // do nothing for now
26227     init : function(s){
26228     
26229     },
26230     /**
26231      * Called before drag operations to get the current size of the resizing element. 
26232      * @param {Roo.SplitBar} s The SplitBar using this adapter
26233      */
26234      getElementSize : function(s){
26235         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26236             return s.resizingEl.getWidth();
26237         }else{
26238             return s.resizingEl.getHeight();
26239         }
26240     },
26241     
26242     /**
26243      * Called after drag operations to set the size of the resizing element.
26244      * @param {Roo.SplitBar} s The SplitBar using this adapter
26245      * @param {Number} newSize The new size to set
26246      * @param {Function} onComplete A function to be invoked when resizing is complete
26247      */
26248     setElementSize : function(s, newSize, onComplete){
26249         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26250             if(!s.animate){
26251                 s.resizingEl.setWidth(newSize);
26252                 if(onComplete){
26253                     onComplete(s, newSize);
26254                 }
26255             }else{
26256                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26257             }
26258         }else{
26259             
26260             if(!s.animate){
26261                 s.resizingEl.setHeight(newSize);
26262                 if(onComplete){
26263                     onComplete(s, newSize);
26264                 }
26265             }else{
26266                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26267             }
26268         }
26269     }
26270 };
26271
26272 /** 
26273  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26274  * @extends Roo.SplitBar.BasicLayoutAdapter
26275  * Adapter that  moves the splitter element to align with the resized sizing element. 
26276  * Used with an absolute positioned SplitBar.
26277  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26278  * document.body, make sure you assign an id to the body element.
26279  */
26280 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26281     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26282     this.container = Roo.get(container);
26283 };
26284
26285 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26286     init : function(s){
26287         this.basic.init(s);
26288     },
26289     
26290     getElementSize : function(s){
26291         return this.basic.getElementSize(s);
26292     },
26293     
26294     setElementSize : function(s, newSize, onComplete){
26295         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26296     },
26297     
26298     moveSplitter : function(s){
26299         var yes = Roo.SplitBar;
26300         switch(s.placement){
26301             case yes.LEFT:
26302                 s.el.setX(s.resizingEl.getRight());
26303                 break;
26304             case yes.RIGHT:
26305                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26306                 break;
26307             case yes.TOP:
26308                 s.el.setY(s.resizingEl.getBottom());
26309                 break;
26310             case yes.BOTTOM:
26311                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26312                 break;
26313         }
26314     }
26315 };
26316
26317 /**
26318  * Orientation constant - Create a vertical SplitBar
26319  * @static
26320  * @type Number
26321  */
26322 Roo.SplitBar.VERTICAL = 1;
26323
26324 /**
26325  * Orientation constant - Create a horizontal SplitBar
26326  * @static
26327  * @type Number
26328  */
26329 Roo.SplitBar.HORIZONTAL = 2;
26330
26331 /**
26332  * Placement constant - The resizing element is to the left of the splitter element
26333  * @static
26334  * @type Number
26335  */
26336 Roo.SplitBar.LEFT = 1;
26337
26338 /**
26339  * Placement constant - The resizing element is to the right of the splitter element
26340  * @static
26341  * @type Number
26342  */
26343 Roo.SplitBar.RIGHT = 2;
26344
26345 /**
26346  * Placement constant - The resizing element is positioned above the splitter element
26347  * @static
26348  * @type Number
26349  */
26350 Roo.SplitBar.TOP = 3;
26351
26352 /**
26353  * Placement constant - The resizing element is positioned under splitter element
26354  * @static
26355  * @type Number
26356  */
26357 Roo.SplitBar.BOTTOM = 4;
26358 /*
26359  * Based on:
26360  * Ext JS Library 1.1.1
26361  * Copyright(c) 2006-2007, Ext JS, LLC.
26362  *
26363  * Originally Released Under LGPL - original licence link has changed is not relivant.
26364  *
26365  * Fork - LGPL
26366  * <script type="text/javascript">
26367  */
26368
26369 /**
26370  * @class Roo.View
26371  * @extends Roo.util.Observable
26372  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26373  * This class also supports single and multi selection modes. <br>
26374  * Create a data model bound view:
26375  <pre><code>
26376  var store = new Roo.data.Store(...);
26377
26378  var view = new Roo.View({
26379     el : "my-element",
26380     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26381  
26382     singleSelect: true,
26383     selectedClass: "ydataview-selected",
26384     store: store
26385  });
26386
26387  // listen for node click?
26388  view.on("click", function(vw, index, node, e){
26389  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26390  });
26391
26392  // load XML data
26393  dataModel.load("foobar.xml");
26394  </code></pre>
26395  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26396  * <br><br>
26397  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26398  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26399  * 
26400  * Note: old style constructor is still suported (container, template, config)
26401  * 
26402  * @constructor
26403  * Create a new View
26404  * @param {Object} config The config object
26405  * 
26406  */
26407 Roo.View = function(config, depreciated_tpl, depreciated_config){
26408     
26409     this.parent = false;
26410     
26411     if (typeof(depreciated_tpl) == 'undefined') {
26412         // new way.. - universal constructor.
26413         Roo.apply(this, config);
26414         this.el  = Roo.get(this.el);
26415     } else {
26416         // old format..
26417         this.el  = Roo.get(config);
26418         this.tpl = depreciated_tpl;
26419         Roo.apply(this, depreciated_config);
26420     }
26421     this.wrapEl  = this.el.wrap().wrap();
26422     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26423     
26424     
26425     if(typeof(this.tpl) == "string"){
26426         this.tpl = new Roo.Template(this.tpl);
26427     } else {
26428         // support xtype ctors..
26429         this.tpl = new Roo.factory(this.tpl, Roo);
26430     }
26431     
26432     
26433     this.tpl.compile();
26434     
26435     /** @private */
26436     this.addEvents({
26437         /**
26438          * @event beforeclick
26439          * Fires before a click is processed. Returns false to cancel the default action.
26440          * @param {Roo.View} this
26441          * @param {Number} index The index of the target node
26442          * @param {HTMLElement} node The target node
26443          * @param {Roo.EventObject} e The raw event object
26444          */
26445             "beforeclick" : true,
26446         /**
26447          * @event click
26448          * Fires when a template node is clicked.
26449          * @param {Roo.View} this
26450          * @param {Number} index The index of the target node
26451          * @param {HTMLElement} node The target node
26452          * @param {Roo.EventObject} e The raw event object
26453          */
26454             "click" : true,
26455         /**
26456          * @event dblclick
26457          * Fires when a template node is double clicked.
26458          * @param {Roo.View} this
26459          * @param {Number} index The index of the target node
26460          * @param {HTMLElement} node The target node
26461          * @param {Roo.EventObject} e The raw event object
26462          */
26463             "dblclick" : true,
26464         /**
26465          * @event contextmenu
26466          * Fires when a template node is right clicked.
26467          * @param {Roo.View} this
26468          * @param {Number} index The index of the target node
26469          * @param {HTMLElement} node The target node
26470          * @param {Roo.EventObject} e The raw event object
26471          */
26472             "contextmenu" : true,
26473         /**
26474          * @event selectionchange
26475          * Fires when the selected nodes change.
26476          * @param {Roo.View} this
26477          * @param {Array} selections Array of the selected nodes
26478          */
26479             "selectionchange" : true,
26480     
26481         /**
26482          * @event beforeselect
26483          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26484          * @param {Roo.View} this
26485          * @param {HTMLElement} node The node to be selected
26486          * @param {Array} selections Array of currently selected nodes
26487          */
26488             "beforeselect" : true,
26489         /**
26490          * @event preparedata
26491          * Fires on every row to render, to allow you to change the data.
26492          * @param {Roo.View} this
26493          * @param {Object} data to be rendered (change this)
26494          */
26495           "preparedata" : true
26496           
26497           
26498         });
26499
26500
26501
26502     this.el.on({
26503         "click": this.onClick,
26504         "dblclick": this.onDblClick,
26505         "contextmenu": this.onContextMenu,
26506         scope:this
26507     });
26508
26509     this.selections = [];
26510     this.nodes = [];
26511     this.cmp = new Roo.CompositeElementLite([]);
26512     if(this.store){
26513         this.store = Roo.factory(this.store, Roo.data);
26514         this.setStore(this.store, true);
26515     }
26516     
26517     if ( this.footer && this.footer.xtype) {
26518            
26519          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26520         
26521         this.footer.dataSource = this.store;
26522         this.footer.container = fctr;
26523         this.footer = Roo.factory(this.footer, Roo);
26524         fctr.insertFirst(this.el);
26525         
26526         // this is a bit insane - as the paging toolbar seems to detach the el..
26527 //        dom.parentNode.parentNode.parentNode
26528          // they get detached?
26529     }
26530     
26531     
26532     Roo.View.superclass.constructor.call(this);
26533     
26534     
26535 };
26536
26537 Roo.extend(Roo.View, Roo.util.Observable, {
26538     
26539      /**
26540      * @cfg {Roo.data.Store} store Data store to load data from.
26541      */
26542     store : false,
26543     
26544     /**
26545      * @cfg {String|Roo.Element} el The container element.
26546      */
26547     el : '',
26548     
26549     /**
26550      * @cfg {String|Roo.Template} tpl The template used by this View 
26551      */
26552     tpl : false,
26553     /**
26554      * @cfg {String} dataName the named area of the template to use as the data area
26555      *                          Works with domtemplates roo-name="name"
26556      */
26557     dataName: false,
26558     /**
26559      * @cfg {String} selectedClass The css class to add to selected nodes
26560      */
26561     selectedClass : "x-view-selected",
26562      /**
26563      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26564      */
26565     emptyText : "",
26566     
26567     /**
26568      * @cfg {String} text to display on mask (default Loading)
26569      */
26570     mask : false,
26571     /**
26572      * @cfg {Boolean} multiSelect Allow multiple selection
26573      */
26574     multiSelect : false,
26575     /**
26576      * @cfg {Boolean} singleSelect Allow single selection
26577      */
26578     singleSelect:  false,
26579     
26580     /**
26581      * @cfg {Boolean} toggleSelect - selecting 
26582      */
26583     toggleSelect : false,
26584     
26585     /**
26586      * @cfg {Boolean} tickable - selecting 
26587      */
26588     tickable : false,
26589     
26590     /**
26591      * Returns the element this view is bound to.
26592      * @return {Roo.Element}
26593      */
26594     getEl : function(){
26595         return this.wrapEl;
26596     },
26597     
26598     
26599
26600     /**
26601      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26602      */
26603     refresh : function(){
26604         //Roo.log('refresh');
26605         var t = this.tpl;
26606         
26607         // if we are using something like 'domtemplate', then
26608         // the what gets used is:
26609         // t.applySubtemplate(NAME, data, wrapping data..)
26610         // the outer template then get' applied with
26611         //     the store 'extra data'
26612         // and the body get's added to the
26613         //      roo-name="data" node?
26614         //      <span class='roo-tpl-{name}'></span> ?????
26615         
26616         
26617         
26618         this.clearSelections();
26619         this.el.update("");
26620         var html = [];
26621         var records = this.store.getRange();
26622         if(records.length < 1) {
26623             
26624             // is this valid??  = should it render a template??
26625             
26626             this.el.update(this.emptyText);
26627             return;
26628         }
26629         var el = this.el;
26630         if (this.dataName) {
26631             this.el.update(t.apply(this.store.meta)); //????
26632             el = this.el.child('.roo-tpl-' + this.dataName);
26633         }
26634         
26635         for(var i = 0, len = records.length; i < len; i++){
26636             var data = this.prepareData(records[i].data, i, records[i]);
26637             this.fireEvent("preparedata", this, data, i, records[i]);
26638             
26639             var d = Roo.apply({}, data);
26640             
26641             if(this.tickable){
26642                 Roo.apply(d, {'roo-id' : Roo.id()});
26643                 
26644                 var _this = this;
26645             
26646                 Roo.each(this.parent.item, function(item){
26647                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26648                         return;
26649                     }
26650                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26651                 });
26652             }
26653             
26654             html[html.length] = Roo.util.Format.trim(
26655                 this.dataName ?
26656                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26657                     t.apply(d)
26658             );
26659         }
26660         
26661         
26662         
26663         el.update(html.join(""));
26664         this.nodes = el.dom.childNodes;
26665         this.updateIndexes(0);
26666     },
26667     
26668
26669     /**
26670      * Function to override to reformat the data that is sent to
26671      * the template for each node.
26672      * DEPRICATED - use the preparedata event handler.
26673      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26674      * a JSON object for an UpdateManager bound view).
26675      */
26676     prepareData : function(data, index, record)
26677     {
26678         this.fireEvent("preparedata", this, data, index, record);
26679         return data;
26680     },
26681
26682     onUpdate : function(ds, record){
26683         // Roo.log('on update');   
26684         this.clearSelections();
26685         var index = this.store.indexOf(record);
26686         var n = this.nodes[index];
26687         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26688         n.parentNode.removeChild(n);
26689         this.updateIndexes(index, index);
26690     },
26691
26692     
26693     
26694 // --------- FIXME     
26695     onAdd : function(ds, records, index)
26696     {
26697         //Roo.log(['on Add', ds, records, index] );        
26698         this.clearSelections();
26699         if(this.nodes.length == 0){
26700             this.refresh();
26701             return;
26702         }
26703         var n = this.nodes[index];
26704         for(var i = 0, len = records.length; i < len; i++){
26705             var d = this.prepareData(records[i].data, i, records[i]);
26706             if(n){
26707                 this.tpl.insertBefore(n, d);
26708             }else{
26709                 
26710                 this.tpl.append(this.el, d);
26711             }
26712         }
26713         this.updateIndexes(index);
26714     },
26715
26716     onRemove : function(ds, record, index){
26717        // Roo.log('onRemove');
26718         this.clearSelections();
26719         var el = this.dataName  ?
26720             this.el.child('.roo-tpl-' + this.dataName) :
26721             this.el; 
26722         
26723         el.dom.removeChild(this.nodes[index]);
26724         this.updateIndexes(index);
26725     },
26726
26727     /**
26728      * Refresh an individual node.
26729      * @param {Number} index
26730      */
26731     refreshNode : function(index){
26732         this.onUpdate(this.store, this.store.getAt(index));
26733     },
26734
26735     updateIndexes : function(startIndex, endIndex){
26736         var ns = this.nodes;
26737         startIndex = startIndex || 0;
26738         endIndex = endIndex || ns.length - 1;
26739         for(var i = startIndex; i <= endIndex; i++){
26740             ns[i].nodeIndex = i;
26741         }
26742     },
26743
26744     /**
26745      * Changes the data store this view uses and refresh the view.
26746      * @param {Store} store
26747      */
26748     setStore : function(store, initial){
26749         if(!initial && this.store){
26750             this.store.un("datachanged", this.refresh);
26751             this.store.un("add", this.onAdd);
26752             this.store.un("remove", this.onRemove);
26753             this.store.un("update", this.onUpdate);
26754             this.store.un("clear", this.refresh);
26755             this.store.un("beforeload", this.onBeforeLoad);
26756             this.store.un("load", this.onLoad);
26757             this.store.un("loadexception", this.onLoad);
26758         }
26759         if(store){
26760           
26761             store.on("datachanged", this.refresh, this);
26762             store.on("add", this.onAdd, this);
26763             store.on("remove", this.onRemove, this);
26764             store.on("update", this.onUpdate, this);
26765             store.on("clear", this.refresh, this);
26766             store.on("beforeload", this.onBeforeLoad, this);
26767             store.on("load", this.onLoad, this);
26768             store.on("loadexception", this.onLoad, this);
26769         }
26770         
26771         if(store){
26772             this.refresh();
26773         }
26774     },
26775     /**
26776      * onbeforeLoad - masks the loading area.
26777      *
26778      */
26779     onBeforeLoad : function(store,opts)
26780     {
26781          //Roo.log('onBeforeLoad');   
26782         if (!opts.add) {
26783             this.el.update("");
26784         }
26785         this.el.mask(this.mask ? this.mask : "Loading" ); 
26786     },
26787     onLoad : function ()
26788     {
26789         this.el.unmask();
26790     },
26791     
26792
26793     /**
26794      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26795      * @param {HTMLElement} node
26796      * @return {HTMLElement} The template node
26797      */
26798     findItemFromChild : function(node){
26799         var el = this.dataName  ?
26800             this.el.child('.roo-tpl-' + this.dataName,true) :
26801             this.el.dom; 
26802         
26803         if(!node || node.parentNode == el){
26804                     return node;
26805             }
26806             var p = node.parentNode;
26807             while(p && p != el){
26808             if(p.parentNode == el){
26809                 return p;
26810             }
26811             p = p.parentNode;
26812         }
26813             return null;
26814     },
26815
26816     /** @ignore */
26817     onClick : function(e){
26818         var item = this.findItemFromChild(e.getTarget());
26819         if(item){
26820             var index = this.indexOf(item);
26821             if(this.onItemClick(item, index, e) !== false){
26822                 this.fireEvent("click", this, index, item, e);
26823             }
26824         }else{
26825             this.clearSelections();
26826         }
26827     },
26828
26829     /** @ignore */
26830     onContextMenu : function(e){
26831         var item = this.findItemFromChild(e.getTarget());
26832         if(item){
26833             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26834         }
26835     },
26836
26837     /** @ignore */
26838     onDblClick : function(e){
26839         var item = this.findItemFromChild(e.getTarget());
26840         if(item){
26841             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26842         }
26843     },
26844
26845     onItemClick : function(item, index, e)
26846     {
26847         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26848             return false;
26849         }
26850         if (this.toggleSelect) {
26851             var m = this.isSelected(item) ? 'unselect' : 'select';
26852             //Roo.log(m);
26853             var _t = this;
26854             _t[m](item, true, false);
26855             return true;
26856         }
26857         if(this.multiSelect || this.singleSelect){
26858             if(this.multiSelect && e.shiftKey && this.lastSelection){
26859                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26860             }else{
26861                 this.select(item, this.multiSelect && e.ctrlKey);
26862                 this.lastSelection = item;
26863             }
26864             
26865             if(!this.tickable){
26866                 e.preventDefault();
26867             }
26868             
26869         }
26870         return true;
26871     },
26872
26873     /**
26874      * Get the number of selected nodes.
26875      * @return {Number}
26876      */
26877     getSelectionCount : function(){
26878         return this.selections.length;
26879     },
26880
26881     /**
26882      * Get the currently selected nodes.
26883      * @return {Array} An array of HTMLElements
26884      */
26885     getSelectedNodes : function(){
26886         return this.selections;
26887     },
26888
26889     /**
26890      * Get the indexes of the selected nodes.
26891      * @return {Array}
26892      */
26893     getSelectedIndexes : function(){
26894         var indexes = [], s = this.selections;
26895         for(var i = 0, len = s.length; i < len; i++){
26896             indexes.push(s[i].nodeIndex);
26897         }
26898         return indexes;
26899     },
26900
26901     /**
26902      * Clear all selections
26903      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26904      */
26905     clearSelections : function(suppressEvent){
26906         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26907             this.cmp.elements = this.selections;
26908             this.cmp.removeClass(this.selectedClass);
26909             this.selections = [];
26910             if(!suppressEvent){
26911                 this.fireEvent("selectionchange", this, this.selections);
26912             }
26913         }
26914     },
26915
26916     /**
26917      * Returns true if the passed node is selected
26918      * @param {HTMLElement/Number} node The node or node index
26919      * @return {Boolean}
26920      */
26921     isSelected : function(node){
26922         var s = this.selections;
26923         if(s.length < 1){
26924             return false;
26925         }
26926         node = this.getNode(node);
26927         return s.indexOf(node) !== -1;
26928     },
26929
26930     /**
26931      * Selects nodes.
26932      * @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
26933      * @param {Boolean} keepExisting (optional) true to keep existing selections
26934      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26935      */
26936     select : function(nodeInfo, keepExisting, suppressEvent){
26937         if(nodeInfo instanceof Array){
26938             if(!keepExisting){
26939                 this.clearSelections(true);
26940             }
26941             for(var i = 0, len = nodeInfo.length; i < len; i++){
26942                 this.select(nodeInfo[i], true, true);
26943             }
26944             return;
26945         } 
26946         var node = this.getNode(nodeInfo);
26947         if(!node || this.isSelected(node)){
26948             return; // already selected.
26949         }
26950         if(!keepExisting){
26951             this.clearSelections(true);
26952         }
26953         
26954         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26955             Roo.fly(node).addClass(this.selectedClass);
26956             this.selections.push(node);
26957             if(!suppressEvent){
26958                 this.fireEvent("selectionchange", this, this.selections);
26959             }
26960         }
26961         
26962         
26963     },
26964       /**
26965      * Unselects nodes.
26966      * @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
26967      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26968      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26969      */
26970     unselect : function(nodeInfo, keepExisting, suppressEvent)
26971     {
26972         if(nodeInfo instanceof Array){
26973             Roo.each(this.selections, function(s) {
26974                 this.unselect(s, nodeInfo);
26975             }, this);
26976             return;
26977         }
26978         var node = this.getNode(nodeInfo);
26979         if(!node || !this.isSelected(node)){
26980             //Roo.log("not selected");
26981             return; // not selected.
26982         }
26983         // fireevent???
26984         var ns = [];
26985         Roo.each(this.selections, function(s) {
26986             if (s == node ) {
26987                 Roo.fly(node).removeClass(this.selectedClass);
26988
26989                 return;
26990             }
26991             ns.push(s);
26992         },this);
26993         
26994         this.selections= ns;
26995         this.fireEvent("selectionchange", this, this.selections);
26996     },
26997
26998     /**
26999      * Gets a template node.
27000      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27001      * @return {HTMLElement} The node or null if it wasn't found
27002      */
27003     getNode : function(nodeInfo){
27004         if(typeof nodeInfo == "string"){
27005             return document.getElementById(nodeInfo);
27006         }else if(typeof nodeInfo == "number"){
27007             return this.nodes[nodeInfo];
27008         }
27009         return nodeInfo;
27010     },
27011
27012     /**
27013      * Gets a range template nodes.
27014      * @param {Number} startIndex
27015      * @param {Number} endIndex
27016      * @return {Array} An array of nodes
27017      */
27018     getNodes : function(start, end){
27019         var ns = this.nodes;
27020         start = start || 0;
27021         end = typeof end == "undefined" ? ns.length - 1 : end;
27022         var nodes = [];
27023         if(start <= end){
27024             for(var i = start; i <= end; i++){
27025                 nodes.push(ns[i]);
27026             }
27027         } else{
27028             for(var i = start; i >= end; i--){
27029                 nodes.push(ns[i]);
27030             }
27031         }
27032         return nodes;
27033     },
27034
27035     /**
27036      * Finds the index of the passed node
27037      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27038      * @return {Number} The index of the node or -1
27039      */
27040     indexOf : function(node){
27041         node = this.getNode(node);
27042         if(typeof node.nodeIndex == "number"){
27043             return node.nodeIndex;
27044         }
27045         var ns = this.nodes;
27046         for(var i = 0, len = ns.length; i < len; i++){
27047             if(ns[i] == node){
27048                 return i;
27049             }
27050         }
27051         return -1;
27052     }
27053 });
27054 /*
27055  * Based on:
27056  * Ext JS Library 1.1.1
27057  * Copyright(c) 2006-2007, Ext JS, LLC.
27058  *
27059  * Originally Released Under LGPL - original licence link has changed is not relivant.
27060  *
27061  * Fork - LGPL
27062  * <script type="text/javascript">
27063  */
27064
27065 /**
27066  * @class Roo.JsonView
27067  * @extends Roo.View
27068  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27069 <pre><code>
27070 var view = new Roo.JsonView({
27071     container: "my-element",
27072     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27073     multiSelect: true, 
27074     jsonRoot: "data" 
27075 });
27076
27077 // listen for node click?
27078 view.on("click", function(vw, index, node, e){
27079     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27080 });
27081
27082 // direct load of JSON data
27083 view.load("foobar.php");
27084
27085 // Example from my blog list
27086 var tpl = new Roo.Template(
27087     '&lt;div class="entry"&gt;' +
27088     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27089     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27090     "&lt;/div&gt;&lt;hr /&gt;"
27091 );
27092
27093 var moreView = new Roo.JsonView({
27094     container :  "entry-list", 
27095     template : tpl,
27096     jsonRoot: "posts"
27097 });
27098 moreView.on("beforerender", this.sortEntries, this);
27099 moreView.load({
27100     url: "/blog/get-posts.php",
27101     params: "allposts=true",
27102     text: "Loading Blog Entries..."
27103 });
27104 </code></pre>
27105
27106 * Note: old code is supported with arguments : (container, template, config)
27107
27108
27109  * @constructor
27110  * Create a new JsonView
27111  * 
27112  * @param {Object} config The config object
27113  * 
27114  */
27115 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27116     
27117     
27118     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27119
27120     var um = this.el.getUpdateManager();
27121     um.setRenderer(this);
27122     um.on("update", this.onLoad, this);
27123     um.on("failure", this.onLoadException, this);
27124
27125     /**
27126      * @event beforerender
27127      * Fires before rendering of the downloaded JSON data.
27128      * @param {Roo.JsonView} this
27129      * @param {Object} data The JSON data loaded
27130      */
27131     /**
27132      * @event load
27133      * Fires when data is loaded.
27134      * @param {Roo.JsonView} this
27135      * @param {Object} data The JSON data loaded
27136      * @param {Object} response The raw Connect response object
27137      */
27138     /**
27139      * @event loadexception
27140      * Fires when loading fails.
27141      * @param {Roo.JsonView} this
27142      * @param {Object} response The raw Connect response object
27143      */
27144     this.addEvents({
27145         'beforerender' : true,
27146         'load' : true,
27147         'loadexception' : true
27148     });
27149 };
27150 Roo.extend(Roo.JsonView, Roo.View, {
27151     /**
27152      * @type {String} The root property in the loaded JSON object that contains the data
27153      */
27154     jsonRoot : "",
27155
27156     /**
27157      * Refreshes the view.
27158      */
27159     refresh : function(){
27160         this.clearSelections();
27161         this.el.update("");
27162         var html = [];
27163         var o = this.jsonData;
27164         if(o && o.length > 0){
27165             for(var i = 0, len = o.length; i < len; i++){
27166                 var data = this.prepareData(o[i], i, o);
27167                 html[html.length] = this.tpl.apply(data);
27168             }
27169         }else{
27170             html.push(this.emptyText);
27171         }
27172         this.el.update(html.join(""));
27173         this.nodes = this.el.dom.childNodes;
27174         this.updateIndexes(0);
27175     },
27176
27177     /**
27178      * 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.
27179      * @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:
27180      <pre><code>
27181      view.load({
27182          url: "your-url.php",
27183          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27184          callback: yourFunction,
27185          scope: yourObject, //(optional scope)
27186          discardUrl: false,
27187          nocache: false,
27188          text: "Loading...",
27189          timeout: 30,
27190          scripts: false
27191      });
27192      </code></pre>
27193      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27194      * 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.
27195      * @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}
27196      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27197      * @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.
27198      */
27199     load : function(){
27200         var um = this.el.getUpdateManager();
27201         um.update.apply(um, arguments);
27202     },
27203
27204     // note - render is a standard framework call...
27205     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27206     render : function(el, response){
27207         
27208         this.clearSelections();
27209         this.el.update("");
27210         var o;
27211         try{
27212             if (response != '') {
27213                 o = Roo.util.JSON.decode(response.responseText);
27214                 if(this.jsonRoot){
27215                     
27216                     o = o[this.jsonRoot];
27217                 }
27218             }
27219         } catch(e){
27220         }
27221         /**
27222          * The current JSON data or null
27223          */
27224         this.jsonData = o;
27225         this.beforeRender();
27226         this.refresh();
27227     },
27228
27229 /**
27230  * Get the number of records in the current JSON dataset
27231  * @return {Number}
27232  */
27233     getCount : function(){
27234         return this.jsonData ? this.jsonData.length : 0;
27235     },
27236
27237 /**
27238  * Returns the JSON object for the specified node(s)
27239  * @param {HTMLElement/Array} node The node or an array of nodes
27240  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27241  * you get the JSON object for the node
27242  */
27243     getNodeData : function(node){
27244         if(node instanceof Array){
27245             var data = [];
27246             for(var i = 0, len = node.length; i < len; i++){
27247                 data.push(this.getNodeData(node[i]));
27248             }
27249             return data;
27250         }
27251         return this.jsonData[this.indexOf(node)] || null;
27252     },
27253
27254     beforeRender : function(){
27255         this.snapshot = this.jsonData;
27256         if(this.sortInfo){
27257             this.sort.apply(this, this.sortInfo);
27258         }
27259         this.fireEvent("beforerender", this, this.jsonData);
27260     },
27261
27262     onLoad : function(el, o){
27263         this.fireEvent("load", this, this.jsonData, o);
27264     },
27265
27266     onLoadException : function(el, o){
27267         this.fireEvent("loadexception", this, o);
27268     },
27269
27270 /**
27271  * Filter the data by a specific property.
27272  * @param {String} property A property on your JSON objects
27273  * @param {String/RegExp} value Either string that the property values
27274  * should start with, or a RegExp to test against the property
27275  */
27276     filter : function(property, value){
27277         if(this.jsonData){
27278             var data = [];
27279             var ss = this.snapshot;
27280             if(typeof value == "string"){
27281                 var vlen = value.length;
27282                 if(vlen == 0){
27283                     this.clearFilter();
27284                     return;
27285                 }
27286                 value = value.toLowerCase();
27287                 for(var i = 0, len = ss.length; i < len; i++){
27288                     var o = ss[i];
27289                     if(o[property].substr(0, vlen).toLowerCase() == value){
27290                         data.push(o);
27291                     }
27292                 }
27293             } else if(value.exec){ // regex?
27294                 for(var i = 0, len = ss.length; i < len; i++){
27295                     var o = ss[i];
27296                     if(value.test(o[property])){
27297                         data.push(o);
27298                     }
27299                 }
27300             } else{
27301                 return;
27302             }
27303             this.jsonData = data;
27304             this.refresh();
27305         }
27306     },
27307
27308 /**
27309  * Filter by a function. The passed function will be called with each
27310  * object in the current dataset. If the function returns true the value is kept,
27311  * otherwise it is filtered.
27312  * @param {Function} fn
27313  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27314  */
27315     filterBy : function(fn, scope){
27316         if(this.jsonData){
27317             var data = [];
27318             var ss = this.snapshot;
27319             for(var i = 0, len = ss.length; i < len; i++){
27320                 var o = ss[i];
27321                 if(fn.call(scope || this, o)){
27322                     data.push(o);
27323                 }
27324             }
27325             this.jsonData = data;
27326             this.refresh();
27327         }
27328     },
27329
27330 /**
27331  * Clears the current filter.
27332  */
27333     clearFilter : function(){
27334         if(this.snapshot && this.jsonData != this.snapshot){
27335             this.jsonData = this.snapshot;
27336             this.refresh();
27337         }
27338     },
27339
27340
27341 /**
27342  * Sorts the data for this view and refreshes it.
27343  * @param {String} property A property on your JSON objects to sort on
27344  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27345  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27346  */
27347     sort : function(property, dir, sortType){
27348         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27349         if(this.jsonData){
27350             var p = property;
27351             var dsc = dir && dir.toLowerCase() == "desc";
27352             var f = function(o1, o2){
27353                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27354                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27355                 ;
27356                 if(v1 < v2){
27357                     return dsc ? +1 : -1;
27358                 } else if(v1 > v2){
27359                     return dsc ? -1 : +1;
27360                 } else{
27361                     return 0;
27362                 }
27363             };
27364             this.jsonData.sort(f);
27365             this.refresh();
27366             if(this.jsonData != this.snapshot){
27367                 this.snapshot.sort(f);
27368             }
27369         }
27370     }
27371 });/*
27372  * Based on:
27373  * Ext JS Library 1.1.1
27374  * Copyright(c) 2006-2007, Ext JS, LLC.
27375  *
27376  * Originally Released Under LGPL - original licence link has changed is not relivant.
27377  *
27378  * Fork - LGPL
27379  * <script type="text/javascript">
27380  */
27381  
27382
27383 /**
27384  * @class Roo.ColorPalette
27385  * @extends Roo.Component
27386  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27387  * Here's an example of typical usage:
27388  * <pre><code>
27389 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27390 cp.render('my-div');
27391
27392 cp.on('select', function(palette, selColor){
27393     // do something with selColor
27394 });
27395 </code></pre>
27396  * @constructor
27397  * Create a new ColorPalette
27398  * @param {Object} config The config object
27399  */
27400 Roo.ColorPalette = function(config){
27401     Roo.ColorPalette.superclass.constructor.call(this, config);
27402     this.addEvents({
27403         /**
27404              * @event select
27405              * Fires when a color is selected
27406              * @param {ColorPalette} this
27407              * @param {String} color The 6-digit color hex code (without the # symbol)
27408              */
27409         select: true
27410     });
27411
27412     if(this.handler){
27413         this.on("select", this.handler, this.scope, true);
27414     }
27415 };
27416 Roo.extend(Roo.ColorPalette, Roo.Component, {
27417     /**
27418      * @cfg {String} itemCls
27419      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27420      */
27421     itemCls : "x-color-palette",
27422     /**
27423      * @cfg {String} value
27424      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27425      * the hex codes are case-sensitive.
27426      */
27427     value : null,
27428     clickEvent:'click',
27429     // private
27430     ctype: "Roo.ColorPalette",
27431
27432     /**
27433      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27434      */
27435     allowReselect : false,
27436
27437     /**
27438      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27439      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27440      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27441      * of colors with the width setting until the box is symmetrical.</p>
27442      * <p>You can override individual colors if needed:</p>
27443      * <pre><code>
27444 var cp = new Roo.ColorPalette();
27445 cp.colors[0] = "FF0000";  // change the first box to red
27446 </code></pre>
27447
27448 Or you can provide a custom array of your own for complete control:
27449 <pre><code>
27450 var cp = new Roo.ColorPalette();
27451 cp.colors = ["000000", "993300", "333300"];
27452 </code></pre>
27453      * @type Array
27454      */
27455     colors : [
27456         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27457         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27458         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27459         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27460         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27461     ],
27462
27463     // private
27464     onRender : function(container, position){
27465         var t = new Roo.MasterTemplate(
27466             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27467         );
27468         var c = this.colors;
27469         for(var i = 0, len = c.length; i < len; i++){
27470             t.add([c[i]]);
27471         }
27472         var el = document.createElement("div");
27473         el.className = this.itemCls;
27474         t.overwrite(el);
27475         container.dom.insertBefore(el, position);
27476         this.el = Roo.get(el);
27477         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27478         if(this.clickEvent != 'click'){
27479             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27480         }
27481     },
27482
27483     // private
27484     afterRender : function(){
27485         Roo.ColorPalette.superclass.afterRender.call(this);
27486         if(this.value){
27487             var s = this.value;
27488             this.value = null;
27489             this.select(s);
27490         }
27491     },
27492
27493     // private
27494     handleClick : function(e, t){
27495         e.preventDefault();
27496         if(!this.disabled){
27497             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27498             this.select(c.toUpperCase());
27499         }
27500     },
27501
27502     /**
27503      * Selects the specified color in the palette (fires the select event)
27504      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27505      */
27506     select : function(color){
27507         color = color.replace("#", "");
27508         if(color != this.value || this.allowReselect){
27509             var el = this.el;
27510             if(this.value){
27511                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27512             }
27513             el.child("a.color-"+color).addClass("x-color-palette-sel");
27514             this.value = color;
27515             this.fireEvent("select", this, color);
27516         }
27517     }
27518 });/*
27519  * Based on:
27520  * Ext JS Library 1.1.1
27521  * Copyright(c) 2006-2007, Ext JS, LLC.
27522  *
27523  * Originally Released Under LGPL - original licence link has changed is not relivant.
27524  *
27525  * Fork - LGPL
27526  * <script type="text/javascript">
27527  */
27528  
27529 /**
27530  * @class Roo.DatePicker
27531  * @extends Roo.Component
27532  * Simple date picker class.
27533  * @constructor
27534  * Create a new DatePicker
27535  * @param {Object} config The config object
27536  */
27537 Roo.DatePicker = function(config){
27538     Roo.DatePicker.superclass.constructor.call(this, config);
27539
27540     this.value = config && config.value ?
27541                  config.value.clearTime() : new Date().clearTime();
27542
27543     this.addEvents({
27544         /**
27545              * @event select
27546              * Fires when a date is selected
27547              * @param {DatePicker} this
27548              * @param {Date} date The selected date
27549              */
27550         'select': true,
27551         /**
27552              * @event monthchange
27553              * Fires when the displayed month changes 
27554              * @param {DatePicker} this
27555              * @param {Date} date The selected month
27556              */
27557         'monthchange': true
27558     });
27559
27560     if(this.handler){
27561         this.on("select", this.handler,  this.scope || this);
27562     }
27563     // build the disabledDatesRE
27564     if(!this.disabledDatesRE && this.disabledDates){
27565         var dd = this.disabledDates;
27566         var re = "(?:";
27567         for(var i = 0; i < dd.length; i++){
27568             re += dd[i];
27569             if(i != dd.length-1) {
27570                 re += "|";
27571             }
27572         }
27573         this.disabledDatesRE = new RegExp(re + ")");
27574     }
27575 };
27576
27577 Roo.extend(Roo.DatePicker, Roo.Component, {
27578     /**
27579      * @cfg {String} todayText
27580      * The text to display on the button that selects the current date (defaults to "Today")
27581      */
27582     todayText : "Today",
27583     /**
27584      * @cfg {String} okText
27585      * The text to display on the ok button
27586      */
27587     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27588     /**
27589      * @cfg {String} cancelText
27590      * The text to display on the cancel button
27591      */
27592     cancelText : "Cancel",
27593     /**
27594      * @cfg {String} todayTip
27595      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27596      */
27597     todayTip : "{0} (Spacebar)",
27598     /**
27599      * @cfg {Date} minDate
27600      * Minimum allowable date (JavaScript date object, defaults to null)
27601      */
27602     minDate : null,
27603     /**
27604      * @cfg {Date} maxDate
27605      * Maximum allowable date (JavaScript date object, defaults to null)
27606      */
27607     maxDate : null,
27608     /**
27609      * @cfg {String} minText
27610      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27611      */
27612     minText : "This date is before the minimum date",
27613     /**
27614      * @cfg {String} maxText
27615      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27616      */
27617     maxText : "This date is after the maximum date",
27618     /**
27619      * @cfg {String} format
27620      * The default date format string which can be overriden for localization support.  The format must be
27621      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27622      */
27623     format : "m/d/y",
27624     /**
27625      * @cfg {Array} disabledDays
27626      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27627      */
27628     disabledDays : null,
27629     /**
27630      * @cfg {String} disabledDaysText
27631      * The tooltip to display when the date falls on a disabled day (defaults to "")
27632      */
27633     disabledDaysText : "",
27634     /**
27635      * @cfg {RegExp} disabledDatesRE
27636      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27637      */
27638     disabledDatesRE : null,
27639     /**
27640      * @cfg {String} disabledDatesText
27641      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27642      */
27643     disabledDatesText : "",
27644     /**
27645      * @cfg {Boolean} constrainToViewport
27646      * True to constrain the date picker to the viewport (defaults to true)
27647      */
27648     constrainToViewport : true,
27649     /**
27650      * @cfg {Array} monthNames
27651      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27652      */
27653     monthNames : Date.monthNames,
27654     /**
27655      * @cfg {Array} dayNames
27656      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27657      */
27658     dayNames : Date.dayNames,
27659     /**
27660      * @cfg {String} nextText
27661      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27662      */
27663     nextText: 'Next Month (Control+Right)',
27664     /**
27665      * @cfg {String} prevText
27666      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27667      */
27668     prevText: 'Previous Month (Control+Left)',
27669     /**
27670      * @cfg {String} monthYearText
27671      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27672      */
27673     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27674     /**
27675      * @cfg {Number} startDay
27676      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27677      */
27678     startDay : 0,
27679     /**
27680      * @cfg {Bool} showClear
27681      * Show a clear button (usefull for date form elements that can be blank.)
27682      */
27683     
27684     showClear: false,
27685     
27686     /**
27687      * Sets the value of the date field
27688      * @param {Date} value The date to set
27689      */
27690     setValue : function(value){
27691         var old = this.value;
27692         
27693         if (typeof(value) == 'string') {
27694          
27695             value = Date.parseDate(value, this.format);
27696         }
27697         if (!value) {
27698             value = new Date();
27699         }
27700         
27701         this.value = value.clearTime(true);
27702         if(this.el){
27703             this.update(this.value);
27704         }
27705     },
27706
27707     /**
27708      * Gets the current selected value of the date field
27709      * @return {Date} The selected date
27710      */
27711     getValue : function(){
27712         return this.value;
27713     },
27714
27715     // private
27716     focus : function(){
27717         if(this.el){
27718             this.update(this.activeDate);
27719         }
27720     },
27721
27722     // privateval
27723     onRender : function(container, position){
27724         
27725         var m = [
27726              '<table cellspacing="0">',
27727                 '<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>',
27728                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27729         var dn = this.dayNames;
27730         for(var i = 0; i < 7; i++){
27731             var d = this.startDay+i;
27732             if(d > 6){
27733                 d = d-7;
27734             }
27735             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27736         }
27737         m[m.length] = "</tr></thead><tbody><tr>";
27738         for(var i = 0; i < 42; i++) {
27739             if(i % 7 == 0 && i != 0){
27740                 m[m.length] = "</tr><tr>";
27741             }
27742             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27743         }
27744         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27745             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27746
27747         var el = document.createElement("div");
27748         el.className = "x-date-picker";
27749         el.innerHTML = m.join("");
27750
27751         container.dom.insertBefore(el, position);
27752
27753         this.el = Roo.get(el);
27754         this.eventEl = Roo.get(el.firstChild);
27755
27756         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27757             handler: this.showPrevMonth,
27758             scope: this,
27759             preventDefault:true,
27760             stopDefault:true
27761         });
27762
27763         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27764             handler: this.showNextMonth,
27765             scope: this,
27766             preventDefault:true,
27767             stopDefault:true
27768         });
27769
27770         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27771
27772         this.monthPicker = this.el.down('div.x-date-mp');
27773         this.monthPicker.enableDisplayMode('block');
27774         
27775         var kn = new Roo.KeyNav(this.eventEl, {
27776             "left" : function(e){
27777                 e.ctrlKey ?
27778                     this.showPrevMonth() :
27779                     this.update(this.activeDate.add("d", -1));
27780             },
27781
27782             "right" : function(e){
27783                 e.ctrlKey ?
27784                     this.showNextMonth() :
27785                     this.update(this.activeDate.add("d", 1));
27786             },
27787
27788             "up" : function(e){
27789                 e.ctrlKey ?
27790                     this.showNextYear() :
27791                     this.update(this.activeDate.add("d", -7));
27792             },
27793
27794             "down" : function(e){
27795                 e.ctrlKey ?
27796                     this.showPrevYear() :
27797                     this.update(this.activeDate.add("d", 7));
27798             },
27799
27800             "pageUp" : function(e){
27801                 this.showNextMonth();
27802             },
27803
27804             "pageDown" : function(e){
27805                 this.showPrevMonth();
27806             },
27807
27808             "enter" : function(e){
27809                 e.stopPropagation();
27810                 return true;
27811             },
27812
27813             scope : this
27814         });
27815
27816         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27817
27818         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27819
27820         this.el.unselectable();
27821         
27822         this.cells = this.el.select("table.x-date-inner tbody td");
27823         this.textNodes = this.el.query("table.x-date-inner tbody span");
27824
27825         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27826             text: "&#160;",
27827             tooltip: this.monthYearText
27828         });
27829
27830         this.mbtn.on('click', this.showMonthPicker, this);
27831         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27832
27833
27834         var today = (new Date()).dateFormat(this.format);
27835         
27836         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27837         if (this.showClear) {
27838             baseTb.add( new Roo.Toolbar.Fill());
27839         }
27840         baseTb.add({
27841             text: String.format(this.todayText, today),
27842             tooltip: String.format(this.todayTip, today),
27843             handler: this.selectToday,
27844             scope: this
27845         });
27846         
27847         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27848             
27849         //});
27850         if (this.showClear) {
27851             
27852             baseTb.add( new Roo.Toolbar.Fill());
27853             baseTb.add({
27854                 text: '&#160;',
27855                 cls: 'x-btn-icon x-btn-clear',
27856                 handler: function() {
27857                     //this.value = '';
27858                     this.fireEvent("select", this, '');
27859                 },
27860                 scope: this
27861             });
27862         }
27863         
27864         
27865         if(Roo.isIE){
27866             this.el.repaint();
27867         }
27868         this.update(this.value);
27869     },
27870
27871     createMonthPicker : function(){
27872         if(!this.monthPicker.dom.firstChild){
27873             var buf = ['<table border="0" cellspacing="0">'];
27874             for(var i = 0; i < 6; i++){
27875                 buf.push(
27876                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27877                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27878                     i == 0 ?
27879                     '<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>' :
27880                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27881                 );
27882             }
27883             buf.push(
27884                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27885                     this.okText,
27886                     '</button><button type="button" class="x-date-mp-cancel">',
27887                     this.cancelText,
27888                     '</button></td></tr>',
27889                 '</table>'
27890             );
27891             this.monthPicker.update(buf.join(''));
27892             this.monthPicker.on('click', this.onMonthClick, this);
27893             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27894
27895             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27896             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27897
27898             this.mpMonths.each(function(m, a, i){
27899                 i += 1;
27900                 if((i%2) == 0){
27901                     m.dom.xmonth = 5 + Math.round(i * .5);
27902                 }else{
27903                     m.dom.xmonth = Math.round((i-1) * .5);
27904                 }
27905             });
27906         }
27907     },
27908
27909     showMonthPicker : function(){
27910         this.createMonthPicker();
27911         var size = this.el.getSize();
27912         this.monthPicker.setSize(size);
27913         this.monthPicker.child('table').setSize(size);
27914
27915         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27916         this.updateMPMonth(this.mpSelMonth);
27917         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27918         this.updateMPYear(this.mpSelYear);
27919
27920         this.monthPicker.slideIn('t', {duration:.2});
27921     },
27922
27923     updateMPYear : function(y){
27924         this.mpyear = y;
27925         var ys = this.mpYears.elements;
27926         for(var i = 1; i <= 10; i++){
27927             var td = ys[i-1], y2;
27928             if((i%2) == 0){
27929                 y2 = y + Math.round(i * .5);
27930                 td.firstChild.innerHTML = y2;
27931                 td.xyear = y2;
27932             }else{
27933                 y2 = y - (5-Math.round(i * .5));
27934                 td.firstChild.innerHTML = y2;
27935                 td.xyear = y2;
27936             }
27937             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27938         }
27939     },
27940
27941     updateMPMonth : function(sm){
27942         this.mpMonths.each(function(m, a, i){
27943             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27944         });
27945     },
27946
27947     selectMPMonth: function(m){
27948         
27949     },
27950
27951     onMonthClick : function(e, t){
27952         e.stopEvent();
27953         var el = new Roo.Element(t), pn;
27954         if(el.is('button.x-date-mp-cancel')){
27955             this.hideMonthPicker();
27956         }
27957         else if(el.is('button.x-date-mp-ok')){
27958             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27959             this.hideMonthPicker();
27960         }
27961         else if(pn = el.up('td.x-date-mp-month', 2)){
27962             this.mpMonths.removeClass('x-date-mp-sel');
27963             pn.addClass('x-date-mp-sel');
27964             this.mpSelMonth = pn.dom.xmonth;
27965         }
27966         else if(pn = el.up('td.x-date-mp-year', 2)){
27967             this.mpYears.removeClass('x-date-mp-sel');
27968             pn.addClass('x-date-mp-sel');
27969             this.mpSelYear = pn.dom.xyear;
27970         }
27971         else if(el.is('a.x-date-mp-prev')){
27972             this.updateMPYear(this.mpyear-10);
27973         }
27974         else if(el.is('a.x-date-mp-next')){
27975             this.updateMPYear(this.mpyear+10);
27976         }
27977     },
27978
27979     onMonthDblClick : function(e, t){
27980         e.stopEvent();
27981         var el = new Roo.Element(t), pn;
27982         if(pn = el.up('td.x-date-mp-month', 2)){
27983             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27984             this.hideMonthPicker();
27985         }
27986         else if(pn = el.up('td.x-date-mp-year', 2)){
27987             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27988             this.hideMonthPicker();
27989         }
27990     },
27991
27992     hideMonthPicker : function(disableAnim){
27993         if(this.monthPicker){
27994             if(disableAnim === true){
27995                 this.monthPicker.hide();
27996             }else{
27997                 this.monthPicker.slideOut('t', {duration:.2});
27998             }
27999         }
28000     },
28001
28002     // private
28003     showPrevMonth : function(e){
28004         this.update(this.activeDate.add("mo", -1));
28005     },
28006
28007     // private
28008     showNextMonth : function(e){
28009         this.update(this.activeDate.add("mo", 1));
28010     },
28011
28012     // private
28013     showPrevYear : function(){
28014         this.update(this.activeDate.add("y", -1));
28015     },
28016
28017     // private
28018     showNextYear : function(){
28019         this.update(this.activeDate.add("y", 1));
28020     },
28021
28022     // private
28023     handleMouseWheel : function(e){
28024         var delta = e.getWheelDelta();
28025         if(delta > 0){
28026             this.showPrevMonth();
28027             e.stopEvent();
28028         } else if(delta < 0){
28029             this.showNextMonth();
28030             e.stopEvent();
28031         }
28032     },
28033
28034     // private
28035     handleDateClick : function(e, t){
28036         e.stopEvent();
28037         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28038             this.setValue(new Date(t.dateValue));
28039             this.fireEvent("select", this, this.value);
28040         }
28041     },
28042
28043     // private
28044     selectToday : function(){
28045         this.setValue(new Date().clearTime());
28046         this.fireEvent("select", this, this.value);
28047     },
28048
28049     // private
28050     update : function(date)
28051     {
28052         var vd = this.activeDate;
28053         this.activeDate = date;
28054         if(vd && this.el){
28055             var t = date.getTime();
28056             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28057                 this.cells.removeClass("x-date-selected");
28058                 this.cells.each(function(c){
28059                    if(c.dom.firstChild.dateValue == t){
28060                        c.addClass("x-date-selected");
28061                        setTimeout(function(){
28062                             try{c.dom.firstChild.focus();}catch(e){}
28063                        }, 50);
28064                        return false;
28065                    }
28066                 });
28067                 return;
28068             }
28069         }
28070         
28071         var days = date.getDaysInMonth();
28072         var firstOfMonth = date.getFirstDateOfMonth();
28073         var startingPos = firstOfMonth.getDay()-this.startDay;
28074
28075         if(startingPos <= this.startDay){
28076             startingPos += 7;
28077         }
28078
28079         var pm = date.add("mo", -1);
28080         var prevStart = pm.getDaysInMonth()-startingPos;
28081
28082         var cells = this.cells.elements;
28083         var textEls = this.textNodes;
28084         days += startingPos;
28085
28086         // convert everything to numbers so it's fast
28087         var day = 86400000;
28088         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28089         var today = new Date().clearTime().getTime();
28090         var sel = date.clearTime().getTime();
28091         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28092         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28093         var ddMatch = this.disabledDatesRE;
28094         var ddText = this.disabledDatesText;
28095         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28096         var ddaysText = this.disabledDaysText;
28097         var format = this.format;
28098
28099         var setCellClass = function(cal, cell){
28100             cell.title = "";
28101             var t = d.getTime();
28102             cell.firstChild.dateValue = t;
28103             if(t == today){
28104                 cell.className += " x-date-today";
28105                 cell.title = cal.todayText;
28106             }
28107             if(t == sel){
28108                 cell.className += " x-date-selected";
28109                 setTimeout(function(){
28110                     try{cell.firstChild.focus();}catch(e){}
28111                 }, 50);
28112             }
28113             // disabling
28114             if(t < min) {
28115                 cell.className = " x-date-disabled";
28116                 cell.title = cal.minText;
28117                 return;
28118             }
28119             if(t > max) {
28120                 cell.className = " x-date-disabled";
28121                 cell.title = cal.maxText;
28122                 return;
28123             }
28124             if(ddays){
28125                 if(ddays.indexOf(d.getDay()) != -1){
28126                     cell.title = ddaysText;
28127                     cell.className = " x-date-disabled";
28128                 }
28129             }
28130             if(ddMatch && format){
28131                 var fvalue = d.dateFormat(format);
28132                 if(ddMatch.test(fvalue)){
28133                     cell.title = ddText.replace("%0", fvalue);
28134                     cell.className = " x-date-disabled";
28135                 }
28136             }
28137         };
28138
28139         var i = 0;
28140         for(; i < startingPos; i++) {
28141             textEls[i].innerHTML = (++prevStart);
28142             d.setDate(d.getDate()+1);
28143             cells[i].className = "x-date-prevday";
28144             setCellClass(this, cells[i]);
28145         }
28146         for(; i < days; i++){
28147             intDay = i - startingPos + 1;
28148             textEls[i].innerHTML = (intDay);
28149             d.setDate(d.getDate()+1);
28150             cells[i].className = "x-date-active";
28151             setCellClass(this, cells[i]);
28152         }
28153         var extraDays = 0;
28154         for(; i < 42; i++) {
28155              textEls[i].innerHTML = (++extraDays);
28156              d.setDate(d.getDate()+1);
28157              cells[i].className = "x-date-nextday";
28158              setCellClass(this, cells[i]);
28159         }
28160
28161         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28162         this.fireEvent('monthchange', this, date);
28163         
28164         if(!this.internalRender){
28165             var main = this.el.dom.firstChild;
28166             var w = main.offsetWidth;
28167             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28168             Roo.fly(main).setWidth(w);
28169             this.internalRender = true;
28170             // opera does not respect the auto grow header center column
28171             // then, after it gets a width opera refuses to recalculate
28172             // without a second pass
28173             if(Roo.isOpera && !this.secondPass){
28174                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28175                 this.secondPass = true;
28176                 this.update.defer(10, this, [date]);
28177             }
28178         }
28179         
28180         
28181     }
28182 });        /*
28183  * Based on:
28184  * Ext JS Library 1.1.1
28185  * Copyright(c) 2006-2007, Ext JS, LLC.
28186  *
28187  * Originally Released Under LGPL - original licence link has changed is not relivant.
28188  *
28189  * Fork - LGPL
28190  * <script type="text/javascript">
28191  */
28192 /**
28193  * @class Roo.TabPanel
28194  * @extends Roo.util.Observable
28195  * A lightweight tab container.
28196  * <br><br>
28197  * Usage:
28198  * <pre><code>
28199 // basic tabs 1, built from existing content
28200 var tabs = new Roo.TabPanel("tabs1");
28201 tabs.addTab("script", "View Script");
28202 tabs.addTab("markup", "View Markup");
28203 tabs.activate("script");
28204
28205 // more advanced tabs, built from javascript
28206 var jtabs = new Roo.TabPanel("jtabs");
28207 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28208
28209 // set up the UpdateManager
28210 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28211 var updater = tab2.getUpdateManager();
28212 updater.setDefaultUrl("ajax1.htm");
28213 tab2.on('activate', updater.refresh, updater, true);
28214
28215 // Use setUrl for Ajax loading
28216 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28217 tab3.setUrl("ajax2.htm", null, true);
28218
28219 // Disabled tab
28220 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28221 tab4.disable();
28222
28223 jtabs.activate("jtabs-1");
28224  * </code></pre>
28225  * @constructor
28226  * Create a new TabPanel.
28227  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28228  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28229  */
28230 Roo.TabPanel = function(container, config){
28231     /**
28232     * The container element for this TabPanel.
28233     * @type Roo.Element
28234     */
28235     this.el = Roo.get(container, true);
28236     if(config){
28237         if(typeof config == "boolean"){
28238             this.tabPosition = config ? "bottom" : "top";
28239         }else{
28240             Roo.apply(this, config);
28241         }
28242     }
28243     if(this.tabPosition == "bottom"){
28244         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28245         this.el.addClass("x-tabs-bottom");
28246     }
28247     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28248     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28249     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28250     if(Roo.isIE){
28251         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28252     }
28253     if(this.tabPosition != "bottom"){
28254         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28255          * @type Roo.Element
28256          */
28257         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28258         this.el.addClass("x-tabs-top");
28259     }
28260     this.items = [];
28261
28262     this.bodyEl.setStyle("position", "relative");
28263
28264     this.active = null;
28265     this.activateDelegate = this.activate.createDelegate(this);
28266
28267     this.addEvents({
28268         /**
28269          * @event tabchange
28270          * Fires when the active tab changes
28271          * @param {Roo.TabPanel} this
28272          * @param {Roo.TabPanelItem} activePanel The new active tab
28273          */
28274         "tabchange": true,
28275         /**
28276          * @event beforetabchange
28277          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28278          * @param {Roo.TabPanel} this
28279          * @param {Object} e Set cancel to true on this object to cancel the tab change
28280          * @param {Roo.TabPanelItem} tab The tab being changed to
28281          */
28282         "beforetabchange" : true
28283     });
28284
28285     Roo.EventManager.onWindowResize(this.onResize, this);
28286     this.cpad = this.el.getPadding("lr");
28287     this.hiddenCount = 0;
28288
28289
28290     // toolbar on the tabbar support...
28291     if (this.toolbar) {
28292         var tcfg = this.toolbar;
28293         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28294         this.toolbar = new Roo.Toolbar(tcfg);
28295         if (Roo.isSafari) {
28296             var tbl = tcfg.container.child('table', true);
28297             tbl.setAttribute('width', '100%');
28298         }
28299         
28300     }
28301    
28302
28303
28304     Roo.TabPanel.superclass.constructor.call(this);
28305 };
28306
28307 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28308     /*
28309      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28310      */
28311     tabPosition : "top",
28312     /*
28313      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28314      */
28315     currentTabWidth : 0,
28316     /*
28317      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28318      */
28319     minTabWidth : 40,
28320     /*
28321      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28322      */
28323     maxTabWidth : 250,
28324     /*
28325      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28326      */
28327     preferredTabWidth : 175,
28328     /*
28329      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28330      */
28331     resizeTabs : false,
28332     /*
28333      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28334      */
28335     monitorResize : true,
28336     /*
28337      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28338      */
28339     toolbar : false,
28340
28341     /**
28342      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28343      * @param {String} id The id of the div to use <b>or create</b>
28344      * @param {String} text The text for the tab
28345      * @param {String} content (optional) Content to put in the TabPanelItem body
28346      * @param {Boolean} closable (optional) True to create a close icon on the tab
28347      * @return {Roo.TabPanelItem} The created TabPanelItem
28348      */
28349     addTab : function(id, text, content, closable){
28350         var item = new Roo.TabPanelItem(this, id, text, closable);
28351         this.addTabItem(item);
28352         if(content){
28353             item.setContent(content);
28354         }
28355         return item;
28356     },
28357
28358     /**
28359      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28360      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28361      * @return {Roo.TabPanelItem}
28362      */
28363     getTab : function(id){
28364         return this.items[id];
28365     },
28366
28367     /**
28368      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28369      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28370      */
28371     hideTab : function(id){
28372         var t = this.items[id];
28373         if(!t.isHidden()){
28374            t.setHidden(true);
28375            this.hiddenCount++;
28376            this.autoSizeTabs();
28377         }
28378     },
28379
28380     /**
28381      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28382      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28383      */
28384     unhideTab : function(id){
28385         var t = this.items[id];
28386         if(t.isHidden()){
28387            t.setHidden(false);
28388            this.hiddenCount--;
28389            this.autoSizeTabs();
28390         }
28391     },
28392
28393     /**
28394      * Adds an existing {@link Roo.TabPanelItem}.
28395      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28396      */
28397     addTabItem : function(item){
28398         this.items[item.id] = item;
28399         this.items.push(item);
28400         if(this.resizeTabs){
28401            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28402            this.autoSizeTabs();
28403         }else{
28404             item.autoSize();
28405         }
28406     },
28407
28408     /**
28409      * Removes a {@link Roo.TabPanelItem}.
28410      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28411      */
28412     removeTab : function(id){
28413         var items = this.items;
28414         var tab = items[id];
28415         if(!tab) { return; }
28416         var index = items.indexOf(tab);
28417         if(this.active == tab && items.length > 1){
28418             var newTab = this.getNextAvailable(index);
28419             if(newTab) {
28420                 newTab.activate();
28421             }
28422         }
28423         this.stripEl.dom.removeChild(tab.pnode.dom);
28424         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28425             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28426         }
28427         items.splice(index, 1);
28428         delete this.items[tab.id];
28429         tab.fireEvent("close", tab);
28430         tab.purgeListeners();
28431         this.autoSizeTabs();
28432     },
28433
28434     getNextAvailable : function(start){
28435         var items = this.items;
28436         var index = start;
28437         // look for a next tab that will slide over to
28438         // replace the one being removed
28439         while(index < items.length){
28440             var item = items[++index];
28441             if(item && !item.isHidden()){
28442                 return item;
28443             }
28444         }
28445         // if one isn't found select the previous tab (on the left)
28446         index = start;
28447         while(index >= 0){
28448             var item = items[--index];
28449             if(item && !item.isHidden()){
28450                 return item;
28451             }
28452         }
28453         return null;
28454     },
28455
28456     /**
28457      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28458      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28459      */
28460     disableTab : function(id){
28461         var tab = this.items[id];
28462         if(tab && this.active != tab){
28463             tab.disable();
28464         }
28465     },
28466
28467     /**
28468      * Enables a {@link Roo.TabPanelItem} that is disabled.
28469      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28470      */
28471     enableTab : function(id){
28472         var tab = this.items[id];
28473         tab.enable();
28474     },
28475
28476     /**
28477      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28478      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28479      * @return {Roo.TabPanelItem} The TabPanelItem.
28480      */
28481     activate : function(id){
28482         var tab = this.items[id];
28483         if(!tab){
28484             return null;
28485         }
28486         if(tab == this.active || tab.disabled){
28487             return tab;
28488         }
28489         var e = {};
28490         this.fireEvent("beforetabchange", this, e, tab);
28491         if(e.cancel !== true && !tab.disabled){
28492             if(this.active){
28493                 this.active.hide();
28494             }
28495             this.active = this.items[id];
28496             this.active.show();
28497             this.fireEvent("tabchange", this, this.active);
28498         }
28499         return tab;
28500     },
28501
28502     /**
28503      * Gets the active {@link Roo.TabPanelItem}.
28504      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28505      */
28506     getActiveTab : function(){
28507         return this.active;
28508     },
28509
28510     /**
28511      * Updates the tab body element to fit the height of the container element
28512      * for overflow scrolling
28513      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28514      */
28515     syncHeight : function(targetHeight){
28516         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28517         var bm = this.bodyEl.getMargins();
28518         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28519         this.bodyEl.setHeight(newHeight);
28520         return newHeight;
28521     },
28522
28523     onResize : function(){
28524         if(this.monitorResize){
28525             this.autoSizeTabs();
28526         }
28527     },
28528
28529     /**
28530      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28531      */
28532     beginUpdate : function(){
28533         this.updating = true;
28534     },
28535
28536     /**
28537      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28538      */
28539     endUpdate : function(){
28540         this.updating = false;
28541         this.autoSizeTabs();
28542     },
28543
28544     /**
28545      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28546      */
28547     autoSizeTabs : function(){
28548         var count = this.items.length;
28549         var vcount = count - this.hiddenCount;
28550         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28551             return;
28552         }
28553         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28554         var availWidth = Math.floor(w / vcount);
28555         var b = this.stripBody;
28556         if(b.getWidth() > w){
28557             var tabs = this.items;
28558             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28559             if(availWidth < this.minTabWidth){
28560                 /*if(!this.sleft){    // incomplete scrolling code
28561                     this.createScrollButtons();
28562                 }
28563                 this.showScroll();
28564                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28565             }
28566         }else{
28567             if(this.currentTabWidth < this.preferredTabWidth){
28568                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28569             }
28570         }
28571     },
28572
28573     /**
28574      * Returns the number of tabs in this TabPanel.
28575      * @return {Number}
28576      */
28577      getCount : function(){
28578          return this.items.length;
28579      },
28580
28581     /**
28582      * Resizes all the tabs to the passed width
28583      * @param {Number} The new width
28584      */
28585     setTabWidth : function(width){
28586         this.currentTabWidth = width;
28587         for(var i = 0, len = this.items.length; i < len; i++) {
28588                 if(!this.items[i].isHidden()) {
28589                 this.items[i].setWidth(width);
28590             }
28591         }
28592     },
28593
28594     /**
28595      * Destroys this TabPanel
28596      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28597      */
28598     destroy : function(removeEl){
28599         Roo.EventManager.removeResizeListener(this.onResize, this);
28600         for(var i = 0, len = this.items.length; i < len; i++){
28601             this.items[i].purgeListeners();
28602         }
28603         if(removeEl === true){
28604             this.el.update("");
28605             this.el.remove();
28606         }
28607     }
28608 });
28609
28610 /**
28611  * @class Roo.TabPanelItem
28612  * @extends Roo.util.Observable
28613  * Represents an individual item (tab plus body) in a TabPanel.
28614  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28615  * @param {String} id The id of this TabPanelItem
28616  * @param {String} text The text for the tab of this TabPanelItem
28617  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28618  */
28619 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28620     /**
28621      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28622      * @type Roo.TabPanel
28623      */
28624     this.tabPanel = tabPanel;
28625     /**
28626      * The id for this TabPanelItem
28627      * @type String
28628      */
28629     this.id = id;
28630     /** @private */
28631     this.disabled = false;
28632     /** @private */
28633     this.text = text;
28634     /** @private */
28635     this.loaded = false;
28636     this.closable = closable;
28637
28638     /**
28639      * The body element for this TabPanelItem.
28640      * @type Roo.Element
28641      */
28642     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28643     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28644     this.bodyEl.setStyle("display", "block");
28645     this.bodyEl.setStyle("zoom", "1");
28646     this.hideAction();
28647
28648     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28649     /** @private */
28650     this.el = Roo.get(els.el, true);
28651     this.inner = Roo.get(els.inner, true);
28652     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28653     this.pnode = Roo.get(els.el.parentNode, true);
28654     this.el.on("mousedown", this.onTabMouseDown, this);
28655     this.el.on("click", this.onTabClick, this);
28656     /** @private */
28657     if(closable){
28658         var c = Roo.get(els.close, true);
28659         c.dom.title = this.closeText;
28660         c.addClassOnOver("close-over");
28661         c.on("click", this.closeClick, this);
28662      }
28663
28664     this.addEvents({
28665          /**
28666          * @event activate
28667          * Fires when this tab becomes the active tab.
28668          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28669          * @param {Roo.TabPanelItem} this
28670          */
28671         "activate": true,
28672         /**
28673          * @event beforeclose
28674          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28675          * @param {Roo.TabPanelItem} this
28676          * @param {Object} e Set cancel to true on this object to cancel the close.
28677          */
28678         "beforeclose": true,
28679         /**
28680          * @event close
28681          * Fires when this tab is closed.
28682          * @param {Roo.TabPanelItem} this
28683          */
28684          "close": true,
28685         /**
28686          * @event deactivate
28687          * Fires when this tab is no longer the active tab.
28688          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28689          * @param {Roo.TabPanelItem} this
28690          */
28691          "deactivate" : true
28692     });
28693     this.hidden = false;
28694
28695     Roo.TabPanelItem.superclass.constructor.call(this);
28696 };
28697
28698 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28699     purgeListeners : function(){
28700        Roo.util.Observable.prototype.purgeListeners.call(this);
28701        this.el.removeAllListeners();
28702     },
28703     /**
28704      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28705      */
28706     show : function(){
28707         this.pnode.addClass("on");
28708         this.showAction();
28709         if(Roo.isOpera){
28710             this.tabPanel.stripWrap.repaint();
28711         }
28712         this.fireEvent("activate", this.tabPanel, this);
28713     },
28714
28715     /**
28716      * Returns true if this tab is the active tab.
28717      * @return {Boolean}
28718      */
28719     isActive : function(){
28720         return this.tabPanel.getActiveTab() == this;
28721     },
28722
28723     /**
28724      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28725      */
28726     hide : function(){
28727         this.pnode.removeClass("on");
28728         this.hideAction();
28729         this.fireEvent("deactivate", this.tabPanel, this);
28730     },
28731
28732     hideAction : function(){
28733         this.bodyEl.hide();
28734         this.bodyEl.setStyle("position", "absolute");
28735         this.bodyEl.setLeft("-20000px");
28736         this.bodyEl.setTop("-20000px");
28737     },
28738
28739     showAction : function(){
28740         this.bodyEl.setStyle("position", "relative");
28741         this.bodyEl.setTop("");
28742         this.bodyEl.setLeft("");
28743         this.bodyEl.show();
28744     },
28745
28746     /**
28747      * Set the tooltip for the tab.
28748      * @param {String} tooltip The tab's tooltip
28749      */
28750     setTooltip : function(text){
28751         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28752             this.textEl.dom.qtip = text;
28753             this.textEl.dom.removeAttribute('title');
28754         }else{
28755             this.textEl.dom.title = text;
28756         }
28757     },
28758
28759     onTabClick : function(e){
28760         e.preventDefault();
28761         this.tabPanel.activate(this.id);
28762     },
28763
28764     onTabMouseDown : function(e){
28765         e.preventDefault();
28766         this.tabPanel.activate(this.id);
28767     },
28768
28769     getWidth : function(){
28770         return this.inner.getWidth();
28771     },
28772
28773     setWidth : function(width){
28774         var iwidth = width - this.pnode.getPadding("lr");
28775         this.inner.setWidth(iwidth);
28776         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28777         this.pnode.setWidth(width);
28778     },
28779
28780     /**
28781      * Show or hide the tab
28782      * @param {Boolean} hidden True to hide or false to show.
28783      */
28784     setHidden : function(hidden){
28785         this.hidden = hidden;
28786         this.pnode.setStyle("display", hidden ? "none" : "");
28787     },
28788
28789     /**
28790      * Returns true if this tab is "hidden"
28791      * @return {Boolean}
28792      */
28793     isHidden : function(){
28794         return this.hidden;
28795     },
28796
28797     /**
28798      * Returns the text for this tab
28799      * @return {String}
28800      */
28801     getText : function(){
28802         return this.text;
28803     },
28804
28805     autoSize : function(){
28806         //this.el.beginMeasure();
28807         this.textEl.setWidth(1);
28808         /*
28809          *  #2804 [new] Tabs in Roojs
28810          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28811          */
28812         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28813         //this.el.endMeasure();
28814     },
28815
28816     /**
28817      * Sets the text for the tab (Note: this also sets the tooltip text)
28818      * @param {String} text The tab's text and tooltip
28819      */
28820     setText : function(text){
28821         this.text = text;
28822         this.textEl.update(text);
28823         this.setTooltip(text);
28824         if(!this.tabPanel.resizeTabs){
28825             this.autoSize();
28826         }
28827     },
28828     /**
28829      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28830      */
28831     activate : function(){
28832         this.tabPanel.activate(this.id);
28833     },
28834
28835     /**
28836      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28837      */
28838     disable : function(){
28839         if(this.tabPanel.active != this){
28840             this.disabled = true;
28841             this.pnode.addClass("disabled");
28842         }
28843     },
28844
28845     /**
28846      * Enables this TabPanelItem if it was previously disabled.
28847      */
28848     enable : function(){
28849         this.disabled = false;
28850         this.pnode.removeClass("disabled");
28851     },
28852
28853     /**
28854      * Sets the content for this TabPanelItem.
28855      * @param {String} content The content
28856      * @param {Boolean} loadScripts true to look for and load scripts
28857      */
28858     setContent : function(content, loadScripts){
28859         this.bodyEl.update(content, loadScripts);
28860     },
28861
28862     /**
28863      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28864      * @return {Roo.UpdateManager} The UpdateManager
28865      */
28866     getUpdateManager : function(){
28867         return this.bodyEl.getUpdateManager();
28868     },
28869
28870     /**
28871      * Set a URL to be used to load the content for this TabPanelItem.
28872      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28873      * @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)
28874      * @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)
28875      * @return {Roo.UpdateManager} The UpdateManager
28876      */
28877     setUrl : function(url, params, loadOnce){
28878         if(this.refreshDelegate){
28879             this.un('activate', this.refreshDelegate);
28880         }
28881         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28882         this.on("activate", this.refreshDelegate);
28883         return this.bodyEl.getUpdateManager();
28884     },
28885
28886     /** @private */
28887     _handleRefresh : function(url, params, loadOnce){
28888         if(!loadOnce || !this.loaded){
28889             var updater = this.bodyEl.getUpdateManager();
28890             updater.update(url, params, this._setLoaded.createDelegate(this));
28891         }
28892     },
28893
28894     /**
28895      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28896      *   Will fail silently if the setUrl method has not been called.
28897      *   This does not activate the panel, just updates its content.
28898      */
28899     refresh : function(){
28900         if(this.refreshDelegate){
28901            this.loaded = false;
28902            this.refreshDelegate();
28903         }
28904     },
28905
28906     /** @private */
28907     _setLoaded : function(){
28908         this.loaded = true;
28909     },
28910
28911     /** @private */
28912     closeClick : function(e){
28913         var o = {};
28914         e.stopEvent();
28915         this.fireEvent("beforeclose", this, o);
28916         if(o.cancel !== true){
28917             this.tabPanel.removeTab(this.id);
28918         }
28919     },
28920     /**
28921      * The text displayed in the tooltip for the close icon.
28922      * @type String
28923      */
28924     closeText : "Close this tab"
28925 });
28926
28927 /** @private */
28928 Roo.TabPanel.prototype.createStrip = function(container){
28929     var strip = document.createElement("div");
28930     strip.className = "x-tabs-wrap";
28931     container.appendChild(strip);
28932     return strip;
28933 };
28934 /** @private */
28935 Roo.TabPanel.prototype.createStripList = function(strip){
28936     // div wrapper for retard IE
28937     // returns the "tr" element.
28938     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28939         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28940         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28941     return strip.firstChild.firstChild.firstChild.firstChild;
28942 };
28943 /** @private */
28944 Roo.TabPanel.prototype.createBody = function(container){
28945     var body = document.createElement("div");
28946     Roo.id(body, "tab-body");
28947     Roo.fly(body).addClass("x-tabs-body");
28948     container.appendChild(body);
28949     return body;
28950 };
28951 /** @private */
28952 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28953     var body = Roo.getDom(id);
28954     if(!body){
28955         body = document.createElement("div");
28956         body.id = id;
28957     }
28958     Roo.fly(body).addClass("x-tabs-item-body");
28959     bodyEl.insertBefore(body, bodyEl.firstChild);
28960     return body;
28961 };
28962 /** @private */
28963 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28964     var td = document.createElement("td");
28965     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28966     //stripEl.appendChild(td);
28967     if(closable){
28968         td.className = "x-tabs-closable";
28969         if(!this.closeTpl){
28970             this.closeTpl = new Roo.Template(
28971                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28972                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28973                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28974             );
28975         }
28976         var el = this.closeTpl.overwrite(td, {"text": text});
28977         var close = el.getElementsByTagName("div")[0];
28978         var inner = el.getElementsByTagName("em")[0];
28979         return {"el": el, "close": close, "inner": inner};
28980     } else {
28981         if(!this.tabTpl){
28982             this.tabTpl = new Roo.Template(
28983                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28984                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28985             );
28986         }
28987         var el = this.tabTpl.overwrite(td, {"text": text});
28988         var inner = el.getElementsByTagName("em")[0];
28989         return {"el": el, "inner": inner};
28990     }
28991 };/*
28992  * Based on:
28993  * Ext JS Library 1.1.1
28994  * Copyright(c) 2006-2007, Ext JS, LLC.
28995  *
28996  * Originally Released Under LGPL - original licence link has changed is not relivant.
28997  *
28998  * Fork - LGPL
28999  * <script type="text/javascript">
29000  */
29001
29002 /**
29003  * @class Roo.Button
29004  * @extends Roo.util.Observable
29005  * Simple Button class
29006  * @cfg {String} text The button text
29007  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29008  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29009  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29010  * @cfg {Object} scope The scope of the handler
29011  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29012  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29013  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29014  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29015  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29016  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29017    applies if enableToggle = true)
29018  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29019  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29020   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29021  * @constructor
29022  * Create a new button
29023  * @param {Object} config The config object
29024  */
29025 Roo.Button = function(renderTo, config)
29026 {
29027     if (!config) {
29028         config = renderTo;
29029         renderTo = config.renderTo || false;
29030     }
29031     
29032     Roo.apply(this, config);
29033     this.addEvents({
29034         /**
29035              * @event click
29036              * Fires when this button is clicked
29037              * @param {Button} this
29038              * @param {EventObject} e The click event
29039              */
29040             "click" : true,
29041         /**
29042              * @event toggle
29043              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29044              * @param {Button} this
29045              * @param {Boolean} pressed
29046              */
29047             "toggle" : true,
29048         /**
29049              * @event mouseover
29050              * Fires when the mouse hovers over the button
29051              * @param {Button} this
29052              * @param {Event} e The event object
29053              */
29054         'mouseover' : true,
29055         /**
29056              * @event mouseout
29057              * Fires when the mouse exits the button
29058              * @param {Button} this
29059              * @param {Event} e The event object
29060              */
29061         'mouseout': true,
29062          /**
29063              * @event render
29064              * Fires when the button is rendered
29065              * @param {Button} this
29066              */
29067         'render': true
29068     });
29069     if(this.menu){
29070         this.menu = Roo.menu.MenuMgr.get(this.menu);
29071     }
29072     // register listeners first!!  - so render can be captured..
29073     Roo.util.Observable.call(this);
29074     if(renderTo){
29075         this.render(renderTo);
29076     }
29077     
29078   
29079 };
29080
29081 Roo.extend(Roo.Button, Roo.util.Observable, {
29082     /**
29083      * 
29084      */
29085     
29086     /**
29087      * Read-only. True if this button is hidden
29088      * @type Boolean
29089      */
29090     hidden : false,
29091     /**
29092      * Read-only. True if this button is disabled
29093      * @type Boolean
29094      */
29095     disabled : false,
29096     /**
29097      * Read-only. True if this button is pressed (only if enableToggle = true)
29098      * @type Boolean
29099      */
29100     pressed : false,
29101
29102     /**
29103      * @cfg {Number} tabIndex 
29104      * The DOM tabIndex for this button (defaults to undefined)
29105      */
29106     tabIndex : undefined,
29107
29108     /**
29109      * @cfg {Boolean} enableToggle
29110      * True to enable pressed/not pressed toggling (defaults to false)
29111      */
29112     enableToggle: false,
29113     /**
29114      * @cfg {Mixed} menu
29115      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29116      */
29117     menu : undefined,
29118     /**
29119      * @cfg {String} menuAlign
29120      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29121      */
29122     menuAlign : "tl-bl?",
29123
29124     /**
29125      * @cfg {String} iconCls
29126      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29127      */
29128     iconCls : undefined,
29129     /**
29130      * @cfg {String} type
29131      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29132      */
29133     type : 'button',
29134
29135     // private
29136     menuClassTarget: 'tr',
29137
29138     /**
29139      * @cfg {String} clickEvent
29140      * The type of event to map to the button's event handler (defaults to 'click')
29141      */
29142     clickEvent : 'click',
29143
29144     /**
29145      * @cfg {Boolean} handleMouseEvents
29146      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29147      */
29148     handleMouseEvents : true,
29149
29150     /**
29151      * @cfg {String} tooltipType
29152      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29153      */
29154     tooltipType : 'qtip',
29155
29156     /**
29157      * @cfg {String} cls
29158      * A CSS class to apply to the button's main element.
29159      */
29160     
29161     /**
29162      * @cfg {Roo.Template} template (Optional)
29163      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29164      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29165      * require code modifications if required elements (e.g. a button) aren't present.
29166      */
29167
29168     // private
29169     render : function(renderTo){
29170         var btn;
29171         if(this.hideParent){
29172             this.parentEl = Roo.get(renderTo);
29173         }
29174         if(!this.dhconfig){
29175             if(!this.template){
29176                 if(!Roo.Button.buttonTemplate){
29177                     // hideous table template
29178                     Roo.Button.buttonTemplate = new Roo.Template(
29179                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29180                         '<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>',
29181                         "</tr></tbody></table>");
29182                 }
29183                 this.template = Roo.Button.buttonTemplate;
29184             }
29185             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29186             var btnEl = btn.child("button:first");
29187             btnEl.on('focus', this.onFocus, this);
29188             btnEl.on('blur', this.onBlur, this);
29189             if(this.cls){
29190                 btn.addClass(this.cls);
29191             }
29192             if(this.icon){
29193                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29194             }
29195             if(this.iconCls){
29196                 btnEl.addClass(this.iconCls);
29197                 if(!this.cls){
29198                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29199                 }
29200             }
29201             if(this.tabIndex !== undefined){
29202                 btnEl.dom.tabIndex = this.tabIndex;
29203             }
29204             if(this.tooltip){
29205                 if(typeof this.tooltip == 'object'){
29206                     Roo.QuickTips.tips(Roo.apply({
29207                           target: btnEl.id
29208                     }, this.tooltip));
29209                 } else {
29210                     btnEl.dom[this.tooltipType] = this.tooltip;
29211                 }
29212             }
29213         }else{
29214             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29215         }
29216         this.el = btn;
29217         if(this.id){
29218             this.el.dom.id = this.el.id = this.id;
29219         }
29220         if(this.menu){
29221             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29222             this.menu.on("show", this.onMenuShow, this);
29223             this.menu.on("hide", this.onMenuHide, this);
29224         }
29225         btn.addClass("x-btn");
29226         if(Roo.isIE && !Roo.isIE7){
29227             this.autoWidth.defer(1, this);
29228         }else{
29229             this.autoWidth();
29230         }
29231         if(this.handleMouseEvents){
29232             btn.on("mouseover", this.onMouseOver, this);
29233             btn.on("mouseout", this.onMouseOut, this);
29234             btn.on("mousedown", this.onMouseDown, this);
29235         }
29236         btn.on(this.clickEvent, this.onClick, this);
29237         //btn.on("mouseup", this.onMouseUp, this);
29238         if(this.hidden){
29239             this.hide();
29240         }
29241         if(this.disabled){
29242             this.disable();
29243         }
29244         Roo.ButtonToggleMgr.register(this);
29245         if(this.pressed){
29246             this.el.addClass("x-btn-pressed");
29247         }
29248         if(this.repeat){
29249             var repeater = new Roo.util.ClickRepeater(btn,
29250                 typeof this.repeat == "object" ? this.repeat : {}
29251             );
29252             repeater.on("click", this.onClick,  this);
29253         }
29254         
29255         this.fireEvent('render', this);
29256         
29257     },
29258     /**
29259      * Returns the button's underlying element
29260      * @return {Roo.Element} The element
29261      */
29262     getEl : function(){
29263         return this.el;  
29264     },
29265     
29266     /**
29267      * Destroys this Button and removes any listeners.
29268      */
29269     destroy : function(){
29270         Roo.ButtonToggleMgr.unregister(this);
29271         this.el.removeAllListeners();
29272         this.purgeListeners();
29273         this.el.remove();
29274     },
29275
29276     // private
29277     autoWidth : function(){
29278         if(this.el){
29279             this.el.setWidth("auto");
29280             if(Roo.isIE7 && Roo.isStrict){
29281                 var ib = this.el.child('button');
29282                 if(ib && ib.getWidth() > 20){
29283                     ib.clip();
29284                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29285                 }
29286             }
29287             if(this.minWidth){
29288                 if(this.hidden){
29289                     this.el.beginMeasure();
29290                 }
29291                 if(this.el.getWidth() < this.minWidth){
29292                     this.el.setWidth(this.minWidth);
29293                 }
29294                 if(this.hidden){
29295                     this.el.endMeasure();
29296                 }
29297             }
29298         }
29299     },
29300
29301     /**
29302      * Assigns this button's click handler
29303      * @param {Function} handler The function to call when the button is clicked
29304      * @param {Object} scope (optional) Scope for the function passed in
29305      */
29306     setHandler : function(handler, scope){
29307         this.handler = handler;
29308         this.scope = scope;  
29309     },
29310     
29311     /**
29312      * Sets this button's text
29313      * @param {String} text The button text
29314      */
29315     setText : function(text){
29316         this.text = text;
29317         if(this.el){
29318             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29319         }
29320         this.autoWidth();
29321     },
29322     
29323     /**
29324      * Gets the text for this button
29325      * @return {String} The button text
29326      */
29327     getText : function(){
29328         return this.text;  
29329     },
29330     
29331     /**
29332      * Show this button
29333      */
29334     show: function(){
29335         this.hidden = false;
29336         if(this.el){
29337             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29338         }
29339     },
29340     
29341     /**
29342      * Hide this button
29343      */
29344     hide: function(){
29345         this.hidden = true;
29346         if(this.el){
29347             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29348         }
29349     },
29350     
29351     /**
29352      * Convenience function for boolean show/hide
29353      * @param {Boolean} visible True to show, false to hide
29354      */
29355     setVisible: function(visible){
29356         if(visible) {
29357             this.show();
29358         }else{
29359             this.hide();
29360         }
29361     },
29362     
29363     /**
29364      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29365      * @param {Boolean} state (optional) Force a particular state
29366      */
29367     toggle : function(state){
29368         state = state === undefined ? !this.pressed : state;
29369         if(state != this.pressed){
29370             if(state){
29371                 this.el.addClass("x-btn-pressed");
29372                 this.pressed = true;
29373                 this.fireEvent("toggle", this, true);
29374             }else{
29375                 this.el.removeClass("x-btn-pressed");
29376                 this.pressed = false;
29377                 this.fireEvent("toggle", this, false);
29378             }
29379             if(this.toggleHandler){
29380                 this.toggleHandler.call(this.scope || this, this, state);
29381             }
29382         }
29383     },
29384     
29385     /**
29386      * Focus the button
29387      */
29388     focus : function(){
29389         this.el.child('button:first').focus();
29390     },
29391     
29392     /**
29393      * Disable this button
29394      */
29395     disable : function(){
29396         if(this.el){
29397             this.el.addClass("x-btn-disabled");
29398         }
29399         this.disabled = true;
29400     },
29401     
29402     /**
29403      * Enable this button
29404      */
29405     enable : function(){
29406         if(this.el){
29407             this.el.removeClass("x-btn-disabled");
29408         }
29409         this.disabled = false;
29410     },
29411
29412     /**
29413      * Convenience function for boolean enable/disable
29414      * @param {Boolean} enabled True to enable, false to disable
29415      */
29416     setDisabled : function(v){
29417         this[v !== true ? "enable" : "disable"]();
29418     },
29419
29420     // private
29421     onClick : function(e)
29422     {
29423         if(e){
29424             e.preventDefault();
29425         }
29426         if(e.button != 0){
29427             return;
29428         }
29429         if(!this.disabled){
29430             if(this.enableToggle){
29431                 this.toggle();
29432             }
29433             if(this.menu && !this.menu.isVisible()){
29434                 this.menu.show(this.el, this.menuAlign);
29435             }
29436             this.fireEvent("click", this, e);
29437             if(this.handler){
29438                 this.el.removeClass("x-btn-over");
29439                 this.handler.call(this.scope || this, this, e);
29440             }
29441         }
29442     },
29443     // private
29444     onMouseOver : function(e){
29445         if(!this.disabled){
29446             this.el.addClass("x-btn-over");
29447             this.fireEvent('mouseover', this, e);
29448         }
29449     },
29450     // private
29451     onMouseOut : function(e){
29452         if(!e.within(this.el,  true)){
29453             this.el.removeClass("x-btn-over");
29454             this.fireEvent('mouseout', this, e);
29455         }
29456     },
29457     // private
29458     onFocus : function(e){
29459         if(!this.disabled){
29460             this.el.addClass("x-btn-focus");
29461         }
29462     },
29463     // private
29464     onBlur : function(e){
29465         this.el.removeClass("x-btn-focus");
29466     },
29467     // private
29468     onMouseDown : function(e){
29469         if(!this.disabled && e.button == 0){
29470             this.el.addClass("x-btn-click");
29471             Roo.get(document).on('mouseup', this.onMouseUp, this);
29472         }
29473     },
29474     // private
29475     onMouseUp : function(e){
29476         if(e.button == 0){
29477             this.el.removeClass("x-btn-click");
29478             Roo.get(document).un('mouseup', this.onMouseUp, this);
29479         }
29480     },
29481     // private
29482     onMenuShow : function(e){
29483         this.el.addClass("x-btn-menu-active");
29484     },
29485     // private
29486     onMenuHide : function(e){
29487         this.el.removeClass("x-btn-menu-active");
29488     }   
29489 });
29490
29491 // Private utility class used by Button
29492 Roo.ButtonToggleMgr = function(){
29493    var groups = {};
29494    
29495    function toggleGroup(btn, state){
29496        if(state){
29497            var g = groups[btn.toggleGroup];
29498            for(var i = 0, l = g.length; i < l; i++){
29499                if(g[i] != btn){
29500                    g[i].toggle(false);
29501                }
29502            }
29503        }
29504    }
29505    
29506    return {
29507        register : function(btn){
29508            if(!btn.toggleGroup){
29509                return;
29510            }
29511            var g = groups[btn.toggleGroup];
29512            if(!g){
29513                g = groups[btn.toggleGroup] = [];
29514            }
29515            g.push(btn);
29516            btn.on("toggle", toggleGroup);
29517        },
29518        
29519        unregister : function(btn){
29520            if(!btn.toggleGroup){
29521                return;
29522            }
29523            var g = groups[btn.toggleGroup];
29524            if(g){
29525                g.remove(btn);
29526                btn.un("toggle", toggleGroup);
29527            }
29528        }
29529    };
29530 }();/*
29531  * Based on:
29532  * Ext JS Library 1.1.1
29533  * Copyright(c) 2006-2007, Ext JS, LLC.
29534  *
29535  * Originally Released Under LGPL - original licence link has changed is not relivant.
29536  *
29537  * Fork - LGPL
29538  * <script type="text/javascript">
29539  */
29540  
29541 /**
29542  * @class Roo.SplitButton
29543  * @extends Roo.Button
29544  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29545  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29546  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29547  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29548  * @cfg {String} arrowTooltip The title attribute of the arrow
29549  * @constructor
29550  * Create a new menu button
29551  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29552  * @param {Object} config The config object
29553  */
29554 Roo.SplitButton = function(renderTo, config){
29555     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29556     /**
29557      * @event arrowclick
29558      * Fires when this button's arrow is clicked
29559      * @param {SplitButton} this
29560      * @param {EventObject} e The click event
29561      */
29562     this.addEvents({"arrowclick":true});
29563 };
29564
29565 Roo.extend(Roo.SplitButton, Roo.Button, {
29566     render : function(renderTo){
29567         // this is one sweet looking template!
29568         var tpl = new Roo.Template(
29569             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29570             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29571             '<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>',
29572             "</tbody></table></td><td>",
29573             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29574             '<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>',
29575             "</tbody></table></td></tr></table>"
29576         );
29577         var btn = tpl.append(renderTo, [this.text, this.type], true);
29578         var btnEl = btn.child("button");
29579         if(this.cls){
29580             btn.addClass(this.cls);
29581         }
29582         if(this.icon){
29583             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29584         }
29585         if(this.iconCls){
29586             btnEl.addClass(this.iconCls);
29587             if(!this.cls){
29588                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29589             }
29590         }
29591         this.el = btn;
29592         if(this.handleMouseEvents){
29593             btn.on("mouseover", this.onMouseOver, this);
29594             btn.on("mouseout", this.onMouseOut, this);
29595             btn.on("mousedown", this.onMouseDown, this);
29596             btn.on("mouseup", this.onMouseUp, this);
29597         }
29598         btn.on(this.clickEvent, this.onClick, this);
29599         if(this.tooltip){
29600             if(typeof this.tooltip == 'object'){
29601                 Roo.QuickTips.tips(Roo.apply({
29602                       target: btnEl.id
29603                 }, this.tooltip));
29604             } else {
29605                 btnEl.dom[this.tooltipType] = this.tooltip;
29606             }
29607         }
29608         if(this.arrowTooltip){
29609             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29610         }
29611         if(this.hidden){
29612             this.hide();
29613         }
29614         if(this.disabled){
29615             this.disable();
29616         }
29617         if(this.pressed){
29618             this.el.addClass("x-btn-pressed");
29619         }
29620         if(Roo.isIE && !Roo.isIE7){
29621             this.autoWidth.defer(1, this);
29622         }else{
29623             this.autoWidth();
29624         }
29625         if(this.menu){
29626             this.menu.on("show", this.onMenuShow, this);
29627             this.menu.on("hide", this.onMenuHide, this);
29628         }
29629         this.fireEvent('render', this);
29630     },
29631
29632     // private
29633     autoWidth : function(){
29634         if(this.el){
29635             var tbl = this.el.child("table:first");
29636             var tbl2 = this.el.child("table:last");
29637             this.el.setWidth("auto");
29638             tbl.setWidth("auto");
29639             if(Roo.isIE7 && Roo.isStrict){
29640                 var ib = this.el.child('button:first');
29641                 if(ib && ib.getWidth() > 20){
29642                     ib.clip();
29643                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29644                 }
29645             }
29646             if(this.minWidth){
29647                 if(this.hidden){
29648                     this.el.beginMeasure();
29649                 }
29650                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29651                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29652                 }
29653                 if(this.hidden){
29654                     this.el.endMeasure();
29655                 }
29656             }
29657             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29658         } 
29659     },
29660     /**
29661      * Sets this button's click handler
29662      * @param {Function} handler The function to call when the button is clicked
29663      * @param {Object} scope (optional) Scope for the function passed above
29664      */
29665     setHandler : function(handler, scope){
29666         this.handler = handler;
29667         this.scope = scope;  
29668     },
29669     
29670     /**
29671      * Sets this button's arrow click handler
29672      * @param {Function} handler The function to call when the arrow is clicked
29673      * @param {Object} scope (optional) Scope for the function passed above
29674      */
29675     setArrowHandler : function(handler, scope){
29676         this.arrowHandler = handler;
29677         this.scope = scope;  
29678     },
29679     
29680     /**
29681      * Focus the button
29682      */
29683     focus : function(){
29684         if(this.el){
29685             this.el.child("button:first").focus();
29686         }
29687     },
29688
29689     // private
29690     onClick : function(e){
29691         e.preventDefault();
29692         if(!this.disabled){
29693             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29694                 if(this.menu && !this.menu.isVisible()){
29695                     this.menu.show(this.el, this.menuAlign);
29696                 }
29697                 this.fireEvent("arrowclick", this, e);
29698                 if(this.arrowHandler){
29699                     this.arrowHandler.call(this.scope || this, this, e);
29700                 }
29701             }else{
29702                 this.fireEvent("click", this, e);
29703                 if(this.handler){
29704                     this.handler.call(this.scope || this, this, e);
29705                 }
29706             }
29707         }
29708     },
29709     // private
29710     onMouseDown : function(e){
29711         if(!this.disabled){
29712             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29713         }
29714     },
29715     // private
29716     onMouseUp : function(e){
29717         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29718     }   
29719 });
29720
29721
29722 // backwards compat
29723 Roo.MenuButton = Roo.SplitButton;/*
29724  * Based on:
29725  * Ext JS Library 1.1.1
29726  * Copyright(c) 2006-2007, Ext JS, LLC.
29727  *
29728  * Originally Released Under LGPL - original licence link has changed is not relivant.
29729  *
29730  * Fork - LGPL
29731  * <script type="text/javascript">
29732  */
29733
29734 /**
29735  * @class Roo.Toolbar
29736  * Basic Toolbar class.
29737  * @constructor
29738  * Creates a new Toolbar
29739  * @param {Object} container The config object
29740  */ 
29741 Roo.Toolbar = function(container, buttons, config)
29742 {
29743     /// old consturctor format still supported..
29744     if(container instanceof Array){ // omit the container for later rendering
29745         buttons = container;
29746         config = buttons;
29747         container = null;
29748     }
29749     if (typeof(container) == 'object' && container.xtype) {
29750         config = container;
29751         container = config.container;
29752         buttons = config.buttons || []; // not really - use items!!
29753     }
29754     var xitems = [];
29755     if (config && config.items) {
29756         xitems = config.items;
29757         delete config.items;
29758     }
29759     Roo.apply(this, config);
29760     this.buttons = buttons;
29761     
29762     if(container){
29763         this.render(container);
29764     }
29765     this.xitems = xitems;
29766     Roo.each(xitems, function(b) {
29767         this.add(b);
29768     }, this);
29769     
29770 };
29771
29772 Roo.Toolbar.prototype = {
29773     /**
29774      * @cfg {Array} items
29775      * array of button configs or elements to add (will be converted to a MixedCollection)
29776      */
29777     
29778     /**
29779      * @cfg {String/HTMLElement/Element} container
29780      * The id or element that will contain the toolbar
29781      */
29782     // private
29783     render : function(ct){
29784         this.el = Roo.get(ct);
29785         if(this.cls){
29786             this.el.addClass(this.cls);
29787         }
29788         // using a table allows for vertical alignment
29789         // 100% width is needed by Safari...
29790         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29791         this.tr = this.el.child("tr", true);
29792         var autoId = 0;
29793         this.items = new Roo.util.MixedCollection(false, function(o){
29794             return o.id || ("item" + (++autoId));
29795         });
29796         if(this.buttons){
29797             this.add.apply(this, this.buttons);
29798             delete this.buttons;
29799         }
29800     },
29801
29802     /**
29803      * Adds element(s) to the toolbar -- this function takes a variable number of 
29804      * arguments of mixed type and adds them to the toolbar.
29805      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29806      * <ul>
29807      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29808      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29809      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29810      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29811      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29812      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29813      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29814      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29815      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29816      * </ul>
29817      * @param {Mixed} arg2
29818      * @param {Mixed} etc.
29819      */
29820     add : function(){
29821         var a = arguments, l = a.length;
29822         for(var i = 0; i < l; i++){
29823             this._add(a[i]);
29824         }
29825     },
29826     // private..
29827     _add : function(el) {
29828         
29829         if (el.xtype) {
29830             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29831         }
29832         
29833         if (el.applyTo){ // some kind of form field
29834             return this.addField(el);
29835         } 
29836         if (el.render){ // some kind of Toolbar.Item
29837             return this.addItem(el);
29838         }
29839         if (typeof el == "string"){ // string
29840             if(el == "separator" || el == "-"){
29841                 return this.addSeparator();
29842             }
29843             if (el == " "){
29844                 return this.addSpacer();
29845             }
29846             if(el == "->"){
29847                 return this.addFill();
29848             }
29849             return this.addText(el);
29850             
29851         }
29852         if(el.tagName){ // element
29853             return this.addElement(el);
29854         }
29855         if(typeof el == "object"){ // must be button config?
29856             return this.addButton(el);
29857         }
29858         // and now what?!?!
29859         return false;
29860         
29861     },
29862     
29863     /**
29864      * Add an Xtype element
29865      * @param {Object} xtype Xtype Object
29866      * @return {Object} created Object
29867      */
29868     addxtype : function(e){
29869         return this.add(e);  
29870     },
29871     
29872     /**
29873      * Returns the Element for this toolbar.
29874      * @return {Roo.Element}
29875      */
29876     getEl : function(){
29877         return this.el;  
29878     },
29879     
29880     /**
29881      * Adds a separator
29882      * @return {Roo.Toolbar.Item} The separator item
29883      */
29884     addSeparator : function(){
29885         return this.addItem(new Roo.Toolbar.Separator());
29886     },
29887
29888     /**
29889      * Adds a spacer element
29890      * @return {Roo.Toolbar.Spacer} The spacer item
29891      */
29892     addSpacer : function(){
29893         return this.addItem(new Roo.Toolbar.Spacer());
29894     },
29895
29896     /**
29897      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29898      * @return {Roo.Toolbar.Fill} The fill item
29899      */
29900     addFill : function(){
29901         return this.addItem(new Roo.Toolbar.Fill());
29902     },
29903
29904     /**
29905      * Adds any standard HTML element to the toolbar
29906      * @param {String/HTMLElement/Element} el The element or id of the element to add
29907      * @return {Roo.Toolbar.Item} The element's item
29908      */
29909     addElement : function(el){
29910         return this.addItem(new Roo.Toolbar.Item(el));
29911     },
29912     /**
29913      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29914      * @type Roo.util.MixedCollection  
29915      */
29916     items : false,
29917      
29918     /**
29919      * Adds any Toolbar.Item or subclass
29920      * @param {Roo.Toolbar.Item} item
29921      * @return {Roo.Toolbar.Item} The item
29922      */
29923     addItem : function(item){
29924         var td = this.nextBlock();
29925         item.render(td);
29926         this.items.add(item);
29927         return item;
29928     },
29929     
29930     /**
29931      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29932      * @param {Object/Array} config A button config or array of configs
29933      * @return {Roo.Toolbar.Button/Array}
29934      */
29935     addButton : function(config){
29936         if(config instanceof Array){
29937             var buttons = [];
29938             for(var i = 0, len = config.length; i < len; i++) {
29939                 buttons.push(this.addButton(config[i]));
29940             }
29941             return buttons;
29942         }
29943         var b = config;
29944         if(!(config instanceof Roo.Toolbar.Button)){
29945             b = config.split ?
29946                 new Roo.Toolbar.SplitButton(config) :
29947                 new Roo.Toolbar.Button(config);
29948         }
29949         var td = this.nextBlock();
29950         b.render(td);
29951         this.items.add(b);
29952         return b;
29953     },
29954     
29955     /**
29956      * Adds text to the toolbar
29957      * @param {String} text The text to add
29958      * @return {Roo.Toolbar.Item} The element's item
29959      */
29960     addText : function(text){
29961         return this.addItem(new Roo.Toolbar.TextItem(text));
29962     },
29963     
29964     /**
29965      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29966      * @param {Number} index The index where the item is to be inserted
29967      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29968      * @return {Roo.Toolbar.Button/Item}
29969      */
29970     insertButton : function(index, item){
29971         if(item instanceof Array){
29972             var buttons = [];
29973             for(var i = 0, len = item.length; i < len; i++) {
29974                buttons.push(this.insertButton(index + i, item[i]));
29975             }
29976             return buttons;
29977         }
29978         if (!(item instanceof Roo.Toolbar.Button)){
29979            item = new Roo.Toolbar.Button(item);
29980         }
29981         var td = document.createElement("td");
29982         this.tr.insertBefore(td, this.tr.childNodes[index]);
29983         item.render(td);
29984         this.items.insert(index, item);
29985         return item;
29986     },
29987     
29988     /**
29989      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29990      * @param {Object} config
29991      * @return {Roo.Toolbar.Item} The element's item
29992      */
29993     addDom : function(config, returnEl){
29994         var td = this.nextBlock();
29995         Roo.DomHelper.overwrite(td, config);
29996         var ti = new Roo.Toolbar.Item(td.firstChild);
29997         ti.render(td);
29998         this.items.add(ti);
29999         return ti;
30000     },
30001
30002     /**
30003      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30004      * @type Roo.util.MixedCollection  
30005      */
30006     fields : false,
30007     
30008     /**
30009      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30010      * Note: the field should not have been rendered yet. For a field that has already been
30011      * rendered, use {@link #addElement}.
30012      * @param {Roo.form.Field} field
30013      * @return {Roo.ToolbarItem}
30014      */
30015      
30016       
30017     addField : function(field) {
30018         if (!this.fields) {
30019             var autoId = 0;
30020             this.fields = new Roo.util.MixedCollection(false, function(o){
30021                 return o.id || ("item" + (++autoId));
30022             });
30023
30024         }
30025         
30026         var td = this.nextBlock();
30027         field.render(td);
30028         var ti = new Roo.Toolbar.Item(td.firstChild);
30029         ti.render(td);
30030         this.items.add(ti);
30031         this.fields.add(field);
30032         return ti;
30033     },
30034     /**
30035      * Hide the toolbar
30036      * @method hide
30037      */
30038      
30039       
30040     hide : function()
30041     {
30042         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30043         this.el.child('div').hide();
30044     },
30045     /**
30046      * Show the toolbar
30047      * @method show
30048      */
30049     show : function()
30050     {
30051         this.el.child('div').show();
30052     },
30053       
30054     // private
30055     nextBlock : function(){
30056         var td = document.createElement("td");
30057         this.tr.appendChild(td);
30058         return td;
30059     },
30060
30061     // private
30062     destroy : function(){
30063         if(this.items){ // rendered?
30064             Roo.destroy.apply(Roo, this.items.items);
30065         }
30066         if(this.fields){ // rendered?
30067             Roo.destroy.apply(Roo, this.fields.items);
30068         }
30069         Roo.Element.uncache(this.el, this.tr);
30070     }
30071 };
30072
30073 /**
30074  * @class Roo.Toolbar.Item
30075  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30076  * @constructor
30077  * Creates a new Item
30078  * @param {HTMLElement} el 
30079  */
30080 Roo.Toolbar.Item = function(el){
30081     var cfg = {};
30082     if (typeof (el.xtype) != 'undefined') {
30083         cfg = el;
30084         el = cfg.el;
30085     }
30086     
30087     this.el = Roo.getDom(el);
30088     this.id = Roo.id(this.el);
30089     this.hidden = false;
30090     
30091     this.addEvents({
30092          /**
30093              * @event render
30094              * Fires when the button is rendered
30095              * @param {Button} this
30096              */
30097         'render': true
30098     });
30099     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30100 };
30101 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30102 //Roo.Toolbar.Item.prototype = {
30103     
30104     /**
30105      * Get this item's HTML Element
30106      * @return {HTMLElement}
30107      */
30108     getEl : function(){
30109        return this.el;  
30110     },
30111
30112     // private
30113     render : function(td){
30114         
30115          this.td = td;
30116         td.appendChild(this.el);
30117         
30118         this.fireEvent('render', this);
30119     },
30120     
30121     /**
30122      * Removes and destroys this item.
30123      */
30124     destroy : function(){
30125         this.td.parentNode.removeChild(this.td);
30126     },
30127     
30128     /**
30129      * Shows this item.
30130      */
30131     show: function(){
30132         this.hidden = false;
30133         this.td.style.display = "";
30134     },
30135     
30136     /**
30137      * Hides this item.
30138      */
30139     hide: function(){
30140         this.hidden = true;
30141         this.td.style.display = "none";
30142     },
30143     
30144     /**
30145      * Convenience function for boolean show/hide.
30146      * @param {Boolean} visible true to show/false to hide
30147      */
30148     setVisible: function(visible){
30149         if(visible) {
30150             this.show();
30151         }else{
30152             this.hide();
30153         }
30154     },
30155     
30156     /**
30157      * Try to focus this item.
30158      */
30159     focus : function(){
30160         Roo.fly(this.el).focus();
30161     },
30162     
30163     /**
30164      * Disables this item.
30165      */
30166     disable : function(){
30167         Roo.fly(this.td).addClass("x-item-disabled");
30168         this.disabled = true;
30169         this.el.disabled = true;
30170     },
30171     
30172     /**
30173      * Enables this item.
30174      */
30175     enable : function(){
30176         Roo.fly(this.td).removeClass("x-item-disabled");
30177         this.disabled = false;
30178         this.el.disabled = false;
30179     }
30180 });
30181
30182
30183 /**
30184  * @class Roo.Toolbar.Separator
30185  * @extends Roo.Toolbar.Item
30186  * A simple toolbar separator class
30187  * @constructor
30188  * Creates a new Separator
30189  */
30190 Roo.Toolbar.Separator = function(cfg){
30191     
30192     var s = document.createElement("span");
30193     s.className = "ytb-sep";
30194     if (cfg) {
30195         cfg.el = s;
30196     }
30197     
30198     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30199 };
30200 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30201     enable:Roo.emptyFn,
30202     disable:Roo.emptyFn,
30203     focus:Roo.emptyFn
30204 });
30205
30206 /**
30207  * @class Roo.Toolbar.Spacer
30208  * @extends Roo.Toolbar.Item
30209  * A simple element that adds extra horizontal space to a toolbar.
30210  * @constructor
30211  * Creates a new Spacer
30212  */
30213 Roo.Toolbar.Spacer = function(cfg){
30214     var s = document.createElement("div");
30215     s.className = "ytb-spacer";
30216     if (cfg) {
30217         cfg.el = s;
30218     }
30219     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30220 };
30221 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30222     enable:Roo.emptyFn,
30223     disable:Roo.emptyFn,
30224     focus:Roo.emptyFn
30225 });
30226
30227 /**
30228  * @class Roo.Toolbar.Fill
30229  * @extends Roo.Toolbar.Spacer
30230  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30231  * @constructor
30232  * Creates a new Spacer
30233  */
30234 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30235     // private
30236     render : function(td){
30237         td.style.width = '100%';
30238         Roo.Toolbar.Fill.superclass.render.call(this, td);
30239     }
30240 });
30241
30242 /**
30243  * @class Roo.Toolbar.TextItem
30244  * @extends Roo.Toolbar.Item
30245  * A simple class that renders text directly into a toolbar.
30246  * @constructor
30247  * Creates a new TextItem
30248  * @param {String} text
30249  */
30250 Roo.Toolbar.TextItem = function(cfg){
30251     var  text = cfg || "";
30252     if (typeof(cfg) == 'object') {
30253         text = cfg.text || "";
30254     }  else {
30255         cfg = null;
30256     }
30257     var s = document.createElement("span");
30258     s.className = "ytb-text";
30259     s.innerHTML = text;
30260     if (cfg) {
30261         cfg.el  = s;
30262     }
30263     
30264     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30265 };
30266 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30267     
30268      
30269     enable:Roo.emptyFn,
30270     disable:Roo.emptyFn,
30271     focus:Roo.emptyFn
30272 });
30273
30274 /**
30275  * @class Roo.Toolbar.Button
30276  * @extends Roo.Button
30277  * A button that renders into a toolbar.
30278  * @constructor
30279  * Creates a new Button
30280  * @param {Object} config A standard {@link Roo.Button} config object
30281  */
30282 Roo.Toolbar.Button = function(config){
30283     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30284 };
30285 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30286     render : function(td){
30287         this.td = td;
30288         Roo.Toolbar.Button.superclass.render.call(this, td);
30289     },
30290     
30291     /**
30292      * Removes and destroys this button
30293      */
30294     destroy : function(){
30295         Roo.Toolbar.Button.superclass.destroy.call(this);
30296         this.td.parentNode.removeChild(this.td);
30297     },
30298     
30299     /**
30300      * Shows this button
30301      */
30302     show: function(){
30303         this.hidden = false;
30304         this.td.style.display = "";
30305     },
30306     
30307     /**
30308      * Hides this button
30309      */
30310     hide: function(){
30311         this.hidden = true;
30312         this.td.style.display = "none";
30313     },
30314
30315     /**
30316      * Disables this item
30317      */
30318     disable : function(){
30319         Roo.fly(this.td).addClass("x-item-disabled");
30320         this.disabled = true;
30321     },
30322
30323     /**
30324      * Enables this item
30325      */
30326     enable : function(){
30327         Roo.fly(this.td).removeClass("x-item-disabled");
30328         this.disabled = false;
30329     }
30330 });
30331 // backwards compat
30332 Roo.ToolbarButton = Roo.Toolbar.Button;
30333
30334 /**
30335  * @class Roo.Toolbar.SplitButton
30336  * @extends Roo.SplitButton
30337  * A menu button that renders into a toolbar.
30338  * @constructor
30339  * Creates a new SplitButton
30340  * @param {Object} config A standard {@link Roo.SplitButton} config object
30341  */
30342 Roo.Toolbar.SplitButton = function(config){
30343     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30344 };
30345 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30346     render : function(td){
30347         this.td = td;
30348         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30349     },
30350     
30351     /**
30352      * Removes and destroys this button
30353      */
30354     destroy : function(){
30355         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30356         this.td.parentNode.removeChild(this.td);
30357     },
30358     
30359     /**
30360      * Shows this button
30361      */
30362     show: function(){
30363         this.hidden = false;
30364         this.td.style.display = "";
30365     },
30366     
30367     /**
30368      * Hides this button
30369      */
30370     hide: function(){
30371         this.hidden = true;
30372         this.td.style.display = "none";
30373     }
30374 });
30375
30376 // backwards compat
30377 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30378  * Based on:
30379  * Ext JS Library 1.1.1
30380  * Copyright(c) 2006-2007, Ext JS, LLC.
30381  *
30382  * Originally Released Under LGPL - original licence link has changed is not relivant.
30383  *
30384  * Fork - LGPL
30385  * <script type="text/javascript">
30386  */
30387  
30388 /**
30389  * @class Roo.PagingToolbar
30390  * @extends Roo.Toolbar
30391  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30392  * @constructor
30393  * Create a new PagingToolbar
30394  * @param {Object} config The config object
30395  */
30396 Roo.PagingToolbar = function(el, ds, config)
30397 {
30398     // old args format still supported... - xtype is prefered..
30399     if (typeof(el) == 'object' && el.xtype) {
30400         // created from xtype...
30401         config = el;
30402         ds = el.dataSource;
30403         el = config.container;
30404     }
30405     var items = [];
30406     if (config.items) {
30407         items = config.items;
30408         config.items = [];
30409     }
30410     
30411     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30412     this.ds = ds;
30413     this.cursor = 0;
30414     this.renderButtons(this.el);
30415     this.bind(ds);
30416     
30417     // supprot items array.
30418    
30419     Roo.each(items, function(e) {
30420         this.add(Roo.factory(e));
30421     },this);
30422     
30423 };
30424
30425 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30426     /**
30427      * @cfg {Roo.data.Store} dataSource
30428      * The underlying data store providing the paged data
30429      */
30430     /**
30431      * @cfg {String/HTMLElement/Element} container
30432      * container The id or element that will contain the toolbar
30433      */
30434     /**
30435      * @cfg {Boolean} displayInfo
30436      * True to display the displayMsg (defaults to false)
30437      */
30438     /**
30439      * @cfg {Number} pageSize
30440      * The number of records to display per page (defaults to 20)
30441      */
30442     pageSize: 20,
30443     /**
30444      * @cfg {String} displayMsg
30445      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30446      */
30447     displayMsg : 'Displaying {0} - {1} of {2}',
30448     /**
30449      * @cfg {String} emptyMsg
30450      * The message to display when no records are found (defaults to "No data to display")
30451      */
30452     emptyMsg : 'No data to display',
30453     /**
30454      * Customizable piece of the default paging text (defaults to "Page")
30455      * @type String
30456      */
30457     beforePageText : "Page",
30458     /**
30459      * Customizable piece of the default paging text (defaults to "of %0")
30460      * @type String
30461      */
30462     afterPageText : "of {0}",
30463     /**
30464      * Customizable piece of the default paging text (defaults to "First Page")
30465      * @type String
30466      */
30467     firstText : "First Page",
30468     /**
30469      * Customizable piece of the default paging text (defaults to "Previous Page")
30470      * @type String
30471      */
30472     prevText : "Previous Page",
30473     /**
30474      * Customizable piece of the default paging text (defaults to "Next Page")
30475      * @type String
30476      */
30477     nextText : "Next Page",
30478     /**
30479      * Customizable piece of the default paging text (defaults to "Last Page")
30480      * @type String
30481      */
30482     lastText : "Last Page",
30483     /**
30484      * Customizable piece of the default paging text (defaults to "Refresh")
30485      * @type String
30486      */
30487     refreshText : "Refresh",
30488
30489     // private
30490     renderButtons : function(el){
30491         Roo.PagingToolbar.superclass.render.call(this, el);
30492         this.first = this.addButton({
30493             tooltip: this.firstText,
30494             cls: "x-btn-icon x-grid-page-first",
30495             disabled: true,
30496             handler: this.onClick.createDelegate(this, ["first"])
30497         });
30498         this.prev = this.addButton({
30499             tooltip: this.prevText,
30500             cls: "x-btn-icon x-grid-page-prev",
30501             disabled: true,
30502             handler: this.onClick.createDelegate(this, ["prev"])
30503         });
30504         //this.addSeparator();
30505         this.add(this.beforePageText);
30506         this.field = Roo.get(this.addDom({
30507            tag: "input",
30508            type: "text",
30509            size: "3",
30510            value: "1",
30511            cls: "x-grid-page-number"
30512         }).el);
30513         this.field.on("keydown", this.onPagingKeydown, this);
30514         this.field.on("focus", function(){this.dom.select();});
30515         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30516         this.field.setHeight(18);
30517         //this.addSeparator();
30518         this.next = this.addButton({
30519             tooltip: this.nextText,
30520             cls: "x-btn-icon x-grid-page-next",
30521             disabled: true,
30522             handler: this.onClick.createDelegate(this, ["next"])
30523         });
30524         this.last = this.addButton({
30525             tooltip: this.lastText,
30526             cls: "x-btn-icon x-grid-page-last",
30527             disabled: true,
30528             handler: this.onClick.createDelegate(this, ["last"])
30529         });
30530         //this.addSeparator();
30531         this.loading = this.addButton({
30532             tooltip: this.refreshText,
30533             cls: "x-btn-icon x-grid-loading",
30534             handler: this.onClick.createDelegate(this, ["refresh"])
30535         });
30536
30537         if(this.displayInfo){
30538             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30539         }
30540     },
30541
30542     // private
30543     updateInfo : function(){
30544         if(this.displayEl){
30545             var count = this.ds.getCount();
30546             var msg = count == 0 ?
30547                 this.emptyMsg :
30548                 String.format(
30549                     this.displayMsg,
30550                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30551                 );
30552             this.displayEl.update(msg);
30553         }
30554     },
30555
30556     // private
30557     onLoad : function(ds, r, o){
30558        this.cursor = o.params ? o.params.start : 0;
30559        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30560
30561        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30562        this.field.dom.value = ap;
30563        this.first.setDisabled(ap == 1);
30564        this.prev.setDisabled(ap == 1);
30565        this.next.setDisabled(ap == ps);
30566        this.last.setDisabled(ap == ps);
30567        this.loading.enable();
30568        this.updateInfo();
30569     },
30570
30571     // private
30572     getPageData : function(){
30573         var total = this.ds.getTotalCount();
30574         return {
30575             total : total,
30576             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30577             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30578         };
30579     },
30580
30581     // private
30582     onLoadError : function(){
30583         this.loading.enable();
30584     },
30585
30586     // private
30587     onPagingKeydown : function(e){
30588         var k = e.getKey();
30589         var d = this.getPageData();
30590         if(k == e.RETURN){
30591             var v = this.field.dom.value, pageNum;
30592             if(!v || isNaN(pageNum = parseInt(v, 10))){
30593                 this.field.dom.value = d.activePage;
30594                 return;
30595             }
30596             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30597             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30598             e.stopEvent();
30599         }
30600         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))
30601         {
30602           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30603           this.field.dom.value = pageNum;
30604           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30605           e.stopEvent();
30606         }
30607         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30608         {
30609           var v = this.field.dom.value, pageNum; 
30610           var increment = (e.shiftKey) ? 10 : 1;
30611           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30612             increment *= -1;
30613           }
30614           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30615             this.field.dom.value = d.activePage;
30616             return;
30617           }
30618           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30619           {
30620             this.field.dom.value = parseInt(v, 10) + increment;
30621             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30623           }
30624           e.stopEvent();
30625         }
30626     },
30627
30628     // private
30629     beforeLoad : function(){
30630         if(this.loading){
30631             this.loading.disable();
30632         }
30633     },
30634
30635     // private
30636     onClick : function(which){
30637         var ds = this.ds;
30638         switch(which){
30639             case "first":
30640                 ds.load({params:{start: 0, limit: this.pageSize}});
30641             break;
30642             case "prev":
30643                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30644             break;
30645             case "next":
30646                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30647             break;
30648             case "last":
30649                 var total = ds.getTotalCount();
30650                 var extra = total % this.pageSize;
30651                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30652                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30653             break;
30654             case "refresh":
30655                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30656             break;
30657         }
30658     },
30659
30660     /**
30661      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30662      * @param {Roo.data.Store} store The data store to unbind
30663      */
30664     unbind : function(ds){
30665         ds.un("beforeload", this.beforeLoad, this);
30666         ds.un("load", this.onLoad, this);
30667         ds.un("loadexception", this.onLoadError, this);
30668         ds.un("remove", this.updateInfo, this);
30669         ds.un("add", this.updateInfo, this);
30670         this.ds = undefined;
30671     },
30672
30673     /**
30674      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30675      * @param {Roo.data.Store} store The data store to bind
30676      */
30677     bind : function(ds){
30678         ds.on("beforeload", this.beforeLoad, this);
30679         ds.on("load", this.onLoad, this);
30680         ds.on("loadexception", this.onLoadError, this);
30681         ds.on("remove", this.updateInfo, this);
30682         ds.on("add", this.updateInfo, this);
30683         this.ds = ds;
30684     }
30685 });/*
30686  * Based on:
30687  * Ext JS Library 1.1.1
30688  * Copyright(c) 2006-2007, Ext JS, LLC.
30689  *
30690  * Originally Released Under LGPL - original licence link has changed is not relivant.
30691  *
30692  * Fork - LGPL
30693  * <script type="text/javascript">
30694  */
30695
30696 /**
30697  * @class Roo.Resizable
30698  * @extends Roo.util.Observable
30699  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30700  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30701  * 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
30702  * the element will be wrapped for you automatically.</p>
30703  * <p>Here is the list of valid resize handles:</p>
30704  * <pre>
30705 Value   Description
30706 ------  -------------------
30707  'n'     north
30708  's'     south
30709  'e'     east
30710  'w'     west
30711  'nw'    northwest
30712  'sw'    southwest
30713  'se'    southeast
30714  'ne'    northeast
30715  'hd'    horizontal drag
30716  'all'   all
30717 </pre>
30718  * <p>Here's an example showing the creation of a typical Resizable:</p>
30719  * <pre><code>
30720 var resizer = new Roo.Resizable("element-id", {
30721     handles: 'all',
30722     minWidth: 200,
30723     minHeight: 100,
30724     maxWidth: 500,
30725     maxHeight: 400,
30726     pinned: true
30727 });
30728 resizer.on("resize", myHandler);
30729 </code></pre>
30730  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30731  * resizer.east.setDisplayed(false);</p>
30732  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30733  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30734  * resize operation's new size (defaults to [0, 0])
30735  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30736  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30737  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30738  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30739  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30740  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30741  * @cfg {Number} width The width of the element in pixels (defaults to null)
30742  * @cfg {Number} height The height of the element in pixels (defaults to null)
30743  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30744  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30745  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30746  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30747  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30748  * in favor of the handles config option (defaults to false)
30749  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30750  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30751  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30752  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30753  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30754  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30755  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30756  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30757  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30758  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30759  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30760  * @constructor
30761  * Create a new resizable component
30762  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30763  * @param {Object} config configuration options
30764   */
30765 Roo.Resizable = function(el, config)
30766 {
30767     this.el = Roo.get(el);
30768
30769     if(config && config.wrap){
30770         config.resizeChild = this.el;
30771         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30772         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30773         this.el.setStyle("overflow", "hidden");
30774         this.el.setPositioning(config.resizeChild.getPositioning());
30775         config.resizeChild.clearPositioning();
30776         if(!config.width || !config.height){
30777             var csize = config.resizeChild.getSize();
30778             this.el.setSize(csize.width, csize.height);
30779         }
30780         if(config.pinned && !config.adjustments){
30781             config.adjustments = "auto";
30782         }
30783     }
30784
30785     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30786     this.proxy.unselectable();
30787     this.proxy.enableDisplayMode('block');
30788
30789     Roo.apply(this, config);
30790
30791     if(this.pinned){
30792         this.disableTrackOver = true;
30793         this.el.addClass("x-resizable-pinned");
30794     }
30795     // if the element isn't positioned, make it relative
30796     var position = this.el.getStyle("position");
30797     if(position != "absolute" && position != "fixed"){
30798         this.el.setStyle("position", "relative");
30799     }
30800     if(!this.handles){ // no handles passed, must be legacy style
30801         this.handles = 's,e,se';
30802         if(this.multiDirectional){
30803             this.handles += ',n,w';
30804         }
30805     }
30806     if(this.handles == "all"){
30807         this.handles = "n s e w ne nw se sw";
30808     }
30809     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30810     var ps = Roo.Resizable.positions;
30811     for(var i = 0, len = hs.length; i < len; i++){
30812         if(hs[i] && ps[hs[i]]){
30813             var pos = ps[hs[i]];
30814             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30815         }
30816     }
30817     // legacy
30818     this.corner = this.southeast;
30819     
30820     // updateBox = the box can move..
30821     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30822         this.updateBox = true;
30823     }
30824
30825     this.activeHandle = null;
30826
30827     if(this.resizeChild){
30828         if(typeof this.resizeChild == "boolean"){
30829             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30830         }else{
30831             this.resizeChild = Roo.get(this.resizeChild, true);
30832         }
30833     }
30834     
30835     if(this.adjustments == "auto"){
30836         var rc = this.resizeChild;
30837         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30838         if(rc && (hw || hn)){
30839             rc.position("relative");
30840             rc.setLeft(hw ? hw.el.getWidth() : 0);
30841             rc.setTop(hn ? hn.el.getHeight() : 0);
30842         }
30843         this.adjustments = [
30844             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30845             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30846         ];
30847     }
30848
30849     if(this.draggable){
30850         this.dd = this.dynamic ?
30851             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30852         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30853     }
30854
30855     // public events
30856     this.addEvents({
30857         /**
30858          * @event beforeresize
30859          * Fired before resize is allowed. Set enabled to false to cancel resize.
30860          * @param {Roo.Resizable} this
30861          * @param {Roo.EventObject} e The mousedown event
30862          */
30863         "beforeresize" : true,
30864         /**
30865          * @event resizing
30866          * Fired a resizing.
30867          * @param {Roo.Resizable} this
30868          * @param {Number} x The new x position
30869          * @param {Number} y The new y position
30870          * @param {Number} w The new w width
30871          * @param {Number} h The new h hight
30872          * @param {Roo.EventObject} e The mouseup event
30873          */
30874         "resizing" : true,
30875         /**
30876          * @event resize
30877          * Fired after a resize.
30878          * @param {Roo.Resizable} this
30879          * @param {Number} width The new width
30880          * @param {Number} height The new height
30881          * @param {Roo.EventObject} e The mouseup event
30882          */
30883         "resize" : true
30884     });
30885
30886     if(this.width !== null && this.height !== null){
30887         this.resizeTo(this.width, this.height);
30888     }else{
30889         this.updateChildSize();
30890     }
30891     if(Roo.isIE){
30892         this.el.dom.style.zoom = 1;
30893     }
30894     Roo.Resizable.superclass.constructor.call(this);
30895 };
30896
30897 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30898         resizeChild : false,
30899         adjustments : [0, 0],
30900         minWidth : 5,
30901         minHeight : 5,
30902         maxWidth : 10000,
30903         maxHeight : 10000,
30904         enabled : true,
30905         animate : false,
30906         duration : .35,
30907         dynamic : false,
30908         handles : false,
30909         multiDirectional : false,
30910         disableTrackOver : false,
30911         easing : 'easeOutStrong',
30912         widthIncrement : 0,
30913         heightIncrement : 0,
30914         pinned : false,
30915         width : null,
30916         height : null,
30917         preserveRatio : false,
30918         transparent: false,
30919         minX: 0,
30920         minY: 0,
30921         draggable: false,
30922
30923         /**
30924          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30925          */
30926         constrainTo: undefined,
30927         /**
30928          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30929          */
30930         resizeRegion: undefined,
30931
30932
30933     /**
30934      * Perform a manual resize
30935      * @param {Number} width
30936      * @param {Number} height
30937      */
30938     resizeTo : function(width, height){
30939         this.el.setSize(width, height);
30940         this.updateChildSize();
30941         this.fireEvent("resize", this, width, height, null);
30942     },
30943
30944     // private
30945     startSizing : function(e, handle){
30946         this.fireEvent("beforeresize", this, e);
30947         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30948
30949             if(!this.overlay){
30950                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30951                 this.overlay.unselectable();
30952                 this.overlay.enableDisplayMode("block");
30953                 this.overlay.on("mousemove", this.onMouseMove, this);
30954                 this.overlay.on("mouseup", this.onMouseUp, this);
30955             }
30956             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30957
30958             this.resizing = true;
30959             this.startBox = this.el.getBox();
30960             this.startPoint = e.getXY();
30961             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30962                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30963
30964             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30965             this.overlay.show();
30966
30967             if(this.constrainTo) {
30968                 var ct = Roo.get(this.constrainTo);
30969                 this.resizeRegion = ct.getRegion().adjust(
30970                     ct.getFrameWidth('t'),
30971                     ct.getFrameWidth('l'),
30972                     -ct.getFrameWidth('b'),
30973                     -ct.getFrameWidth('r')
30974                 );
30975             }
30976
30977             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30978             this.proxy.show();
30979             this.proxy.setBox(this.startBox);
30980             if(!this.dynamic){
30981                 this.proxy.setStyle('visibility', 'visible');
30982             }
30983         }
30984     },
30985
30986     // private
30987     onMouseDown : function(handle, e){
30988         if(this.enabled){
30989             e.stopEvent();
30990             this.activeHandle = handle;
30991             this.startSizing(e, handle);
30992         }
30993     },
30994
30995     // private
30996     onMouseUp : function(e){
30997         var size = this.resizeElement();
30998         this.resizing = false;
30999         this.handleOut();
31000         this.overlay.hide();
31001         this.proxy.hide();
31002         this.fireEvent("resize", this, size.width, size.height, e);
31003     },
31004
31005     // private
31006     updateChildSize : function(){
31007         
31008         if(this.resizeChild){
31009             var el = this.el;
31010             var child = this.resizeChild;
31011             var adj = this.adjustments;
31012             if(el.dom.offsetWidth){
31013                 var b = el.getSize(true);
31014                 child.setSize(b.width+adj[0], b.height+adj[1]);
31015             }
31016             // Second call here for IE
31017             // The first call enables instant resizing and
31018             // the second call corrects scroll bars if they
31019             // exist
31020             if(Roo.isIE){
31021                 setTimeout(function(){
31022                     if(el.dom.offsetWidth){
31023                         var b = el.getSize(true);
31024                         child.setSize(b.width+adj[0], b.height+adj[1]);
31025                     }
31026                 }, 10);
31027             }
31028         }
31029     },
31030
31031     // private
31032     snap : function(value, inc, min){
31033         if(!inc || !value) {
31034             return value;
31035         }
31036         var newValue = value;
31037         var m = value % inc;
31038         if(m > 0){
31039             if(m > (inc/2)){
31040                 newValue = value + (inc-m);
31041             }else{
31042                 newValue = value - m;
31043             }
31044         }
31045         return Math.max(min, newValue);
31046     },
31047
31048     // private
31049     resizeElement : function(){
31050         var box = this.proxy.getBox();
31051         if(this.updateBox){
31052             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31053         }else{
31054             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31055         }
31056         this.updateChildSize();
31057         if(!this.dynamic){
31058             this.proxy.hide();
31059         }
31060         return box;
31061     },
31062
31063     // private
31064     constrain : function(v, diff, m, mx){
31065         if(v - diff < m){
31066             diff = v - m;
31067         }else if(v - diff > mx){
31068             diff = mx - v;
31069         }
31070         return diff;
31071     },
31072
31073     // private
31074     onMouseMove : function(e){
31075         
31076         if(this.enabled){
31077             try{// try catch so if something goes wrong the user doesn't get hung
31078
31079             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31080                 return;
31081             }
31082
31083             //var curXY = this.startPoint;
31084             var curSize = this.curSize || this.startBox;
31085             var x = this.startBox.x, y = this.startBox.y;
31086             var ox = x, oy = y;
31087             var w = curSize.width, h = curSize.height;
31088             var ow = w, oh = h;
31089             var mw = this.minWidth, mh = this.minHeight;
31090             var mxw = this.maxWidth, mxh = this.maxHeight;
31091             var wi = this.widthIncrement;
31092             var hi = this.heightIncrement;
31093
31094             var eventXY = e.getXY();
31095             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31096             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31097
31098             var pos = this.activeHandle.position;
31099
31100             switch(pos){
31101                 case "east":
31102                     w += diffX;
31103                     w = Math.min(Math.max(mw, w), mxw);
31104                     break;
31105              
31106                 case "south":
31107                     h += diffY;
31108                     h = Math.min(Math.max(mh, h), mxh);
31109                     break;
31110                 case "southeast":
31111                     w += diffX;
31112                     h += diffY;
31113                     w = Math.min(Math.max(mw, w), mxw);
31114                     h = Math.min(Math.max(mh, h), mxh);
31115                     break;
31116                 case "north":
31117                     diffY = this.constrain(h, diffY, mh, mxh);
31118                     y += diffY;
31119                     h -= diffY;
31120                     break;
31121                 case "hdrag":
31122                     
31123                     if (wi) {
31124                         var adiffX = Math.abs(diffX);
31125                         var sub = (adiffX % wi); // how much 
31126                         if (sub > (wi/2)) { // far enough to snap
31127                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31128                         } else {
31129                             // remove difference.. 
31130                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31131                         }
31132                     }
31133                     x += diffX;
31134                     x = Math.max(this.minX, x);
31135                     break;
31136                 case "west":
31137                     diffX = this.constrain(w, diffX, mw, mxw);
31138                     x += diffX;
31139                     w -= diffX;
31140                     break;
31141                 case "northeast":
31142                     w += diffX;
31143                     w = Math.min(Math.max(mw, w), mxw);
31144                     diffY = this.constrain(h, diffY, mh, mxh);
31145                     y += diffY;
31146                     h -= diffY;
31147                     break;
31148                 case "northwest":
31149                     diffX = this.constrain(w, diffX, mw, mxw);
31150                     diffY = this.constrain(h, diffY, mh, mxh);
31151                     y += diffY;
31152                     h -= diffY;
31153                     x += diffX;
31154                     w -= diffX;
31155                     break;
31156                case "southwest":
31157                     diffX = this.constrain(w, diffX, mw, mxw);
31158                     h += diffY;
31159                     h = Math.min(Math.max(mh, h), mxh);
31160                     x += diffX;
31161                     w -= diffX;
31162                     break;
31163             }
31164
31165             var sw = this.snap(w, wi, mw);
31166             var sh = this.snap(h, hi, mh);
31167             if(sw != w || sh != h){
31168                 switch(pos){
31169                     case "northeast":
31170                         y -= sh - h;
31171                     break;
31172                     case "north":
31173                         y -= sh - h;
31174                         break;
31175                     case "southwest":
31176                         x -= sw - w;
31177                     break;
31178                     case "west":
31179                         x -= sw - w;
31180                         break;
31181                     case "northwest":
31182                         x -= sw - w;
31183                         y -= sh - h;
31184                     break;
31185                 }
31186                 w = sw;
31187                 h = sh;
31188             }
31189
31190             if(this.preserveRatio){
31191                 switch(pos){
31192                     case "southeast":
31193                     case "east":
31194                         h = oh * (w/ow);
31195                         h = Math.min(Math.max(mh, h), mxh);
31196                         w = ow * (h/oh);
31197                        break;
31198                     case "south":
31199                         w = ow * (h/oh);
31200                         w = Math.min(Math.max(mw, w), mxw);
31201                         h = oh * (w/ow);
31202                         break;
31203                     case "northeast":
31204                         w = ow * (h/oh);
31205                         w = Math.min(Math.max(mw, w), mxw);
31206                         h = oh * (w/ow);
31207                     break;
31208                     case "north":
31209                         var tw = w;
31210                         w = ow * (h/oh);
31211                         w = Math.min(Math.max(mw, w), mxw);
31212                         h = oh * (w/ow);
31213                         x += (tw - w) / 2;
31214                         break;
31215                     case "southwest":
31216                         h = oh * (w/ow);
31217                         h = Math.min(Math.max(mh, h), mxh);
31218                         var tw = w;
31219                         w = ow * (h/oh);
31220                         x += tw - w;
31221                         break;
31222                     case "west":
31223                         var th = h;
31224                         h = oh * (w/ow);
31225                         h = Math.min(Math.max(mh, h), mxh);
31226                         y += (th - h) / 2;
31227                         var tw = w;
31228                         w = ow * (h/oh);
31229                         x += tw - w;
31230                        break;
31231                     case "northwest":
31232                         var tw = w;
31233                         var th = h;
31234                         h = oh * (w/ow);
31235                         h = Math.min(Math.max(mh, h), mxh);
31236                         w = ow * (h/oh);
31237                         y += th - h;
31238                         x += tw - w;
31239                        break;
31240
31241                 }
31242             }
31243             if (pos == 'hdrag') {
31244                 w = ow;
31245             }
31246             this.proxy.setBounds(x, y, w, h);
31247             if(this.dynamic){
31248                 this.resizeElement();
31249             }
31250             }catch(e){}
31251         }
31252         this.fireEvent("resizing", this, x, y, w, h, e);
31253     },
31254
31255     // private
31256     handleOver : function(){
31257         if(this.enabled){
31258             this.el.addClass("x-resizable-over");
31259         }
31260     },
31261
31262     // private
31263     handleOut : function(){
31264         if(!this.resizing){
31265             this.el.removeClass("x-resizable-over");
31266         }
31267     },
31268
31269     /**
31270      * Returns the element this component is bound to.
31271      * @return {Roo.Element}
31272      */
31273     getEl : function(){
31274         return this.el;
31275     },
31276
31277     /**
31278      * Returns the resizeChild element (or null).
31279      * @return {Roo.Element}
31280      */
31281     getResizeChild : function(){
31282         return this.resizeChild;
31283     },
31284     groupHandler : function()
31285     {
31286         
31287     },
31288     /**
31289      * Destroys this resizable. If the element was wrapped and
31290      * removeEl is not true then the element remains.
31291      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31292      */
31293     destroy : function(removeEl){
31294         this.proxy.remove();
31295         if(this.overlay){
31296             this.overlay.removeAllListeners();
31297             this.overlay.remove();
31298         }
31299         var ps = Roo.Resizable.positions;
31300         for(var k in ps){
31301             if(typeof ps[k] != "function" && this[ps[k]]){
31302                 var h = this[ps[k]];
31303                 h.el.removeAllListeners();
31304                 h.el.remove();
31305             }
31306         }
31307         if(removeEl){
31308             this.el.update("");
31309             this.el.remove();
31310         }
31311     }
31312 });
31313
31314 // private
31315 // hash to map config positions to true positions
31316 Roo.Resizable.positions = {
31317     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31318     hd: "hdrag"
31319 };
31320
31321 // private
31322 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31323     if(!this.tpl){
31324         // only initialize the template if resizable is used
31325         var tpl = Roo.DomHelper.createTemplate(
31326             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31327         );
31328         tpl.compile();
31329         Roo.Resizable.Handle.prototype.tpl = tpl;
31330     }
31331     this.position = pos;
31332     this.rz = rz;
31333     // show north drag fro topdra
31334     var handlepos = pos == 'hdrag' ? 'north' : pos;
31335     
31336     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31337     if (pos == 'hdrag') {
31338         this.el.setStyle('cursor', 'pointer');
31339     }
31340     this.el.unselectable();
31341     if(transparent){
31342         this.el.setOpacity(0);
31343     }
31344     this.el.on("mousedown", this.onMouseDown, this);
31345     if(!disableTrackOver){
31346         this.el.on("mouseover", this.onMouseOver, this);
31347         this.el.on("mouseout", this.onMouseOut, this);
31348     }
31349 };
31350
31351 // private
31352 Roo.Resizable.Handle.prototype = {
31353     afterResize : function(rz){
31354         Roo.log('after?');
31355         // do nothing
31356     },
31357     // private
31358     onMouseDown : function(e){
31359         this.rz.onMouseDown(this, e);
31360     },
31361     // private
31362     onMouseOver : function(e){
31363         this.rz.handleOver(this, e);
31364     },
31365     // private
31366     onMouseOut : function(e){
31367         this.rz.handleOut(this, e);
31368     }
31369 };/*
31370  * Based on:
31371  * Ext JS Library 1.1.1
31372  * Copyright(c) 2006-2007, Ext JS, LLC.
31373  *
31374  * Originally Released Under LGPL - original licence link has changed is not relivant.
31375  *
31376  * Fork - LGPL
31377  * <script type="text/javascript">
31378  */
31379
31380 /**
31381  * @class Roo.Editor
31382  * @extends Roo.Component
31383  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31384  * @constructor
31385  * Create a new Editor
31386  * @param {Roo.form.Field} field The Field object (or descendant)
31387  * @param {Object} config The config object
31388  */
31389 Roo.Editor = function(field, config){
31390     Roo.Editor.superclass.constructor.call(this, config);
31391     this.field = field;
31392     this.addEvents({
31393         /**
31394              * @event beforestartedit
31395              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31396              * false from the handler of this event.
31397              * @param {Editor} this
31398              * @param {Roo.Element} boundEl The underlying element bound to this editor
31399              * @param {Mixed} value The field value being set
31400              */
31401         "beforestartedit" : true,
31402         /**
31403              * @event startedit
31404              * Fires when this editor is displayed
31405              * @param {Roo.Element} boundEl The underlying element bound to this editor
31406              * @param {Mixed} value The starting field value
31407              */
31408         "startedit" : true,
31409         /**
31410              * @event beforecomplete
31411              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31412              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31413              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31414              * event will not fire since no edit actually occurred.
31415              * @param {Editor} this
31416              * @param {Mixed} value The current field value
31417              * @param {Mixed} startValue The original field value
31418              */
31419         "beforecomplete" : true,
31420         /**
31421              * @event complete
31422              * Fires after editing is complete and any changed value has been written to the underlying field.
31423              * @param {Editor} this
31424              * @param {Mixed} value The current field value
31425              * @param {Mixed} startValue The original field value
31426              */
31427         "complete" : true,
31428         /**
31429          * @event specialkey
31430          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31431          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31432          * @param {Roo.form.Field} this
31433          * @param {Roo.EventObject} e The event object
31434          */
31435         "specialkey" : true
31436     });
31437 };
31438
31439 Roo.extend(Roo.Editor, Roo.Component, {
31440     /**
31441      * @cfg {Boolean/String} autosize
31442      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31443      * or "height" to adopt the height only (defaults to false)
31444      */
31445     /**
31446      * @cfg {Boolean} revertInvalid
31447      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31448      * validation fails (defaults to true)
31449      */
31450     /**
31451      * @cfg {Boolean} ignoreNoChange
31452      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31453      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31454      * will never be ignored.
31455      */
31456     /**
31457      * @cfg {Boolean} hideEl
31458      * False to keep the bound element visible while the editor is displayed (defaults to true)
31459      */
31460     /**
31461      * @cfg {Mixed} value
31462      * The data value of the underlying field (defaults to "")
31463      */
31464     value : "",
31465     /**
31466      * @cfg {String} alignment
31467      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31468      */
31469     alignment: "c-c?",
31470     /**
31471      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31472      * for bottom-right shadow (defaults to "frame")
31473      */
31474     shadow : "frame",
31475     /**
31476      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31477      */
31478     constrain : false,
31479     /**
31480      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31481      */
31482     completeOnEnter : false,
31483     /**
31484      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31485      */
31486     cancelOnEsc : false,
31487     /**
31488      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31489      */
31490     updateEl : false,
31491
31492     // private
31493     onRender : function(ct, position){
31494         this.el = new Roo.Layer({
31495             shadow: this.shadow,
31496             cls: "x-editor",
31497             parentEl : ct,
31498             shim : this.shim,
31499             shadowOffset:4,
31500             id: this.id,
31501             constrain: this.constrain
31502         });
31503         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31504         if(this.field.msgTarget != 'title'){
31505             this.field.msgTarget = 'qtip';
31506         }
31507         this.field.render(this.el);
31508         if(Roo.isGecko){
31509             this.field.el.dom.setAttribute('autocomplete', 'off');
31510         }
31511         this.field.on("specialkey", this.onSpecialKey, this);
31512         if(this.swallowKeys){
31513             this.field.el.swallowEvent(['keydown','keypress']);
31514         }
31515         this.field.show();
31516         this.field.on("blur", this.onBlur, this);
31517         if(this.field.grow){
31518             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31519         }
31520     },
31521
31522     onSpecialKey : function(field, e)
31523     {
31524         //Roo.log('editor onSpecialKey');
31525         if(this.completeOnEnter && e.getKey() == e.ENTER){
31526             e.stopEvent();
31527             this.completeEdit();
31528             return;
31529         }
31530         // do not fire special key otherwise it might hide close the editor...
31531         if(e.getKey() == e.ENTER){    
31532             return;
31533         }
31534         if(this.cancelOnEsc && e.getKey() == e.ESC){
31535             this.cancelEdit();
31536             return;
31537         } 
31538         this.fireEvent('specialkey', field, e);
31539     
31540     },
31541
31542     /**
31543      * Starts the editing process and shows the editor.
31544      * @param {String/HTMLElement/Element} el The element to edit
31545      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31546       * to the innerHTML of el.
31547      */
31548     startEdit : function(el, value){
31549         if(this.editing){
31550             this.completeEdit();
31551         }
31552         this.boundEl = Roo.get(el);
31553         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31554         if(!this.rendered){
31555             this.render(this.parentEl || document.body);
31556         }
31557         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31558             return;
31559         }
31560         this.startValue = v;
31561         this.field.setValue(v);
31562         if(this.autoSize){
31563             var sz = this.boundEl.getSize();
31564             switch(this.autoSize){
31565                 case "width":
31566                 this.setSize(sz.width,  "");
31567                 break;
31568                 case "height":
31569                 this.setSize("",  sz.height);
31570                 break;
31571                 default:
31572                 this.setSize(sz.width,  sz.height);
31573             }
31574         }
31575         this.el.alignTo(this.boundEl, this.alignment);
31576         this.editing = true;
31577         if(Roo.QuickTips){
31578             Roo.QuickTips.disable();
31579         }
31580         this.show();
31581     },
31582
31583     /**
31584      * Sets the height and width of this editor.
31585      * @param {Number} width The new width
31586      * @param {Number} height The new height
31587      */
31588     setSize : function(w, h){
31589         this.field.setSize(w, h);
31590         if(this.el){
31591             this.el.sync();
31592         }
31593     },
31594
31595     /**
31596      * Realigns the editor to the bound field based on the current alignment config value.
31597      */
31598     realign : function(){
31599         this.el.alignTo(this.boundEl, this.alignment);
31600     },
31601
31602     /**
31603      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31604      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31605      */
31606     completeEdit : function(remainVisible){
31607         if(!this.editing){
31608             return;
31609         }
31610         var v = this.getValue();
31611         if(this.revertInvalid !== false && !this.field.isValid()){
31612             v = this.startValue;
31613             this.cancelEdit(true);
31614         }
31615         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31616             this.editing = false;
31617             this.hide();
31618             return;
31619         }
31620         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31621             this.editing = false;
31622             if(this.updateEl && this.boundEl){
31623                 this.boundEl.update(v);
31624             }
31625             if(remainVisible !== true){
31626                 this.hide();
31627             }
31628             this.fireEvent("complete", this, v, this.startValue);
31629         }
31630     },
31631
31632     // private
31633     onShow : function(){
31634         this.el.show();
31635         if(this.hideEl !== false){
31636             this.boundEl.hide();
31637         }
31638         this.field.show();
31639         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31640             this.fixIEFocus = true;
31641             this.deferredFocus.defer(50, this);
31642         }else{
31643             this.field.focus();
31644         }
31645         this.fireEvent("startedit", this.boundEl, this.startValue);
31646     },
31647
31648     deferredFocus : function(){
31649         if(this.editing){
31650             this.field.focus();
31651         }
31652     },
31653
31654     /**
31655      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31656      * reverted to the original starting value.
31657      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31658      * cancel (defaults to false)
31659      */
31660     cancelEdit : function(remainVisible){
31661         if(this.editing){
31662             this.setValue(this.startValue);
31663             if(remainVisible !== true){
31664                 this.hide();
31665             }
31666         }
31667     },
31668
31669     // private
31670     onBlur : function(){
31671         if(this.allowBlur !== true && this.editing){
31672             this.completeEdit();
31673         }
31674     },
31675
31676     // private
31677     onHide : function(){
31678         if(this.editing){
31679             this.completeEdit();
31680             return;
31681         }
31682         this.field.blur();
31683         if(this.field.collapse){
31684             this.field.collapse();
31685         }
31686         this.el.hide();
31687         if(this.hideEl !== false){
31688             this.boundEl.show();
31689         }
31690         if(Roo.QuickTips){
31691             Roo.QuickTips.enable();
31692         }
31693     },
31694
31695     /**
31696      * Sets the data value of the editor
31697      * @param {Mixed} value Any valid value supported by the underlying field
31698      */
31699     setValue : function(v){
31700         this.field.setValue(v);
31701     },
31702
31703     /**
31704      * Gets the data value of the editor
31705      * @return {Mixed} The data value
31706      */
31707     getValue : function(){
31708         return this.field.getValue();
31709     }
31710 });/*
31711  * Based on:
31712  * Ext JS Library 1.1.1
31713  * Copyright(c) 2006-2007, Ext JS, LLC.
31714  *
31715  * Originally Released Under LGPL - original licence link has changed is not relivant.
31716  *
31717  * Fork - LGPL
31718  * <script type="text/javascript">
31719  */
31720  
31721 /**
31722  * @class Roo.BasicDialog
31723  * @extends Roo.util.Observable
31724  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31725  * <pre><code>
31726 var dlg = new Roo.BasicDialog("my-dlg", {
31727     height: 200,
31728     width: 300,
31729     minHeight: 100,
31730     minWidth: 150,
31731     modal: true,
31732     proxyDrag: true,
31733     shadow: true
31734 });
31735 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31736 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31737 dlg.addButton('Cancel', dlg.hide, dlg);
31738 dlg.show();
31739 </code></pre>
31740   <b>A Dialog should always be a direct child of the body element.</b>
31741  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31742  * @cfg {String} title Default text to display in the title bar (defaults to null)
31743  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31744  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31745  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31746  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31747  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31748  * (defaults to null with no animation)
31749  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31750  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31751  * property for valid values (defaults to 'all')
31752  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31753  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31754  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31755  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31756  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31757  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31758  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31759  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31760  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31761  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31762  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31763  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31764  * draggable = true (defaults to false)
31765  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31766  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31767  * shadow (defaults to false)
31768  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31769  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31770  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31771  * @cfg {Array} buttons Array of buttons
31772  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31773  * @constructor
31774  * Create a new BasicDialog.
31775  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31776  * @param {Object} config Configuration options
31777  */
31778 Roo.BasicDialog = function(el, config){
31779     this.el = Roo.get(el);
31780     var dh = Roo.DomHelper;
31781     if(!this.el && config && config.autoCreate){
31782         if(typeof config.autoCreate == "object"){
31783             if(!config.autoCreate.id){
31784                 config.autoCreate.id = el;
31785             }
31786             this.el = dh.append(document.body,
31787                         config.autoCreate, true);
31788         }else{
31789             this.el = dh.append(document.body,
31790                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31791         }
31792     }
31793     el = this.el;
31794     el.setDisplayed(true);
31795     el.hide = this.hideAction;
31796     this.id = el.id;
31797     el.addClass("x-dlg");
31798
31799     Roo.apply(this, config);
31800
31801     this.proxy = el.createProxy("x-dlg-proxy");
31802     this.proxy.hide = this.hideAction;
31803     this.proxy.setOpacity(.5);
31804     this.proxy.hide();
31805
31806     if(config.width){
31807         el.setWidth(config.width);
31808     }
31809     if(config.height){
31810         el.setHeight(config.height);
31811     }
31812     this.size = el.getSize();
31813     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31814         this.xy = [config.x,config.y];
31815     }else{
31816         this.xy = el.getCenterXY(true);
31817     }
31818     /** The header element @type Roo.Element */
31819     this.header = el.child("> .x-dlg-hd");
31820     /** The body element @type Roo.Element */
31821     this.body = el.child("> .x-dlg-bd");
31822     /** The footer element @type Roo.Element */
31823     this.footer = el.child("> .x-dlg-ft");
31824
31825     if(!this.header){
31826         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31827     }
31828     if(!this.body){
31829         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31830     }
31831
31832     this.header.unselectable();
31833     if(this.title){
31834         this.header.update(this.title);
31835     }
31836     // this element allows the dialog to be focused for keyboard event
31837     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31838     this.focusEl.swallowEvent("click", true);
31839
31840     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31841
31842     // wrap the body and footer for special rendering
31843     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31844     if(this.footer){
31845         this.bwrap.dom.appendChild(this.footer.dom);
31846     }
31847
31848     this.bg = this.el.createChild({
31849         tag: "div", cls:"x-dlg-bg",
31850         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31851     });
31852     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31853
31854
31855     if(this.autoScroll !== false && !this.autoTabs){
31856         this.body.setStyle("overflow", "auto");
31857     }
31858
31859     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31860
31861     if(this.closable !== false){
31862         this.el.addClass("x-dlg-closable");
31863         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31864         this.close.on("click", this.closeClick, this);
31865         this.close.addClassOnOver("x-dlg-close-over");
31866     }
31867     if(this.collapsible !== false){
31868         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31869         this.collapseBtn.on("click", this.collapseClick, this);
31870         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31871         this.header.on("dblclick", this.collapseClick, this);
31872     }
31873     if(this.resizable !== false){
31874         this.el.addClass("x-dlg-resizable");
31875         this.resizer = new Roo.Resizable(el, {
31876             minWidth: this.minWidth || 80,
31877             minHeight:this.minHeight || 80,
31878             handles: this.resizeHandles || "all",
31879             pinned: true
31880         });
31881         this.resizer.on("beforeresize", this.beforeResize, this);
31882         this.resizer.on("resize", this.onResize, this);
31883     }
31884     if(this.draggable !== false){
31885         el.addClass("x-dlg-draggable");
31886         if (!this.proxyDrag) {
31887             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31888         }
31889         else {
31890             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31891         }
31892         dd.setHandleElId(this.header.id);
31893         dd.endDrag = this.endMove.createDelegate(this);
31894         dd.startDrag = this.startMove.createDelegate(this);
31895         dd.onDrag = this.onDrag.createDelegate(this);
31896         dd.scroll = false;
31897         this.dd = dd;
31898     }
31899     if(this.modal){
31900         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31901         this.mask.enableDisplayMode("block");
31902         this.mask.hide();
31903         this.el.addClass("x-dlg-modal");
31904     }
31905     if(this.shadow){
31906         this.shadow = new Roo.Shadow({
31907             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31908             offset : this.shadowOffset
31909         });
31910     }else{
31911         this.shadowOffset = 0;
31912     }
31913     if(Roo.useShims && this.shim !== false){
31914         this.shim = this.el.createShim();
31915         this.shim.hide = this.hideAction;
31916         this.shim.hide();
31917     }else{
31918         this.shim = false;
31919     }
31920     if(this.autoTabs){
31921         this.initTabs();
31922     }
31923     if (this.buttons) { 
31924         var bts= this.buttons;
31925         this.buttons = [];
31926         Roo.each(bts, function(b) {
31927             this.addButton(b);
31928         }, this);
31929     }
31930     
31931     
31932     this.addEvents({
31933         /**
31934          * @event keydown
31935          * Fires when a key is pressed
31936          * @param {Roo.BasicDialog} this
31937          * @param {Roo.EventObject} e
31938          */
31939         "keydown" : true,
31940         /**
31941          * @event move
31942          * Fires when this dialog is moved by the user.
31943          * @param {Roo.BasicDialog} this
31944          * @param {Number} x The new page X
31945          * @param {Number} y The new page Y
31946          */
31947         "move" : true,
31948         /**
31949          * @event resize
31950          * Fires when this dialog is resized by the user.
31951          * @param {Roo.BasicDialog} this
31952          * @param {Number} width The new width
31953          * @param {Number} height The new height
31954          */
31955         "resize" : true,
31956         /**
31957          * @event beforehide
31958          * Fires before this dialog is hidden.
31959          * @param {Roo.BasicDialog} this
31960          */
31961         "beforehide" : true,
31962         /**
31963          * @event hide
31964          * Fires when this dialog is hidden.
31965          * @param {Roo.BasicDialog} this
31966          */
31967         "hide" : true,
31968         /**
31969          * @event beforeshow
31970          * Fires before this dialog is shown.
31971          * @param {Roo.BasicDialog} this
31972          */
31973         "beforeshow" : true,
31974         /**
31975          * @event show
31976          * Fires when this dialog is shown.
31977          * @param {Roo.BasicDialog} this
31978          */
31979         "show" : true
31980     });
31981     el.on("keydown", this.onKeyDown, this);
31982     el.on("mousedown", this.toFront, this);
31983     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31984     this.el.hide();
31985     Roo.DialogManager.register(this);
31986     Roo.BasicDialog.superclass.constructor.call(this);
31987 };
31988
31989 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31990     shadowOffset: Roo.isIE ? 6 : 5,
31991     minHeight: 80,
31992     minWidth: 200,
31993     minButtonWidth: 75,
31994     defaultButton: null,
31995     buttonAlign: "right",
31996     tabTag: 'div',
31997     firstShow: true,
31998
31999     /**
32000      * Sets the dialog title text
32001      * @param {String} text The title text to display
32002      * @return {Roo.BasicDialog} this
32003      */
32004     setTitle : function(text){
32005         this.header.update(text);
32006         return this;
32007     },
32008
32009     // private
32010     closeClick : function(){
32011         this.hide();
32012     },
32013
32014     // private
32015     collapseClick : function(){
32016         this[this.collapsed ? "expand" : "collapse"]();
32017     },
32018
32019     /**
32020      * Collapses the dialog to its minimized state (only the title bar is visible).
32021      * Equivalent to the user clicking the collapse dialog button.
32022      */
32023     collapse : function(){
32024         if(!this.collapsed){
32025             this.collapsed = true;
32026             this.el.addClass("x-dlg-collapsed");
32027             this.restoreHeight = this.el.getHeight();
32028             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32029         }
32030     },
32031
32032     /**
32033      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32034      * clicking the expand dialog button.
32035      */
32036     expand : function(){
32037         if(this.collapsed){
32038             this.collapsed = false;
32039             this.el.removeClass("x-dlg-collapsed");
32040             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32041         }
32042     },
32043
32044     /**
32045      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32046      * @return {Roo.TabPanel} The tabs component
32047      */
32048     initTabs : function(){
32049         var tabs = this.getTabs();
32050         while(tabs.getTab(0)){
32051             tabs.removeTab(0);
32052         }
32053         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32054             var dom = el.dom;
32055             tabs.addTab(Roo.id(dom), dom.title);
32056             dom.title = "";
32057         });
32058         tabs.activate(0);
32059         return tabs;
32060     },
32061
32062     // private
32063     beforeResize : function(){
32064         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32065     },
32066
32067     // private
32068     onResize : function(){
32069         this.refreshSize();
32070         this.syncBodyHeight();
32071         this.adjustAssets();
32072         this.focus();
32073         this.fireEvent("resize", this, this.size.width, this.size.height);
32074     },
32075
32076     // private
32077     onKeyDown : function(e){
32078         if(this.isVisible()){
32079             this.fireEvent("keydown", this, e);
32080         }
32081     },
32082
32083     /**
32084      * Resizes the dialog.
32085      * @param {Number} width
32086      * @param {Number} height
32087      * @return {Roo.BasicDialog} this
32088      */
32089     resizeTo : function(width, height){
32090         this.el.setSize(width, height);
32091         this.size = {width: width, height: height};
32092         this.syncBodyHeight();
32093         if(this.fixedcenter){
32094             this.center();
32095         }
32096         if(this.isVisible()){
32097             this.constrainXY();
32098             this.adjustAssets();
32099         }
32100         this.fireEvent("resize", this, width, height);
32101         return this;
32102     },
32103
32104
32105     /**
32106      * Resizes the dialog to fit the specified content size.
32107      * @param {Number} width
32108      * @param {Number} height
32109      * @return {Roo.BasicDialog} this
32110      */
32111     setContentSize : function(w, h){
32112         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32113         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32114         //if(!this.el.isBorderBox()){
32115             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32116             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32117         //}
32118         if(this.tabs){
32119             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32120             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32121         }
32122         this.resizeTo(w, h);
32123         return this;
32124     },
32125
32126     /**
32127      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32128      * executed in response to a particular key being pressed while the dialog is active.
32129      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32130      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32131      * @param {Function} fn The function to call
32132      * @param {Object} scope (optional) The scope of the function
32133      * @return {Roo.BasicDialog} this
32134      */
32135     addKeyListener : function(key, fn, scope){
32136         var keyCode, shift, ctrl, alt;
32137         if(typeof key == "object" && !(key instanceof Array)){
32138             keyCode = key["key"];
32139             shift = key["shift"];
32140             ctrl = key["ctrl"];
32141             alt = key["alt"];
32142         }else{
32143             keyCode = key;
32144         }
32145         var handler = function(dlg, e){
32146             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32147                 var k = e.getKey();
32148                 if(keyCode instanceof Array){
32149                     for(var i = 0, len = keyCode.length; i < len; i++){
32150                         if(keyCode[i] == k){
32151                           fn.call(scope || window, dlg, k, e);
32152                           return;
32153                         }
32154                     }
32155                 }else{
32156                     if(k == keyCode){
32157                         fn.call(scope || window, dlg, k, e);
32158                     }
32159                 }
32160             }
32161         };
32162         this.on("keydown", handler);
32163         return this;
32164     },
32165
32166     /**
32167      * Returns the TabPanel component (creates it if it doesn't exist).
32168      * Note: If you wish to simply check for the existence of tabs without creating them,
32169      * check for a null 'tabs' property.
32170      * @return {Roo.TabPanel} The tabs component
32171      */
32172     getTabs : function(){
32173         if(!this.tabs){
32174             this.el.addClass("x-dlg-auto-tabs");
32175             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32176             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32177         }
32178         return this.tabs;
32179     },
32180
32181     /**
32182      * Adds a button to the footer section of the dialog.
32183      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32184      * object or a valid Roo.DomHelper element config
32185      * @param {Function} handler The function called when the button is clicked
32186      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32187      * @return {Roo.Button} The new button
32188      */
32189     addButton : function(config, handler, scope){
32190         var dh = Roo.DomHelper;
32191         if(!this.footer){
32192             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32193         }
32194         if(!this.btnContainer){
32195             var tb = this.footer.createChild({
32196
32197                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32198                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32199             }, null, true);
32200             this.btnContainer = tb.firstChild.firstChild.firstChild;
32201         }
32202         var bconfig = {
32203             handler: handler,
32204             scope: scope,
32205             minWidth: this.minButtonWidth,
32206             hideParent:true
32207         };
32208         if(typeof config == "string"){
32209             bconfig.text = config;
32210         }else{
32211             if(config.tag){
32212                 bconfig.dhconfig = config;
32213             }else{
32214                 Roo.apply(bconfig, config);
32215             }
32216         }
32217         var fc = false;
32218         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32219             bconfig.position = Math.max(0, bconfig.position);
32220             fc = this.btnContainer.childNodes[bconfig.position];
32221         }
32222          
32223         var btn = new Roo.Button(
32224             fc ? 
32225                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32226                 : this.btnContainer.appendChild(document.createElement("td")),
32227             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32228             bconfig
32229         );
32230         this.syncBodyHeight();
32231         if(!this.buttons){
32232             /**
32233              * Array of all the buttons that have been added to this dialog via addButton
32234              * @type Array
32235              */
32236             this.buttons = [];
32237         }
32238         this.buttons.push(btn);
32239         return btn;
32240     },
32241
32242     /**
32243      * Sets the default button to be focused when the dialog is displayed.
32244      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32245      * @return {Roo.BasicDialog} this
32246      */
32247     setDefaultButton : function(btn){
32248         this.defaultButton = btn;
32249         return this;
32250     },
32251
32252     // private
32253     getHeaderFooterHeight : function(safe){
32254         var height = 0;
32255         if(this.header){
32256            height += this.header.getHeight();
32257         }
32258         if(this.footer){
32259            var fm = this.footer.getMargins();
32260             height += (this.footer.getHeight()+fm.top+fm.bottom);
32261         }
32262         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32263         height += this.centerBg.getPadding("tb");
32264         return height;
32265     },
32266
32267     // private
32268     syncBodyHeight : function()
32269     {
32270         var bd = this.body, // the text
32271             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32272             bw = this.bwrap;
32273         var height = this.size.height - this.getHeaderFooterHeight(false);
32274         bd.setHeight(height-bd.getMargins("tb"));
32275         var hh = this.header.getHeight();
32276         var h = this.size.height-hh;
32277         cb.setHeight(h);
32278         
32279         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32280         bw.setHeight(h-cb.getPadding("tb"));
32281         
32282         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32283         bd.setWidth(bw.getWidth(true));
32284         if(this.tabs){
32285             this.tabs.syncHeight();
32286             if(Roo.isIE){
32287                 this.tabs.el.repaint();
32288             }
32289         }
32290     },
32291
32292     /**
32293      * Restores the previous state of the dialog if Roo.state is configured.
32294      * @return {Roo.BasicDialog} this
32295      */
32296     restoreState : function(){
32297         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32298         if(box && box.width){
32299             this.xy = [box.x, box.y];
32300             this.resizeTo(box.width, box.height);
32301         }
32302         return this;
32303     },
32304
32305     // private
32306     beforeShow : function(){
32307         this.expand();
32308         if(this.fixedcenter){
32309             this.xy = this.el.getCenterXY(true);
32310         }
32311         if(this.modal){
32312             Roo.get(document.body).addClass("x-body-masked");
32313             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32314             this.mask.show();
32315         }
32316         this.constrainXY();
32317     },
32318
32319     // private
32320     animShow : function(){
32321         var b = Roo.get(this.animateTarget).getBox();
32322         this.proxy.setSize(b.width, b.height);
32323         this.proxy.setLocation(b.x, b.y);
32324         this.proxy.show();
32325         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32326                     true, .35, this.showEl.createDelegate(this));
32327     },
32328
32329     /**
32330      * Shows the dialog.
32331      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32332      * @return {Roo.BasicDialog} this
32333      */
32334     show : function(animateTarget){
32335         if (this.fireEvent("beforeshow", this) === false){
32336             return;
32337         }
32338         if(this.syncHeightBeforeShow){
32339             this.syncBodyHeight();
32340         }else if(this.firstShow){
32341             this.firstShow = false;
32342             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32343         }
32344         this.animateTarget = animateTarget || this.animateTarget;
32345         if(!this.el.isVisible()){
32346             this.beforeShow();
32347             if(this.animateTarget && Roo.get(this.animateTarget)){
32348                 this.animShow();
32349             }else{
32350                 this.showEl();
32351             }
32352         }
32353         return this;
32354     },
32355
32356     // private
32357     showEl : function(){
32358         this.proxy.hide();
32359         this.el.setXY(this.xy);
32360         this.el.show();
32361         this.adjustAssets(true);
32362         this.toFront();
32363         this.focus();
32364         // IE peekaboo bug - fix found by Dave Fenwick
32365         if(Roo.isIE){
32366             this.el.repaint();
32367         }
32368         this.fireEvent("show", this);
32369     },
32370
32371     /**
32372      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32373      * dialog itself will receive focus.
32374      */
32375     focus : function(){
32376         if(this.defaultButton){
32377             this.defaultButton.focus();
32378         }else{
32379             this.focusEl.focus();
32380         }
32381     },
32382
32383     // private
32384     constrainXY : function(){
32385         if(this.constraintoviewport !== false){
32386             if(!this.viewSize){
32387                 if(this.container){
32388                     var s = this.container.getSize();
32389                     this.viewSize = [s.width, s.height];
32390                 }else{
32391                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32392                 }
32393             }
32394             var s = Roo.get(this.container||document).getScroll();
32395
32396             var x = this.xy[0], y = this.xy[1];
32397             var w = this.size.width, h = this.size.height;
32398             var vw = this.viewSize[0], vh = this.viewSize[1];
32399             // only move it if it needs it
32400             var moved = false;
32401             // first validate right/bottom
32402             if(x + w > vw+s.left){
32403                 x = vw - w;
32404                 moved = true;
32405             }
32406             if(y + h > vh+s.top){
32407                 y = vh - h;
32408                 moved = true;
32409             }
32410             // then make sure top/left isn't negative
32411             if(x < s.left){
32412                 x = s.left;
32413                 moved = true;
32414             }
32415             if(y < s.top){
32416                 y = s.top;
32417                 moved = true;
32418             }
32419             if(moved){
32420                 // cache xy
32421                 this.xy = [x, y];
32422                 if(this.isVisible()){
32423                     this.el.setLocation(x, y);
32424                     this.adjustAssets();
32425                 }
32426             }
32427         }
32428     },
32429
32430     // private
32431     onDrag : function(){
32432         if(!this.proxyDrag){
32433             this.xy = this.el.getXY();
32434             this.adjustAssets();
32435         }
32436     },
32437
32438     // private
32439     adjustAssets : function(doShow){
32440         var x = this.xy[0], y = this.xy[1];
32441         var w = this.size.width, h = this.size.height;
32442         if(doShow === true){
32443             if(this.shadow){
32444                 this.shadow.show(this.el);
32445             }
32446             if(this.shim){
32447                 this.shim.show();
32448             }
32449         }
32450         if(this.shadow && this.shadow.isVisible()){
32451             this.shadow.show(this.el);
32452         }
32453         if(this.shim && this.shim.isVisible()){
32454             this.shim.setBounds(x, y, w, h);
32455         }
32456     },
32457
32458     // private
32459     adjustViewport : function(w, h){
32460         if(!w || !h){
32461             w = Roo.lib.Dom.getViewWidth();
32462             h = Roo.lib.Dom.getViewHeight();
32463         }
32464         // cache the size
32465         this.viewSize = [w, h];
32466         if(this.modal && this.mask.isVisible()){
32467             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32468             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32469         }
32470         if(this.isVisible()){
32471             this.constrainXY();
32472         }
32473     },
32474
32475     /**
32476      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32477      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32478      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32479      */
32480     destroy : function(removeEl){
32481         if(this.isVisible()){
32482             this.animateTarget = null;
32483             this.hide();
32484         }
32485         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32486         if(this.tabs){
32487             this.tabs.destroy(removeEl);
32488         }
32489         Roo.destroy(
32490              this.shim,
32491              this.proxy,
32492              this.resizer,
32493              this.close,
32494              this.mask
32495         );
32496         if(this.dd){
32497             this.dd.unreg();
32498         }
32499         if(this.buttons){
32500            for(var i = 0, len = this.buttons.length; i < len; i++){
32501                this.buttons[i].destroy();
32502            }
32503         }
32504         this.el.removeAllListeners();
32505         if(removeEl === true){
32506             this.el.update("");
32507             this.el.remove();
32508         }
32509         Roo.DialogManager.unregister(this);
32510     },
32511
32512     // private
32513     startMove : function(){
32514         if(this.proxyDrag){
32515             this.proxy.show();
32516         }
32517         if(this.constraintoviewport !== false){
32518             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32519         }
32520     },
32521
32522     // private
32523     endMove : function(){
32524         if(!this.proxyDrag){
32525             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32526         }else{
32527             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32528             this.proxy.hide();
32529         }
32530         this.refreshSize();
32531         this.adjustAssets();
32532         this.focus();
32533         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32534     },
32535
32536     /**
32537      * Brings this dialog to the front of any other visible dialogs
32538      * @return {Roo.BasicDialog} this
32539      */
32540     toFront : function(){
32541         Roo.DialogManager.bringToFront(this);
32542         return this;
32543     },
32544
32545     /**
32546      * Sends this dialog to the back (under) of any other visible dialogs
32547      * @return {Roo.BasicDialog} this
32548      */
32549     toBack : function(){
32550         Roo.DialogManager.sendToBack(this);
32551         return this;
32552     },
32553
32554     /**
32555      * Centers this dialog in the viewport
32556      * @return {Roo.BasicDialog} this
32557      */
32558     center : function(){
32559         var xy = this.el.getCenterXY(true);
32560         this.moveTo(xy[0], xy[1]);
32561         return this;
32562     },
32563
32564     /**
32565      * Moves the dialog's top-left corner to the specified point
32566      * @param {Number} x
32567      * @param {Number} y
32568      * @return {Roo.BasicDialog} this
32569      */
32570     moveTo : function(x, y){
32571         this.xy = [x,y];
32572         if(this.isVisible()){
32573             this.el.setXY(this.xy);
32574             this.adjustAssets();
32575         }
32576         return this;
32577     },
32578
32579     /**
32580      * Aligns the dialog to the specified element
32581      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32582      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32583      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32584      * @return {Roo.BasicDialog} this
32585      */
32586     alignTo : function(element, position, offsets){
32587         this.xy = this.el.getAlignToXY(element, position, offsets);
32588         if(this.isVisible()){
32589             this.el.setXY(this.xy);
32590             this.adjustAssets();
32591         }
32592         return this;
32593     },
32594
32595     /**
32596      * Anchors an element to another element and realigns it when the window is resized.
32597      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32598      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32599      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32600      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32601      * is a number, it is used as the buffer delay (defaults to 50ms).
32602      * @return {Roo.BasicDialog} this
32603      */
32604     anchorTo : function(el, alignment, offsets, monitorScroll){
32605         var action = function(){
32606             this.alignTo(el, alignment, offsets);
32607         };
32608         Roo.EventManager.onWindowResize(action, this);
32609         var tm = typeof monitorScroll;
32610         if(tm != 'undefined'){
32611             Roo.EventManager.on(window, 'scroll', action, this,
32612                 {buffer: tm == 'number' ? monitorScroll : 50});
32613         }
32614         action.call(this);
32615         return this;
32616     },
32617
32618     /**
32619      * Returns true if the dialog is visible
32620      * @return {Boolean}
32621      */
32622     isVisible : function(){
32623         return this.el.isVisible();
32624     },
32625
32626     // private
32627     animHide : function(callback){
32628         var b = Roo.get(this.animateTarget).getBox();
32629         this.proxy.show();
32630         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32631         this.el.hide();
32632         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32633                     this.hideEl.createDelegate(this, [callback]));
32634     },
32635
32636     /**
32637      * Hides the dialog.
32638      * @param {Function} callback (optional) Function to call when the dialog is hidden
32639      * @return {Roo.BasicDialog} this
32640      */
32641     hide : function(callback){
32642         if (this.fireEvent("beforehide", this) === false){
32643             return;
32644         }
32645         if(this.shadow){
32646             this.shadow.hide();
32647         }
32648         if(this.shim) {
32649           this.shim.hide();
32650         }
32651         // sometimes animateTarget seems to get set.. causing problems...
32652         // this just double checks..
32653         if(this.animateTarget && Roo.get(this.animateTarget)) {
32654            this.animHide(callback);
32655         }else{
32656             this.el.hide();
32657             this.hideEl(callback);
32658         }
32659         return this;
32660     },
32661
32662     // private
32663     hideEl : function(callback){
32664         this.proxy.hide();
32665         if(this.modal){
32666             this.mask.hide();
32667             Roo.get(document.body).removeClass("x-body-masked");
32668         }
32669         this.fireEvent("hide", this);
32670         if(typeof callback == "function"){
32671             callback();
32672         }
32673     },
32674
32675     // private
32676     hideAction : function(){
32677         this.setLeft("-10000px");
32678         this.setTop("-10000px");
32679         this.setStyle("visibility", "hidden");
32680     },
32681
32682     // private
32683     refreshSize : function(){
32684         this.size = this.el.getSize();
32685         this.xy = this.el.getXY();
32686         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32687     },
32688
32689     // private
32690     // z-index is managed by the DialogManager and may be overwritten at any time
32691     setZIndex : function(index){
32692         if(this.modal){
32693             this.mask.setStyle("z-index", index);
32694         }
32695         if(this.shim){
32696             this.shim.setStyle("z-index", ++index);
32697         }
32698         if(this.shadow){
32699             this.shadow.setZIndex(++index);
32700         }
32701         this.el.setStyle("z-index", ++index);
32702         if(this.proxy){
32703             this.proxy.setStyle("z-index", ++index);
32704         }
32705         if(this.resizer){
32706             this.resizer.proxy.setStyle("z-index", ++index);
32707         }
32708
32709         this.lastZIndex = index;
32710     },
32711
32712     /**
32713      * Returns the element for this dialog
32714      * @return {Roo.Element} The underlying dialog Element
32715      */
32716     getEl : function(){
32717         return this.el;
32718     }
32719 });
32720
32721 /**
32722  * @class Roo.DialogManager
32723  * Provides global access to BasicDialogs that have been created and
32724  * support for z-indexing (layering) multiple open dialogs.
32725  */
32726 Roo.DialogManager = function(){
32727     var list = {};
32728     var accessList = [];
32729     var front = null;
32730
32731     // private
32732     var sortDialogs = function(d1, d2){
32733         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32734     };
32735
32736     // private
32737     var orderDialogs = function(){
32738         accessList.sort(sortDialogs);
32739         var seed = Roo.DialogManager.zseed;
32740         for(var i = 0, len = accessList.length; i < len; i++){
32741             var dlg = accessList[i];
32742             if(dlg){
32743                 dlg.setZIndex(seed + (i*10));
32744             }
32745         }
32746     };
32747
32748     return {
32749         /**
32750          * The starting z-index for BasicDialogs (defaults to 9000)
32751          * @type Number The z-index value
32752          */
32753         zseed : 9000,
32754
32755         // private
32756         register : function(dlg){
32757             list[dlg.id] = dlg;
32758             accessList.push(dlg);
32759         },
32760
32761         // private
32762         unregister : function(dlg){
32763             delete list[dlg.id];
32764             var i=0;
32765             var len=0;
32766             if(!accessList.indexOf){
32767                 for(  i = 0, len = accessList.length; i < len; i++){
32768                     if(accessList[i] == dlg){
32769                         accessList.splice(i, 1);
32770                         return;
32771                     }
32772                 }
32773             }else{
32774                  i = accessList.indexOf(dlg);
32775                 if(i != -1){
32776                     accessList.splice(i, 1);
32777                 }
32778             }
32779         },
32780
32781         /**
32782          * Gets a registered dialog by id
32783          * @param {String/Object} id The id of the dialog or a dialog
32784          * @return {Roo.BasicDialog} this
32785          */
32786         get : function(id){
32787             return typeof id == "object" ? id : list[id];
32788         },
32789
32790         /**
32791          * Brings the specified dialog to the front
32792          * @param {String/Object} dlg The id of the dialog or a dialog
32793          * @return {Roo.BasicDialog} this
32794          */
32795         bringToFront : function(dlg){
32796             dlg = this.get(dlg);
32797             if(dlg != front){
32798                 front = dlg;
32799                 dlg._lastAccess = new Date().getTime();
32800                 orderDialogs();
32801             }
32802             return dlg;
32803         },
32804
32805         /**
32806          * Sends the specified dialog to the back
32807          * @param {String/Object} dlg The id of the dialog or a dialog
32808          * @return {Roo.BasicDialog} this
32809          */
32810         sendToBack : function(dlg){
32811             dlg = this.get(dlg);
32812             dlg._lastAccess = -(new Date().getTime());
32813             orderDialogs();
32814             return dlg;
32815         },
32816
32817         /**
32818          * Hides all dialogs
32819          */
32820         hideAll : function(){
32821             for(var id in list){
32822                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32823                     list[id].hide();
32824                 }
32825             }
32826         }
32827     };
32828 }();
32829
32830 /**
32831  * @class Roo.LayoutDialog
32832  * @extends Roo.BasicDialog
32833  * Dialog which provides adjustments for working with a layout in a Dialog.
32834  * Add your necessary layout config options to the dialog's config.<br>
32835  * Example usage (including a nested layout):
32836  * <pre><code>
32837 if(!dialog){
32838     dialog = new Roo.LayoutDialog("download-dlg", {
32839         modal: true,
32840         width:600,
32841         height:450,
32842         shadow:true,
32843         minWidth:500,
32844         minHeight:350,
32845         autoTabs:true,
32846         proxyDrag:true,
32847         // layout config merges with the dialog config
32848         center:{
32849             tabPosition: "top",
32850             alwaysShowTabs: true
32851         }
32852     });
32853     dialog.addKeyListener(27, dialog.hide, dialog);
32854     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32855     dialog.addButton("Build It!", this.getDownload, this);
32856
32857     // we can even add nested layouts
32858     var innerLayout = new Roo.BorderLayout("dl-inner", {
32859         east: {
32860             initialSize: 200,
32861             autoScroll:true,
32862             split:true
32863         },
32864         center: {
32865             autoScroll:true
32866         }
32867     });
32868     innerLayout.beginUpdate();
32869     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32870     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32871     innerLayout.endUpdate(true);
32872
32873     var layout = dialog.getLayout();
32874     layout.beginUpdate();
32875     layout.add("center", new Roo.ContentPanel("standard-panel",
32876                         {title: "Download the Source", fitToFrame:true}));
32877     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32878                {title: "Build your own roo.js"}));
32879     layout.getRegion("center").showPanel(sp);
32880     layout.endUpdate();
32881 }
32882 </code></pre>
32883     * @constructor
32884     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32885     * @param {Object} config configuration options
32886   */
32887 Roo.LayoutDialog = function(el, cfg){
32888     
32889     var config=  cfg;
32890     if (typeof(cfg) == 'undefined') {
32891         config = Roo.apply({}, el);
32892         // not sure why we use documentElement here.. - it should always be body.
32893         // IE7 borks horribly if we use documentElement.
32894         // webkit also does not like documentElement - it creates a body element...
32895         el = Roo.get( document.body || document.documentElement ).createChild();
32896         //config.autoCreate = true;
32897     }
32898     
32899     
32900     config.autoTabs = false;
32901     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32902     this.body.setStyle({overflow:"hidden", position:"relative"});
32903     this.layout = new Roo.BorderLayout(this.body.dom, config);
32904     this.layout.monitorWindowResize = false;
32905     this.el.addClass("x-dlg-auto-layout");
32906     // fix case when center region overwrites center function
32907     this.center = Roo.BasicDialog.prototype.center;
32908     this.on("show", this.layout.layout, this.layout, true);
32909     if (config.items) {
32910         var xitems = config.items;
32911         delete config.items;
32912         Roo.each(xitems, this.addxtype, this);
32913     }
32914     
32915     
32916 };
32917 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32918     /**
32919      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32920      * @deprecated
32921      */
32922     endUpdate : function(){
32923         this.layout.endUpdate();
32924     },
32925
32926     /**
32927      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32928      *  @deprecated
32929      */
32930     beginUpdate : function(){
32931         this.layout.beginUpdate();
32932     },
32933
32934     /**
32935      * Get the BorderLayout for this dialog
32936      * @return {Roo.BorderLayout}
32937      */
32938     getLayout : function(){
32939         return this.layout;
32940     },
32941
32942     showEl : function(){
32943         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32944         if(Roo.isIE7){
32945             this.layout.layout();
32946         }
32947     },
32948
32949     // private
32950     // Use the syncHeightBeforeShow config option to control this automatically
32951     syncBodyHeight : function(){
32952         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32953         if(this.layout){this.layout.layout();}
32954     },
32955     
32956       /**
32957      * Add an xtype element (actually adds to the layout.)
32958      * @return {Object} xdata xtype object data.
32959      */
32960     
32961     addxtype : function(c) {
32962         return this.layout.addxtype(c);
32963     }
32964 });/*
32965  * Based on:
32966  * Ext JS Library 1.1.1
32967  * Copyright(c) 2006-2007, Ext JS, LLC.
32968  *
32969  * Originally Released Under LGPL - original licence link has changed is not relivant.
32970  *
32971  * Fork - LGPL
32972  * <script type="text/javascript">
32973  */
32974  
32975 /**
32976  * @class Roo.MessageBox
32977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32978  * Example usage:
32979  *<pre><code>
32980 // Basic alert:
32981 Roo.Msg.alert('Status', 'Changes saved successfully.');
32982
32983 // Prompt for user data:
32984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32985     if (btn == 'ok'){
32986         // process text value...
32987     }
32988 });
32989
32990 // Show a dialog using config options:
32991 Roo.Msg.show({
32992    title:'Save Changes?',
32993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32994    buttons: Roo.Msg.YESNOCANCEL,
32995    fn: processResult,
32996    animEl: 'elId'
32997 });
32998 </code></pre>
32999  * @singleton
33000  */
33001 Roo.MessageBox = function(){
33002     var dlg, opt, mask, waitTimer;
33003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33004     var buttons, activeTextEl, bwidth;
33005
33006     // private
33007     var handleButton = function(button){
33008         dlg.hide();
33009         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33010     };
33011
33012     // private
33013     var handleHide = function(){
33014         if(opt && opt.cls){
33015             dlg.el.removeClass(opt.cls);
33016         }
33017         if(waitTimer){
33018             Roo.TaskMgr.stop(waitTimer);
33019             waitTimer = null;
33020         }
33021     };
33022
33023     // private
33024     var updateButtons = function(b){
33025         var width = 0;
33026         if(!b){
33027             buttons["ok"].hide();
33028             buttons["cancel"].hide();
33029             buttons["yes"].hide();
33030             buttons["no"].hide();
33031             dlg.footer.dom.style.display = 'none';
33032             return width;
33033         }
33034         dlg.footer.dom.style.display = '';
33035         for(var k in buttons){
33036             if(typeof buttons[k] != "function"){
33037                 if(b[k]){
33038                     buttons[k].show();
33039                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33040                     width += buttons[k].el.getWidth()+15;
33041                 }else{
33042                     buttons[k].hide();
33043                 }
33044             }
33045         }
33046         return width;
33047     };
33048
33049     // private
33050     var handleEsc = function(d, k, e){
33051         if(opt && opt.closable !== false){
33052             dlg.hide();
33053         }
33054         if(e){
33055             e.stopEvent();
33056         }
33057     };
33058
33059     return {
33060         /**
33061          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33062          * @return {Roo.BasicDialog} The BasicDialog element
33063          */
33064         getDialog : function(){
33065            if(!dlg){
33066                 dlg = new Roo.BasicDialog("x-msg-box", {
33067                     autoCreate : true,
33068                     shadow: true,
33069                     draggable: true,
33070                     resizable:false,
33071                     constraintoviewport:false,
33072                     fixedcenter:true,
33073                     collapsible : false,
33074                     shim:true,
33075                     modal: true,
33076                     width:400, height:100,
33077                     buttonAlign:"center",
33078                     closeClick : function(){
33079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33080                             handleButton("no");
33081                         }else{
33082                             handleButton("cancel");
33083                         }
33084                     }
33085                 });
33086                 dlg.on("hide", handleHide);
33087                 mask = dlg.mask;
33088                 dlg.addKeyListener(27, handleEsc);
33089                 buttons = {};
33090                 var bt = this.buttonText;
33091                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33092                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33093                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33094                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33095                 bodyEl = dlg.body.createChild({
33096
33097                     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>'
33098                 });
33099                 msgEl = bodyEl.dom.firstChild;
33100                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33101                 textboxEl.enableDisplayMode();
33102                 textboxEl.addKeyListener([10,13], function(){
33103                     if(dlg.isVisible() && opt && opt.buttons){
33104                         if(opt.buttons.ok){
33105                             handleButton("ok");
33106                         }else if(opt.buttons.yes){
33107                             handleButton("yes");
33108                         }
33109                     }
33110                 });
33111                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33112                 textareaEl.enableDisplayMode();
33113                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33114                 progressEl.enableDisplayMode();
33115                 var pf = progressEl.dom.firstChild;
33116                 if (pf) {
33117                     pp = Roo.get(pf.firstChild);
33118                     pp.setHeight(pf.offsetHeight);
33119                 }
33120                 
33121             }
33122             return dlg;
33123         },
33124
33125         /**
33126          * Updates the message box body text
33127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33128          * the XHTML-compliant non-breaking space character '&amp;#160;')
33129          * @return {Roo.MessageBox} This message box
33130          */
33131         updateText : function(text){
33132             if(!dlg.isVisible() && !opt.width){
33133                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33134             }
33135             msgEl.innerHTML = text || '&#160;';
33136       
33137             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33138             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33139             var w = Math.max(
33140                     Math.min(opt.width || cw , this.maxWidth), 
33141                     Math.max(opt.minWidth || this.minWidth, bwidth)
33142             );
33143             if(opt.prompt){
33144                 activeTextEl.setWidth(w);
33145             }
33146             if(dlg.isVisible()){
33147                 dlg.fixedcenter = false;
33148             }
33149             // to big, make it scroll. = But as usual stupid IE does not support
33150             // !important..
33151             
33152             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33153                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33154                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33155             } else {
33156                 bodyEl.dom.style.height = '';
33157                 bodyEl.dom.style.overflowY = '';
33158             }
33159             if (cw > w) {
33160                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33161             } else {
33162                 bodyEl.dom.style.overflowX = '';
33163             }
33164             
33165             dlg.setContentSize(w, bodyEl.getHeight());
33166             if(dlg.isVisible()){
33167                 dlg.fixedcenter = true;
33168             }
33169             return this;
33170         },
33171
33172         /**
33173          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33174          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33175          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33176          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33177          * @return {Roo.MessageBox} This message box
33178          */
33179         updateProgress : function(value, text){
33180             if(text){
33181                 this.updateText(text);
33182             }
33183             if (pp) { // weird bug on my firefox - for some reason this is not defined
33184                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33185             }
33186             return this;
33187         },        
33188
33189         /**
33190          * Returns true if the message box is currently displayed
33191          * @return {Boolean} True if the message box is visible, else false
33192          */
33193         isVisible : function(){
33194             return dlg && dlg.isVisible();  
33195         },
33196
33197         /**
33198          * Hides the message box if it is displayed
33199          */
33200         hide : function(){
33201             if(this.isVisible()){
33202                 dlg.hide();
33203             }  
33204         },
33205
33206         /**
33207          * Displays a new message box, or reinitializes an existing message box, based on the config options
33208          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33209          * The following config object properties are supported:
33210          * <pre>
33211 Property    Type             Description
33212 ----------  ---------------  ------------------------------------------------------------------------------------
33213 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33214                                    closes (defaults to undefined)
33215 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33216                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33217 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33218                                    progress and wait dialogs will ignore this property and always hide the
33219                                    close button as they can only be closed programmatically.
33220 cls               String           A custom CSS class to apply to the message box element
33221 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33222                                    displayed (defaults to 75)
33223 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33224                                    function will be btn (the name of the button that was clicked, if applicable,
33225                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33226                                    Progress and wait dialogs will ignore this option since they do not respond to
33227                                    user actions and can only be closed programmatically, so any required function
33228                                    should be called by the same code after it closes the dialog.
33229 icon              String           A CSS class that provides a background image to be used as an icon for
33230                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33231 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33232 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33233 modal             Boolean          False to allow user interaction with the page while the message box is
33234                                    displayed (defaults to true)
33235 msg               String           A string that will replace the existing message box body text (defaults
33236                                    to the XHTML-compliant non-breaking space character '&#160;')
33237 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33238 progress          Boolean          True to display a progress bar (defaults to false)
33239 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33240 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33241 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33242 title             String           The title text
33243 value             String           The string value to set into the active textbox element if displayed
33244 wait              Boolean          True to display a progress bar (defaults to false)
33245 width             Number           The width of the dialog in pixels
33246 </pre>
33247          *
33248          * Example usage:
33249          * <pre><code>
33250 Roo.Msg.show({
33251    title: 'Address',
33252    msg: 'Please enter your address:',
33253    width: 300,
33254    buttons: Roo.MessageBox.OKCANCEL,
33255    multiline: true,
33256    fn: saveAddress,
33257    animEl: 'addAddressBtn'
33258 });
33259 </code></pre>
33260          * @param {Object} config Configuration options
33261          * @return {Roo.MessageBox} This message box
33262          */
33263         show : function(options)
33264         {
33265             
33266             // this causes nightmares if you show one dialog after another
33267             // especially on callbacks..
33268              
33269             if(this.isVisible()){
33270                 
33271                 this.hide();
33272                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33273                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33274                 Roo.log("New Dialog Message:" +  options.msg )
33275                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33276                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33277                 
33278             }
33279             var d = this.getDialog();
33280             opt = options;
33281             d.setTitle(opt.title || "&#160;");
33282             d.close.setDisplayed(opt.closable !== false);
33283             activeTextEl = textboxEl;
33284             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33285             if(opt.prompt){
33286                 if(opt.multiline){
33287                     textboxEl.hide();
33288                     textareaEl.show();
33289                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33290                         opt.multiline : this.defaultTextHeight);
33291                     activeTextEl = textareaEl;
33292                 }else{
33293                     textboxEl.show();
33294                     textareaEl.hide();
33295                 }
33296             }else{
33297                 textboxEl.hide();
33298                 textareaEl.hide();
33299             }
33300             progressEl.setDisplayed(opt.progress === true);
33301             this.updateProgress(0);
33302             activeTextEl.dom.value = opt.value || "";
33303             if(opt.prompt){
33304                 dlg.setDefaultButton(activeTextEl);
33305             }else{
33306                 var bs = opt.buttons;
33307                 var db = null;
33308                 if(bs && bs.ok){
33309                     db = buttons["ok"];
33310                 }else if(bs && bs.yes){
33311                     db = buttons["yes"];
33312                 }
33313                 dlg.setDefaultButton(db);
33314             }
33315             bwidth = updateButtons(opt.buttons);
33316             this.updateText(opt.msg);
33317             if(opt.cls){
33318                 d.el.addClass(opt.cls);
33319             }
33320             d.proxyDrag = opt.proxyDrag === true;
33321             d.modal = opt.modal !== false;
33322             d.mask = opt.modal !== false ? mask : false;
33323             if(!d.isVisible()){
33324                 // force it to the end of the z-index stack so it gets a cursor in FF
33325                 document.body.appendChild(dlg.el.dom);
33326                 d.animateTarget = null;
33327                 d.show(options.animEl);
33328             }
33329             return this;
33330         },
33331
33332         /**
33333          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33334          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33335          * and closing the message box when the process is complete.
33336          * @param {String} title The title bar text
33337          * @param {String} msg The message box body text
33338          * @return {Roo.MessageBox} This message box
33339          */
33340         progress : function(title, msg){
33341             this.show({
33342                 title : title,
33343                 msg : msg,
33344                 buttons: false,
33345                 progress:true,
33346                 closable:false,
33347                 minWidth: this.minProgressWidth,
33348                 modal : true
33349             });
33350             return this;
33351         },
33352
33353         /**
33354          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33355          * If a callback function is passed it will be called after the user clicks the button, and the
33356          * id of the button that was clicked will be passed as the only parameter to the callback
33357          * (could also be the top-right close button).
33358          * @param {String} title The title bar text
33359          * @param {String} msg The message box body text
33360          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33361          * @param {Object} scope (optional) The scope of the callback function
33362          * @return {Roo.MessageBox} This message box
33363          */
33364         alert : function(title, msg, fn, scope){
33365             this.show({
33366                 title : title,
33367                 msg : msg,
33368                 buttons: this.OK,
33369                 fn: fn,
33370                 scope : scope,
33371                 modal : true
33372             });
33373             return this;
33374         },
33375
33376         /**
33377          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33378          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33379          * You are responsible for closing the message box when the process is complete.
33380          * @param {String} msg The message box body text
33381          * @param {String} title (optional) The title bar text
33382          * @return {Roo.MessageBox} This message box
33383          */
33384         wait : function(msg, title){
33385             this.show({
33386                 title : title,
33387                 msg : msg,
33388                 buttons: false,
33389                 closable:false,
33390                 progress:true,
33391                 modal:true,
33392                 width:300,
33393                 wait:true
33394             });
33395             waitTimer = Roo.TaskMgr.start({
33396                 run: function(i){
33397                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33398                 },
33399                 interval: 1000
33400             });
33401             return this;
33402         },
33403
33404         /**
33405          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33406          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33407          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33408          * @param {String} title The title bar text
33409          * @param {String} msg The message box body text
33410          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33411          * @param {Object} scope (optional) The scope of the callback function
33412          * @return {Roo.MessageBox} This message box
33413          */
33414         confirm : function(title, msg, fn, scope){
33415             this.show({
33416                 title : title,
33417                 msg : msg,
33418                 buttons: this.YESNO,
33419                 fn: fn,
33420                 scope : scope,
33421                 modal : true
33422             });
33423             return this;
33424         },
33425
33426         /**
33427          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33428          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33429          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33430          * (could also be the top-right close button) and the text that was entered will be passed as the two
33431          * parameters to the callback.
33432          * @param {String} title The title bar text
33433          * @param {String} msg The message box body text
33434          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33435          * @param {Object} scope (optional) The scope of the callback function
33436          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33437          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33438          * @return {Roo.MessageBox} This message box
33439          */
33440         prompt : function(title, msg, fn, scope, multiline){
33441             this.show({
33442                 title : title,
33443                 msg : msg,
33444                 buttons: this.OKCANCEL,
33445                 fn: fn,
33446                 minWidth:250,
33447                 scope : scope,
33448                 prompt:true,
33449                 multiline: multiline,
33450                 modal : true
33451             });
33452             return this;
33453         },
33454
33455         /**
33456          * Button config that displays a single OK button
33457          * @type Object
33458          */
33459         OK : {ok:true},
33460         /**
33461          * Button config that displays Yes and No buttons
33462          * @type Object
33463          */
33464         YESNO : {yes:true, no:true},
33465         /**
33466          * Button config that displays OK and Cancel buttons
33467          * @type Object
33468          */
33469         OKCANCEL : {ok:true, cancel:true},
33470         /**
33471          * Button config that displays Yes, No and Cancel buttons
33472          * @type Object
33473          */
33474         YESNOCANCEL : {yes:true, no:true, cancel:true},
33475
33476         /**
33477          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33478          * @type Number
33479          */
33480         defaultTextHeight : 75,
33481         /**
33482          * The maximum width in pixels of the message box (defaults to 600)
33483          * @type Number
33484          */
33485         maxWidth : 600,
33486         /**
33487          * The minimum width in pixels of the message box (defaults to 100)
33488          * @type Number
33489          */
33490         minWidth : 100,
33491         /**
33492          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33493          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33494          * @type Number
33495          */
33496         minProgressWidth : 250,
33497         /**
33498          * An object containing the default button text strings that can be overriden for localized language support.
33499          * Supported properties are: ok, cancel, yes and no.
33500          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33501          * @type Object
33502          */
33503         buttonText : {
33504             ok : "OK",
33505             cancel : "Cancel",
33506             yes : "Yes",
33507             no : "No"
33508         }
33509     };
33510 }();
33511
33512 /**
33513  * Shorthand for {@link Roo.MessageBox}
33514  */
33515 Roo.Msg = Roo.MessageBox;/*
33516  * Based on:
33517  * Ext JS Library 1.1.1
33518  * Copyright(c) 2006-2007, Ext JS, LLC.
33519  *
33520  * Originally Released Under LGPL - original licence link has changed is not relivant.
33521  *
33522  * Fork - LGPL
33523  * <script type="text/javascript">
33524  */
33525 /**
33526  * @class Roo.QuickTips
33527  * Provides attractive and customizable tooltips for any element.
33528  * @singleton
33529  */
33530 Roo.QuickTips = function(){
33531     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33532     var ce, bd, xy, dd;
33533     var visible = false, disabled = true, inited = false;
33534     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33535     
33536     var onOver = function(e){
33537         if(disabled){
33538             return;
33539         }
33540         var t = e.getTarget();
33541         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33542             return;
33543         }
33544         if(ce && t == ce.el){
33545             clearTimeout(hideProc);
33546             return;
33547         }
33548         if(t && tagEls[t.id]){
33549             tagEls[t.id].el = t;
33550             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33551             return;
33552         }
33553         var ttp, et = Roo.fly(t);
33554         var ns = cfg.namespace;
33555         if(tm.interceptTitles && t.title){
33556             ttp = t.title;
33557             t.qtip = ttp;
33558             t.removeAttribute("title");
33559             e.preventDefault();
33560         }else{
33561             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33562         }
33563         if(ttp){
33564             showProc = show.defer(tm.showDelay, tm, [{
33565                 el: t, 
33566                 text: ttp, 
33567                 width: et.getAttributeNS(ns, cfg.width),
33568                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33569                 title: et.getAttributeNS(ns, cfg.title),
33570                     cls: et.getAttributeNS(ns, cfg.cls)
33571             }]);
33572         }
33573     };
33574     
33575     var onOut = function(e){
33576         clearTimeout(showProc);
33577         var t = e.getTarget();
33578         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33579             hideProc = setTimeout(hide, tm.hideDelay);
33580         }
33581     };
33582     
33583     var onMove = function(e){
33584         if(disabled){
33585             return;
33586         }
33587         xy = e.getXY();
33588         xy[1] += 18;
33589         if(tm.trackMouse && ce){
33590             el.setXY(xy);
33591         }
33592     };
33593     
33594     var onDown = function(e){
33595         clearTimeout(showProc);
33596         clearTimeout(hideProc);
33597         if(!e.within(el)){
33598             if(tm.hideOnClick){
33599                 hide();
33600                 tm.disable();
33601                 tm.enable.defer(100, tm);
33602             }
33603         }
33604     };
33605     
33606     var getPad = function(){
33607         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33608     };
33609
33610     var show = function(o){
33611         if(disabled){
33612             return;
33613         }
33614         clearTimeout(dismissProc);
33615         ce = o;
33616         if(removeCls){ // in case manually hidden
33617             el.removeClass(removeCls);
33618             removeCls = null;
33619         }
33620         if(ce.cls){
33621             el.addClass(ce.cls);
33622             removeCls = ce.cls;
33623         }
33624         if(ce.title){
33625             tipTitle.update(ce.title);
33626             tipTitle.show();
33627         }else{
33628             tipTitle.update('');
33629             tipTitle.hide();
33630         }
33631         el.dom.style.width  = tm.maxWidth+'px';
33632         //tipBody.dom.style.width = '';
33633         tipBodyText.update(o.text);
33634         var p = getPad(), w = ce.width;
33635         if(!w){
33636             var td = tipBodyText.dom;
33637             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33638             if(aw > tm.maxWidth){
33639                 w = tm.maxWidth;
33640             }else if(aw < tm.minWidth){
33641                 w = tm.minWidth;
33642             }else{
33643                 w = aw;
33644             }
33645         }
33646         //tipBody.setWidth(w);
33647         el.setWidth(parseInt(w, 10) + p);
33648         if(ce.autoHide === false){
33649             close.setDisplayed(true);
33650             if(dd){
33651                 dd.unlock();
33652             }
33653         }else{
33654             close.setDisplayed(false);
33655             if(dd){
33656                 dd.lock();
33657             }
33658         }
33659         if(xy){
33660             el.avoidY = xy[1]-18;
33661             el.setXY(xy);
33662         }
33663         if(tm.animate){
33664             el.setOpacity(.1);
33665             el.setStyle("visibility", "visible");
33666             el.fadeIn({callback: afterShow});
33667         }else{
33668             afterShow();
33669         }
33670     };
33671     
33672     var afterShow = function(){
33673         if(ce){
33674             el.show();
33675             esc.enable();
33676             if(tm.autoDismiss && ce.autoHide !== false){
33677                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33678             }
33679         }
33680     };
33681     
33682     var hide = function(noanim){
33683         clearTimeout(dismissProc);
33684         clearTimeout(hideProc);
33685         ce = null;
33686         if(el.isVisible()){
33687             esc.disable();
33688             if(noanim !== true && tm.animate){
33689                 el.fadeOut({callback: afterHide});
33690             }else{
33691                 afterHide();
33692             } 
33693         }
33694     };
33695     
33696     var afterHide = function(){
33697         el.hide();
33698         if(removeCls){
33699             el.removeClass(removeCls);
33700             removeCls = null;
33701         }
33702     };
33703     
33704     return {
33705         /**
33706         * @cfg {Number} minWidth
33707         * The minimum width of the quick tip (defaults to 40)
33708         */
33709        minWidth : 40,
33710         /**
33711         * @cfg {Number} maxWidth
33712         * The maximum width of the quick tip (defaults to 300)
33713         */
33714        maxWidth : 300,
33715         /**
33716         * @cfg {Boolean} interceptTitles
33717         * True to automatically use the element's DOM title value if available (defaults to false)
33718         */
33719        interceptTitles : false,
33720         /**
33721         * @cfg {Boolean} trackMouse
33722         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33723         */
33724        trackMouse : false,
33725         /**
33726         * @cfg {Boolean} hideOnClick
33727         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33728         */
33729        hideOnClick : true,
33730         /**
33731         * @cfg {Number} showDelay
33732         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33733         */
33734        showDelay : 500,
33735         /**
33736         * @cfg {Number} hideDelay
33737         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33738         */
33739        hideDelay : 200,
33740         /**
33741         * @cfg {Boolean} autoHide
33742         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33743         * Used in conjunction with hideDelay.
33744         */
33745        autoHide : true,
33746         /**
33747         * @cfg {Boolean}
33748         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33749         * (defaults to true).  Used in conjunction with autoDismissDelay.
33750         */
33751        autoDismiss : true,
33752         /**
33753         * @cfg {Number}
33754         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33755         */
33756        autoDismissDelay : 5000,
33757        /**
33758         * @cfg {Boolean} animate
33759         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33760         */
33761        animate : false,
33762
33763        /**
33764         * @cfg {String} title
33765         * Title text to display (defaults to '').  This can be any valid HTML markup.
33766         */
33767         title: '',
33768        /**
33769         * @cfg {String} text
33770         * Body text to display (defaults to '').  This can be any valid HTML markup.
33771         */
33772         text : '',
33773        /**
33774         * @cfg {String} cls
33775         * A CSS class to apply to the base quick tip element (defaults to '').
33776         */
33777         cls : '',
33778        /**
33779         * @cfg {Number} width
33780         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33781         * minWidth or maxWidth.
33782         */
33783         width : null,
33784
33785     /**
33786      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33787      * or display QuickTips in a page.
33788      */
33789        init : function(){
33790           tm = Roo.QuickTips;
33791           cfg = tm.tagConfig;
33792           if(!inited){
33793               if(!Roo.isReady){ // allow calling of init() before onReady
33794                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33795                   return;
33796               }
33797               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33798               el.fxDefaults = {stopFx: true};
33799               // maximum custom styling
33800               //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>');
33801               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>');              
33802               tipTitle = el.child('h3');
33803               tipTitle.enableDisplayMode("block");
33804               tipBody = el.child('div.x-tip-bd');
33805               tipBodyText = el.child('div.x-tip-bd-inner');
33806               //bdLeft = el.child('div.x-tip-bd-left');
33807               //bdRight = el.child('div.x-tip-bd-right');
33808               close = el.child('div.x-tip-close');
33809               close.enableDisplayMode("block");
33810               close.on("click", hide);
33811               var d = Roo.get(document);
33812               d.on("mousedown", onDown);
33813               d.on("mouseover", onOver);
33814               d.on("mouseout", onOut);
33815               d.on("mousemove", onMove);
33816               esc = d.addKeyListener(27, hide);
33817               esc.disable();
33818               if(Roo.dd.DD){
33819                   dd = el.initDD("default", null, {
33820                       onDrag : function(){
33821                           el.sync();  
33822                       }
33823                   });
33824                   dd.setHandleElId(tipTitle.id);
33825                   dd.lock();
33826               }
33827               inited = true;
33828           }
33829           this.enable(); 
33830        },
33831
33832     /**
33833      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33834      * are supported:
33835      * <pre>
33836 Property    Type                   Description
33837 ----------  ---------------------  ------------------------------------------------------------------------
33838 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33839      * </ul>
33840      * @param {Object} config The config object
33841      */
33842        register : function(config){
33843            var cs = config instanceof Array ? config : arguments;
33844            for(var i = 0, len = cs.length; i < len; i++) {
33845                var c = cs[i];
33846                var target = c.target;
33847                if(target){
33848                    if(target instanceof Array){
33849                        for(var j = 0, jlen = target.length; j < jlen; j++){
33850                            tagEls[target[j]] = c;
33851                        }
33852                    }else{
33853                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33854                    }
33855                }
33856            }
33857        },
33858
33859     /**
33860      * Removes this quick tip from its element and destroys it.
33861      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33862      */
33863        unregister : function(el){
33864            delete tagEls[Roo.id(el)];
33865        },
33866
33867     /**
33868      * Enable this quick tip.
33869      */
33870        enable : function(){
33871            if(inited && disabled){
33872                locks.pop();
33873                if(locks.length < 1){
33874                    disabled = false;
33875                }
33876            }
33877        },
33878
33879     /**
33880      * Disable this quick tip.
33881      */
33882        disable : function(){
33883           disabled = true;
33884           clearTimeout(showProc);
33885           clearTimeout(hideProc);
33886           clearTimeout(dismissProc);
33887           if(ce){
33888               hide(true);
33889           }
33890           locks.push(1);
33891        },
33892
33893     /**
33894      * Returns true if the quick tip is enabled, else false.
33895      */
33896        isEnabled : function(){
33897             return !disabled;
33898        },
33899
33900         // private
33901        tagConfig : {
33902            namespace : "roo", // was ext?? this may break..
33903            alt_namespace : "ext",
33904            attribute : "qtip",
33905            width : "width",
33906            target : "target",
33907            title : "qtitle",
33908            hide : "hide",
33909            cls : "qclass"
33910        }
33911    };
33912 }();
33913
33914 // backwards compat
33915 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33916  * Based on:
33917  * Ext JS Library 1.1.1
33918  * Copyright(c) 2006-2007, Ext JS, LLC.
33919  *
33920  * Originally Released Under LGPL - original licence link has changed is not relivant.
33921  *
33922  * Fork - LGPL
33923  * <script type="text/javascript">
33924  */
33925  
33926
33927 /**
33928  * @class Roo.tree.TreePanel
33929  * @extends Roo.data.Tree
33930
33931  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33932  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33933  * @cfg {Boolean} enableDD true to enable drag and drop
33934  * @cfg {Boolean} enableDrag true to enable just drag
33935  * @cfg {Boolean} enableDrop true to enable just drop
33936  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33937  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33938  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33939  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33940  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33941  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33942  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33943  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33944  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33945  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33946  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33947  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33948  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33949  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33950  * @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>
33951  * @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>
33952  * 
33953  * @constructor
33954  * @param {String/HTMLElement/Element} el The container element
33955  * @param {Object} config
33956  */
33957 Roo.tree.TreePanel = function(el, config){
33958     var root = false;
33959     var loader = false;
33960     if (config.root) {
33961         root = config.root;
33962         delete config.root;
33963     }
33964     if (config.loader) {
33965         loader = config.loader;
33966         delete config.loader;
33967     }
33968     
33969     Roo.apply(this, config);
33970     Roo.tree.TreePanel.superclass.constructor.call(this);
33971     this.el = Roo.get(el);
33972     this.el.addClass('x-tree');
33973     //console.log(root);
33974     if (root) {
33975         this.setRootNode( Roo.factory(root, Roo.tree));
33976     }
33977     if (loader) {
33978         this.loader = Roo.factory(loader, Roo.tree);
33979     }
33980    /**
33981     * Read-only. The id of the container element becomes this TreePanel's id.
33982     */
33983     this.id = this.el.id;
33984     this.addEvents({
33985         /**
33986         * @event beforeload
33987         * Fires before a node is loaded, return false to cancel
33988         * @param {Node} node The node being loaded
33989         */
33990         "beforeload" : true,
33991         /**
33992         * @event load
33993         * Fires when a node is loaded
33994         * @param {Node} node The node that was loaded
33995         */
33996         "load" : true,
33997         /**
33998         * @event textchange
33999         * Fires when the text for a node is changed
34000         * @param {Node} node The node
34001         * @param {String} text The new text
34002         * @param {String} oldText The old text
34003         */
34004         "textchange" : true,
34005         /**
34006         * @event beforeexpand
34007         * Fires before a node is expanded, return false to cancel.
34008         * @param {Node} node The node
34009         * @param {Boolean} deep
34010         * @param {Boolean} anim
34011         */
34012         "beforeexpand" : true,
34013         /**
34014         * @event beforecollapse
34015         * Fires before a node is collapsed, return false to cancel.
34016         * @param {Node} node The node
34017         * @param {Boolean} deep
34018         * @param {Boolean} anim
34019         */
34020         "beforecollapse" : true,
34021         /**
34022         * @event expand
34023         * Fires when a node is expanded
34024         * @param {Node} node The node
34025         */
34026         "expand" : true,
34027         /**
34028         * @event disabledchange
34029         * Fires when the disabled status of a node changes
34030         * @param {Node} node The node
34031         * @param {Boolean} disabled
34032         */
34033         "disabledchange" : true,
34034         /**
34035         * @event collapse
34036         * Fires when a node is collapsed
34037         * @param {Node} node The node
34038         */
34039         "collapse" : true,
34040         /**
34041         * @event beforeclick
34042         * Fires before click processing on a node. Return false to cancel the default action.
34043         * @param {Node} node The node
34044         * @param {Roo.EventObject} e The event object
34045         */
34046         "beforeclick":true,
34047         /**
34048         * @event checkchange
34049         * Fires when a node with a checkbox's checked property changes
34050         * @param {Node} this This node
34051         * @param {Boolean} checked
34052         */
34053         "checkchange":true,
34054         /**
34055         * @event click
34056         * Fires when a node is clicked
34057         * @param {Node} node The node
34058         * @param {Roo.EventObject} e The event object
34059         */
34060         "click":true,
34061         /**
34062         * @event dblclick
34063         * Fires when a node is double clicked
34064         * @param {Node} node The node
34065         * @param {Roo.EventObject} e The event object
34066         */
34067         "dblclick":true,
34068         /**
34069         * @event contextmenu
34070         * Fires when a node is right clicked
34071         * @param {Node} node The node
34072         * @param {Roo.EventObject} e The event object
34073         */
34074         "contextmenu":true,
34075         /**
34076         * @event beforechildrenrendered
34077         * Fires right before the child nodes for a node are rendered
34078         * @param {Node} node The node
34079         */
34080         "beforechildrenrendered":true,
34081         /**
34082         * @event startdrag
34083         * Fires when a node starts being dragged
34084         * @param {Roo.tree.TreePanel} this
34085         * @param {Roo.tree.TreeNode} node
34086         * @param {event} e The raw browser event
34087         */ 
34088        "startdrag" : true,
34089        /**
34090         * @event enddrag
34091         * Fires when a drag operation is complete
34092         * @param {Roo.tree.TreePanel} this
34093         * @param {Roo.tree.TreeNode} node
34094         * @param {event} e The raw browser event
34095         */
34096        "enddrag" : true,
34097        /**
34098         * @event dragdrop
34099         * Fires when a dragged node is dropped on a valid DD target
34100         * @param {Roo.tree.TreePanel} this
34101         * @param {Roo.tree.TreeNode} node
34102         * @param {DD} dd The dd it was dropped on
34103         * @param {event} e The raw browser event
34104         */
34105        "dragdrop" : true,
34106        /**
34107         * @event beforenodedrop
34108         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34109         * passed to handlers has the following properties:<br />
34110         * <ul style="padding:5px;padding-left:16px;">
34111         * <li>tree - The TreePanel</li>
34112         * <li>target - The node being targeted for the drop</li>
34113         * <li>data - The drag data from the drag source</li>
34114         * <li>point - The point of the drop - append, above or below</li>
34115         * <li>source - The drag source</li>
34116         * <li>rawEvent - Raw mouse event</li>
34117         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34118         * to be inserted by setting them on this object.</li>
34119         * <li>cancel - Set this to true to cancel the drop.</li>
34120         * </ul>
34121         * @param {Object} dropEvent
34122         */
34123        "beforenodedrop" : true,
34124        /**
34125         * @event nodedrop
34126         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34127         * passed to handlers has the following properties:<br />
34128         * <ul style="padding:5px;padding-left:16px;">
34129         * <li>tree - The TreePanel</li>
34130         * <li>target - The node being targeted for the drop</li>
34131         * <li>data - The drag data from the drag source</li>
34132         * <li>point - The point of the drop - append, above or below</li>
34133         * <li>source - The drag source</li>
34134         * <li>rawEvent - Raw mouse event</li>
34135         * <li>dropNode - Dropped node(s).</li>
34136         * </ul>
34137         * @param {Object} dropEvent
34138         */
34139        "nodedrop" : true,
34140         /**
34141         * @event nodedragover
34142         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34143         * passed to handlers has the following properties:<br />
34144         * <ul style="padding:5px;padding-left:16px;">
34145         * <li>tree - The TreePanel</li>
34146         * <li>target - The node being targeted for the drop</li>
34147         * <li>data - The drag data from the drag source</li>
34148         * <li>point - The point of the drop - append, above or below</li>
34149         * <li>source - The drag source</li>
34150         * <li>rawEvent - Raw mouse event</li>
34151         * <li>dropNode - Drop node(s) provided by the source.</li>
34152         * <li>cancel - Set this to true to signal drop not allowed.</li>
34153         * </ul>
34154         * @param {Object} dragOverEvent
34155         */
34156        "nodedragover" : true
34157         
34158     });
34159     if(this.singleExpand){
34160        this.on("beforeexpand", this.restrictExpand, this);
34161     }
34162     if (this.editor) {
34163         this.editor.tree = this;
34164         this.editor = Roo.factory(this.editor, Roo.tree);
34165     }
34166     
34167     if (this.selModel) {
34168         this.selModel = Roo.factory(this.selModel, Roo.tree);
34169     }
34170    
34171 };
34172 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34173     rootVisible : true,
34174     animate: Roo.enableFx,
34175     lines : true,
34176     enableDD : false,
34177     hlDrop : Roo.enableFx,
34178   
34179     renderer: false,
34180     
34181     rendererTip: false,
34182     // private
34183     restrictExpand : function(node){
34184         var p = node.parentNode;
34185         if(p){
34186             if(p.expandedChild && p.expandedChild.parentNode == p){
34187                 p.expandedChild.collapse();
34188             }
34189             p.expandedChild = node;
34190         }
34191     },
34192
34193     // private override
34194     setRootNode : function(node){
34195         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34196         if(!this.rootVisible){
34197             node.ui = new Roo.tree.RootTreeNodeUI(node);
34198         }
34199         return node;
34200     },
34201
34202     /**
34203      * Returns the container element for this TreePanel
34204      */
34205     getEl : function(){
34206         return this.el;
34207     },
34208
34209     /**
34210      * Returns the default TreeLoader for this TreePanel
34211      */
34212     getLoader : function(){
34213         return this.loader;
34214     },
34215
34216     /**
34217      * Expand all nodes
34218      */
34219     expandAll : function(){
34220         this.root.expand(true);
34221     },
34222
34223     /**
34224      * Collapse all nodes
34225      */
34226     collapseAll : function(){
34227         this.root.collapse(true);
34228     },
34229
34230     /**
34231      * Returns the selection model used by this TreePanel
34232      */
34233     getSelectionModel : function(){
34234         if(!this.selModel){
34235             this.selModel = new Roo.tree.DefaultSelectionModel();
34236         }
34237         return this.selModel;
34238     },
34239
34240     /**
34241      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34242      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34243      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34244      * @return {Array}
34245      */
34246     getChecked : function(a, startNode){
34247         startNode = startNode || this.root;
34248         var r = [];
34249         var f = function(){
34250             if(this.attributes.checked){
34251                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34252             }
34253         }
34254         startNode.cascade(f);
34255         return r;
34256     },
34257
34258     /**
34259      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34260      * @param {String} path
34261      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34262      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34263      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34264      */
34265     expandPath : function(path, attr, callback){
34266         attr = attr || "id";
34267         var keys = path.split(this.pathSeparator);
34268         var curNode = this.root;
34269         if(curNode.attributes[attr] != keys[1]){ // invalid root
34270             if(callback){
34271                 callback(false, null);
34272             }
34273             return;
34274         }
34275         var index = 1;
34276         var f = function(){
34277             if(++index == keys.length){
34278                 if(callback){
34279                     callback(true, curNode);
34280                 }
34281                 return;
34282             }
34283             var c = curNode.findChild(attr, keys[index]);
34284             if(!c){
34285                 if(callback){
34286                     callback(false, curNode);
34287                 }
34288                 return;
34289             }
34290             curNode = c;
34291             c.expand(false, false, f);
34292         };
34293         curNode.expand(false, false, f);
34294     },
34295
34296     /**
34297      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34298      * @param {String} path
34299      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34300      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34301      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34302      */
34303     selectPath : function(path, attr, callback){
34304         attr = attr || "id";
34305         var keys = path.split(this.pathSeparator);
34306         var v = keys.pop();
34307         if(keys.length > 0){
34308             var f = function(success, node){
34309                 if(success && node){
34310                     var n = node.findChild(attr, v);
34311                     if(n){
34312                         n.select();
34313                         if(callback){
34314                             callback(true, n);
34315                         }
34316                     }else if(callback){
34317                         callback(false, n);
34318                     }
34319                 }else{
34320                     if(callback){
34321                         callback(false, n);
34322                     }
34323                 }
34324             };
34325             this.expandPath(keys.join(this.pathSeparator), attr, f);
34326         }else{
34327             this.root.select();
34328             if(callback){
34329                 callback(true, this.root);
34330             }
34331         }
34332     },
34333
34334     getTreeEl : function(){
34335         return this.el;
34336     },
34337
34338     /**
34339      * Trigger rendering of this TreePanel
34340      */
34341     render : function(){
34342         if (this.innerCt) {
34343             return this; // stop it rendering more than once!!
34344         }
34345         
34346         this.innerCt = this.el.createChild({tag:"ul",
34347                cls:"x-tree-root-ct " +
34348                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34349
34350         if(this.containerScroll){
34351             Roo.dd.ScrollManager.register(this.el);
34352         }
34353         if((this.enableDD || this.enableDrop) && !this.dropZone){
34354            /**
34355             * The dropZone used by this tree if drop is enabled
34356             * @type Roo.tree.TreeDropZone
34357             */
34358              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34359                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34360            });
34361         }
34362         if((this.enableDD || this.enableDrag) && !this.dragZone){
34363            /**
34364             * The dragZone used by this tree if drag is enabled
34365             * @type Roo.tree.TreeDragZone
34366             */
34367             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34368                ddGroup: this.ddGroup || "TreeDD",
34369                scroll: this.ddScroll
34370            });
34371         }
34372         this.getSelectionModel().init(this);
34373         if (!this.root) {
34374             Roo.log("ROOT not set in tree");
34375             return this;
34376         }
34377         this.root.render();
34378         if(!this.rootVisible){
34379             this.root.renderChildren();
34380         }
34381         return this;
34382     }
34383 });/*
34384  * Based on:
34385  * Ext JS Library 1.1.1
34386  * Copyright(c) 2006-2007, Ext JS, LLC.
34387  *
34388  * Originally Released Under LGPL - original licence link has changed is not relivant.
34389  *
34390  * Fork - LGPL
34391  * <script type="text/javascript">
34392  */
34393  
34394
34395 /**
34396  * @class Roo.tree.DefaultSelectionModel
34397  * @extends Roo.util.Observable
34398  * The default single selection for a TreePanel.
34399  * @param {Object} cfg Configuration
34400  */
34401 Roo.tree.DefaultSelectionModel = function(cfg){
34402    this.selNode = null;
34403    
34404    
34405    
34406    this.addEvents({
34407        /**
34408         * @event selectionchange
34409         * Fires when the selected node changes
34410         * @param {DefaultSelectionModel} this
34411         * @param {TreeNode} node the new selection
34412         */
34413        "selectionchange" : true,
34414
34415        /**
34416         * @event beforeselect
34417         * Fires before the selected node changes, return false to cancel the change
34418         * @param {DefaultSelectionModel} this
34419         * @param {TreeNode} node the new selection
34420         * @param {TreeNode} node the old selection
34421         */
34422        "beforeselect" : true
34423    });
34424    
34425     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34426 };
34427
34428 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34429     init : function(tree){
34430         this.tree = tree;
34431         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34432         tree.on("click", this.onNodeClick, this);
34433     },
34434     
34435     onNodeClick : function(node, e){
34436         if (e.ctrlKey && this.selNode == node)  {
34437             this.unselect(node);
34438             return;
34439         }
34440         this.select(node);
34441     },
34442     
34443     /**
34444      * Select a node.
34445      * @param {TreeNode} node The node to select
34446      * @return {TreeNode} The selected node
34447      */
34448     select : function(node){
34449         var last = this.selNode;
34450         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34451             if(last){
34452                 last.ui.onSelectedChange(false);
34453             }
34454             this.selNode = node;
34455             node.ui.onSelectedChange(true);
34456             this.fireEvent("selectionchange", this, node, last);
34457         }
34458         return node;
34459     },
34460     
34461     /**
34462      * Deselect a node.
34463      * @param {TreeNode} node The node to unselect
34464      */
34465     unselect : function(node){
34466         if(this.selNode == node){
34467             this.clearSelections();
34468         }    
34469     },
34470     
34471     /**
34472      * Clear all selections
34473      */
34474     clearSelections : function(){
34475         var n = this.selNode;
34476         if(n){
34477             n.ui.onSelectedChange(false);
34478             this.selNode = null;
34479             this.fireEvent("selectionchange", this, null);
34480         }
34481         return n;
34482     },
34483     
34484     /**
34485      * Get the selected node
34486      * @return {TreeNode} The selected node
34487      */
34488     getSelectedNode : function(){
34489         return this.selNode;    
34490     },
34491     
34492     /**
34493      * Returns true if the node is selected
34494      * @param {TreeNode} node The node to check
34495      * @return {Boolean}
34496      */
34497     isSelected : function(node){
34498         return this.selNode == node;  
34499     },
34500
34501     /**
34502      * Selects the node above the selected node in the tree, intelligently walking the nodes
34503      * @return TreeNode The new selection
34504      */
34505     selectPrevious : function(){
34506         var s = this.selNode || this.lastSelNode;
34507         if(!s){
34508             return null;
34509         }
34510         var ps = s.previousSibling;
34511         if(ps){
34512             if(!ps.isExpanded() || ps.childNodes.length < 1){
34513                 return this.select(ps);
34514             } else{
34515                 var lc = ps.lastChild;
34516                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34517                     lc = lc.lastChild;
34518                 }
34519                 return this.select(lc);
34520             }
34521         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34522             return this.select(s.parentNode);
34523         }
34524         return null;
34525     },
34526
34527     /**
34528      * Selects the node above the selected node in the tree, intelligently walking the nodes
34529      * @return TreeNode The new selection
34530      */
34531     selectNext : function(){
34532         var s = this.selNode || this.lastSelNode;
34533         if(!s){
34534             return null;
34535         }
34536         if(s.firstChild && s.isExpanded()){
34537              return this.select(s.firstChild);
34538          }else if(s.nextSibling){
34539              return this.select(s.nextSibling);
34540          }else if(s.parentNode){
34541             var newS = null;
34542             s.parentNode.bubble(function(){
34543                 if(this.nextSibling){
34544                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34545                     return false;
34546                 }
34547             });
34548             return newS;
34549          }
34550         return null;
34551     },
34552
34553     onKeyDown : function(e){
34554         var s = this.selNode || this.lastSelNode;
34555         // undesirable, but required
34556         var sm = this;
34557         if(!s){
34558             return;
34559         }
34560         var k = e.getKey();
34561         switch(k){
34562              case e.DOWN:
34563                  e.stopEvent();
34564                  this.selectNext();
34565              break;
34566              case e.UP:
34567                  e.stopEvent();
34568                  this.selectPrevious();
34569              break;
34570              case e.RIGHT:
34571                  e.preventDefault();
34572                  if(s.hasChildNodes()){
34573                      if(!s.isExpanded()){
34574                          s.expand();
34575                      }else if(s.firstChild){
34576                          this.select(s.firstChild, e);
34577                      }
34578                  }
34579              break;
34580              case e.LEFT:
34581                  e.preventDefault();
34582                  if(s.hasChildNodes() && s.isExpanded()){
34583                      s.collapse();
34584                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34585                      this.select(s.parentNode, e);
34586                  }
34587              break;
34588         };
34589     }
34590 });
34591
34592 /**
34593  * @class Roo.tree.MultiSelectionModel
34594  * @extends Roo.util.Observable
34595  * Multi selection for a TreePanel.
34596  * @param {Object} cfg Configuration
34597  */
34598 Roo.tree.MultiSelectionModel = function(){
34599    this.selNodes = [];
34600    this.selMap = {};
34601    this.addEvents({
34602        /**
34603         * @event selectionchange
34604         * Fires when the selected nodes change
34605         * @param {MultiSelectionModel} this
34606         * @param {Array} nodes Array of the selected nodes
34607         */
34608        "selectionchange" : true
34609    });
34610    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34611    
34612 };
34613
34614 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34615     init : function(tree){
34616         this.tree = tree;
34617         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34618         tree.on("click", this.onNodeClick, this);
34619     },
34620     
34621     onNodeClick : function(node, e){
34622         this.select(node, e, e.ctrlKey);
34623     },
34624     
34625     /**
34626      * Select a node.
34627      * @param {TreeNode} node The node to select
34628      * @param {EventObject} e (optional) An event associated with the selection
34629      * @param {Boolean} keepExisting True to retain existing selections
34630      * @return {TreeNode} The selected node
34631      */
34632     select : function(node, e, keepExisting){
34633         if(keepExisting !== true){
34634             this.clearSelections(true);
34635         }
34636         if(this.isSelected(node)){
34637             this.lastSelNode = node;
34638             return node;
34639         }
34640         this.selNodes.push(node);
34641         this.selMap[node.id] = node;
34642         this.lastSelNode = node;
34643         node.ui.onSelectedChange(true);
34644         this.fireEvent("selectionchange", this, this.selNodes);
34645         return node;
34646     },
34647     
34648     /**
34649      * Deselect a node.
34650      * @param {TreeNode} node The node to unselect
34651      */
34652     unselect : function(node){
34653         if(this.selMap[node.id]){
34654             node.ui.onSelectedChange(false);
34655             var sn = this.selNodes;
34656             var index = -1;
34657             if(sn.indexOf){
34658                 index = sn.indexOf(node);
34659             }else{
34660                 for(var i = 0, len = sn.length; i < len; i++){
34661                     if(sn[i] == node){
34662                         index = i;
34663                         break;
34664                     }
34665                 }
34666             }
34667             if(index != -1){
34668                 this.selNodes.splice(index, 1);
34669             }
34670             delete this.selMap[node.id];
34671             this.fireEvent("selectionchange", this, this.selNodes);
34672         }
34673     },
34674     
34675     /**
34676      * Clear all selections
34677      */
34678     clearSelections : function(suppressEvent){
34679         var sn = this.selNodes;
34680         if(sn.length > 0){
34681             for(var i = 0, len = sn.length; i < len; i++){
34682                 sn[i].ui.onSelectedChange(false);
34683             }
34684             this.selNodes = [];
34685             this.selMap = {};
34686             if(suppressEvent !== true){
34687                 this.fireEvent("selectionchange", this, this.selNodes);
34688             }
34689         }
34690     },
34691     
34692     /**
34693      * Returns true if the node is selected
34694      * @param {TreeNode} node The node to check
34695      * @return {Boolean}
34696      */
34697     isSelected : function(node){
34698         return this.selMap[node.id] ? true : false;  
34699     },
34700     
34701     /**
34702      * Returns an array of the selected nodes
34703      * @return {Array}
34704      */
34705     getSelectedNodes : function(){
34706         return this.selNodes;    
34707     },
34708
34709     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34710
34711     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34712
34713     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34714 });/*
34715  * Based on:
34716  * Ext JS Library 1.1.1
34717  * Copyright(c) 2006-2007, Ext JS, LLC.
34718  *
34719  * Originally Released Under LGPL - original licence link has changed is not relivant.
34720  *
34721  * Fork - LGPL
34722  * <script type="text/javascript">
34723  */
34724  
34725 /**
34726  * @class Roo.tree.TreeNode
34727  * @extends Roo.data.Node
34728  * @cfg {String} text The text for this node
34729  * @cfg {Boolean} expanded true to start the node expanded
34730  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34731  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34732  * @cfg {Boolean} disabled true to start the node disabled
34733  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34734  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34735  * @cfg {String} cls A css class to be added to the node
34736  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34737  * @cfg {String} href URL of the link used for the node (defaults to #)
34738  * @cfg {String} hrefTarget target frame for the link
34739  * @cfg {String} qtip An Ext QuickTip for the node
34740  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34741  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34742  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34743  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34744  * (defaults to undefined with no checkbox rendered)
34745  * @constructor
34746  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34747  */
34748 Roo.tree.TreeNode = function(attributes){
34749     attributes = attributes || {};
34750     if(typeof attributes == "string"){
34751         attributes = {text: attributes};
34752     }
34753     this.childrenRendered = false;
34754     this.rendered = false;
34755     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34756     this.expanded = attributes.expanded === true;
34757     this.isTarget = attributes.isTarget !== false;
34758     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34759     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34760
34761     /**
34762      * Read-only. The text for this node. To change it use setText().
34763      * @type String
34764      */
34765     this.text = attributes.text;
34766     /**
34767      * True if this node is disabled.
34768      * @type Boolean
34769      */
34770     this.disabled = attributes.disabled === true;
34771
34772     this.addEvents({
34773         /**
34774         * @event textchange
34775         * Fires when the text for this node is changed
34776         * @param {Node} this This node
34777         * @param {String} text The new text
34778         * @param {String} oldText The old text
34779         */
34780         "textchange" : true,
34781         /**
34782         * @event beforeexpand
34783         * Fires before this node is expanded, return false to cancel.
34784         * @param {Node} this This node
34785         * @param {Boolean} deep
34786         * @param {Boolean} anim
34787         */
34788         "beforeexpand" : true,
34789         /**
34790         * @event beforecollapse
34791         * Fires before this node is collapsed, return false to cancel.
34792         * @param {Node} this This node
34793         * @param {Boolean} deep
34794         * @param {Boolean} anim
34795         */
34796         "beforecollapse" : true,
34797         /**
34798         * @event expand
34799         * Fires when this node is expanded
34800         * @param {Node} this This node
34801         */
34802         "expand" : true,
34803         /**
34804         * @event disabledchange
34805         * Fires when the disabled status of this node changes
34806         * @param {Node} this This node
34807         * @param {Boolean} disabled
34808         */
34809         "disabledchange" : true,
34810         /**
34811         * @event collapse
34812         * Fires when this node is collapsed
34813         * @param {Node} this This node
34814         */
34815         "collapse" : true,
34816         /**
34817         * @event beforeclick
34818         * Fires before click processing. Return false to cancel the default action.
34819         * @param {Node} this This node
34820         * @param {Roo.EventObject} e The event object
34821         */
34822         "beforeclick":true,
34823         /**
34824         * @event checkchange
34825         * Fires when a node with a checkbox's checked property changes
34826         * @param {Node} this This node
34827         * @param {Boolean} checked
34828         */
34829         "checkchange":true,
34830         /**
34831         * @event click
34832         * Fires when this node is clicked
34833         * @param {Node} this This node
34834         * @param {Roo.EventObject} e The event object
34835         */
34836         "click":true,
34837         /**
34838         * @event dblclick
34839         * Fires when this node is double clicked
34840         * @param {Node} this This node
34841         * @param {Roo.EventObject} e The event object
34842         */
34843         "dblclick":true,
34844         /**
34845         * @event contextmenu
34846         * Fires when this node is right clicked
34847         * @param {Node} this This node
34848         * @param {Roo.EventObject} e The event object
34849         */
34850         "contextmenu":true,
34851         /**
34852         * @event beforechildrenrendered
34853         * Fires right before the child nodes for this node are rendered
34854         * @param {Node} this This node
34855         */
34856         "beforechildrenrendered":true
34857     });
34858
34859     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34860
34861     /**
34862      * Read-only. The UI for this node
34863      * @type TreeNodeUI
34864      */
34865     this.ui = new uiClass(this);
34866     
34867     // finally support items[]
34868     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34869         return;
34870     }
34871     
34872     
34873     Roo.each(this.attributes.items, function(c) {
34874         this.appendChild(Roo.factory(c,Roo.Tree));
34875     }, this);
34876     delete this.attributes.items;
34877     
34878     
34879     
34880 };
34881 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34882     preventHScroll: true,
34883     /**
34884      * Returns true if this node is expanded
34885      * @return {Boolean}
34886      */
34887     isExpanded : function(){
34888         return this.expanded;
34889     },
34890
34891     /**
34892      * Returns the UI object for this node
34893      * @return {TreeNodeUI}
34894      */
34895     getUI : function(){
34896         return this.ui;
34897     },
34898
34899     // private override
34900     setFirstChild : function(node){
34901         var of = this.firstChild;
34902         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34903         if(this.childrenRendered && of && node != of){
34904             of.renderIndent(true, true);
34905         }
34906         if(this.rendered){
34907             this.renderIndent(true, true);
34908         }
34909     },
34910
34911     // private override
34912     setLastChild : function(node){
34913         var ol = this.lastChild;
34914         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34915         if(this.childrenRendered && ol && node != ol){
34916             ol.renderIndent(true, true);
34917         }
34918         if(this.rendered){
34919             this.renderIndent(true, true);
34920         }
34921     },
34922
34923     // these methods are overridden to provide lazy rendering support
34924     // private override
34925     appendChild : function()
34926     {
34927         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34928         if(node && this.childrenRendered){
34929             node.render();
34930         }
34931         this.ui.updateExpandIcon();
34932         return node;
34933     },
34934
34935     // private override
34936     removeChild : function(node){
34937         this.ownerTree.getSelectionModel().unselect(node);
34938         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34939         // if it's been rendered remove dom node
34940         if(this.childrenRendered){
34941             node.ui.remove();
34942         }
34943         if(this.childNodes.length < 1){
34944             this.collapse(false, false);
34945         }else{
34946             this.ui.updateExpandIcon();
34947         }
34948         if(!this.firstChild) {
34949             this.childrenRendered = false;
34950         }
34951         return node;
34952     },
34953
34954     // private override
34955     insertBefore : function(node, refNode){
34956         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34957         if(newNode && refNode && this.childrenRendered){
34958             node.render();
34959         }
34960         this.ui.updateExpandIcon();
34961         return newNode;
34962     },
34963
34964     /**
34965      * Sets the text for this node
34966      * @param {String} text
34967      */
34968     setText : function(text){
34969         var oldText = this.text;
34970         this.text = text;
34971         this.attributes.text = text;
34972         if(this.rendered){ // event without subscribing
34973             this.ui.onTextChange(this, text, oldText);
34974         }
34975         this.fireEvent("textchange", this, text, oldText);
34976     },
34977
34978     /**
34979      * Triggers selection of this node
34980      */
34981     select : function(){
34982         this.getOwnerTree().getSelectionModel().select(this);
34983     },
34984
34985     /**
34986      * Triggers deselection of this node
34987      */
34988     unselect : function(){
34989         this.getOwnerTree().getSelectionModel().unselect(this);
34990     },
34991
34992     /**
34993      * Returns true if this node is selected
34994      * @return {Boolean}
34995      */
34996     isSelected : function(){
34997         return this.getOwnerTree().getSelectionModel().isSelected(this);
34998     },
34999
35000     /**
35001      * Expand this node.
35002      * @param {Boolean} deep (optional) True to expand all children as well
35003      * @param {Boolean} anim (optional) false to cancel the default animation
35004      * @param {Function} callback (optional) A callback to be called when
35005      * expanding this node completes (does not wait for deep expand to complete).
35006      * Called with 1 parameter, this node.
35007      */
35008     expand : function(deep, anim, callback){
35009         if(!this.expanded){
35010             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35011                 return;
35012             }
35013             if(!this.childrenRendered){
35014                 this.renderChildren();
35015             }
35016             this.expanded = true;
35017             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35018                 this.ui.animExpand(function(){
35019                     this.fireEvent("expand", this);
35020                     if(typeof callback == "function"){
35021                         callback(this);
35022                     }
35023                     if(deep === true){
35024                         this.expandChildNodes(true);
35025                     }
35026                 }.createDelegate(this));
35027                 return;
35028             }else{
35029                 this.ui.expand();
35030                 this.fireEvent("expand", this);
35031                 if(typeof callback == "function"){
35032                     callback(this);
35033                 }
35034             }
35035         }else{
35036            if(typeof callback == "function"){
35037                callback(this);
35038            }
35039         }
35040         if(deep === true){
35041             this.expandChildNodes(true);
35042         }
35043     },
35044
35045     isHiddenRoot : function(){
35046         return this.isRoot && !this.getOwnerTree().rootVisible;
35047     },
35048
35049     /**
35050      * Collapse this node.
35051      * @param {Boolean} deep (optional) True to collapse all children as well
35052      * @param {Boolean} anim (optional) false to cancel the default animation
35053      */
35054     collapse : function(deep, anim){
35055         if(this.expanded && !this.isHiddenRoot()){
35056             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35057                 return;
35058             }
35059             this.expanded = false;
35060             if((this.getOwnerTree().animate && anim !== false) || anim){
35061                 this.ui.animCollapse(function(){
35062                     this.fireEvent("collapse", this);
35063                     if(deep === true){
35064                         this.collapseChildNodes(true);
35065                     }
35066                 }.createDelegate(this));
35067                 return;
35068             }else{
35069                 this.ui.collapse();
35070                 this.fireEvent("collapse", this);
35071             }
35072         }
35073         if(deep === true){
35074             var cs = this.childNodes;
35075             for(var i = 0, len = cs.length; i < len; i++) {
35076                 cs[i].collapse(true, false);
35077             }
35078         }
35079     },
35080
35081     // private
35082     delayedExpand : function(delay){
35083         if(!this.expandProcId){
35084             this.expandProcId = this.expand.defer(delay, this);
35085         }
35086     },
35087
35088     // private
35089     cancelExpand : function(){
35090         if(this.expandProcId){
35091             clearTimeout(this.expandProcId);
35092         }
35093         this.expandProcId = false;
35094     },
35095
35096     /**
35097      * Toggles expanded/collapsed state of the node
35098      */
35099     toggle : function(){
35100         if(this.expanded){
35101             this.collapse();
35102         }else{
35103             this.expand();
35104         }
35105     },
35106
35107     /**
35108      * Ensures all parent nodes are expanded
35109      */
35110     ensureVisible : function(callback){
35111         var tree = this.getOwnerTree();
35112         tree.expandPath(this.parentNode.getPath(), false, function(){
35113             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35114             Roo.callback(callback);
35115         }.createDelegate(this));
35116     },
35117
35118     /**
35119      * Expand all child nodes
35120      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35121      */
35122     expandChildNodes : function(deep){
35123         var cs = this.childNodes;
35124         for(var i = 0, len = cs.length; i < len; i++) {
35125                 cs[i].expand(deep);
35126         }
35127     },
35128
35129     /**
35130      * Collapse all child nodes
35131      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35132      */
35133     collapseChildNodes : function(deep){
35134         var cs = this.childNodes;
35135         for(var i = 0, len = cs.length; i < len; i++) {
35136                 cs[i].collapse(deep);
35137         }
35138     },
35139
35140     /**
35141      * Disables this node
35142      */
35143     disable : function(){
35144         this.disabled = true;
35145         this.unselect();
35146         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35147             this.ui.onDisableChange(this, true);
35148         }
35149         this.fireEvent("disabledchange", this, true);
35150     },
35151
35152     /**
35153      * Enables this node
35154      */
35155     enable : function(){
35156         this.disabled = false;
35157         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35158             this.ui.onDisableChange(this, false);
35159         }
35160         this.fireEvent("disabledchange", this, false);
35161     },
35162
35163     // private
35164     renderChildren : function(suppressEvent){
35165         if(suppressEvent !== false){
35166             this.fireEvent("beforechildrenrendered", this);
35167         }
35168         var cs = this.childNodes;
35169         for(var i = 0, len = cs.length; i < len; i++){
35170             cs[i].render(true);
35171         }
35172         this.childrenRendered = true;
35173     },
35174
35175     // private
35176     sort : function(fn, scope){
35177         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35178         if(this.childrenRendered){
35179             var cs = this.childNodes;
35180             for(var i = 0, len = cs.length; i < len; i++){
35181                 cs[i].render(true);
35182             }
35183         }
35184     },
35185
35186     // private
35187     render : function(bulkRender){
35188         this.ui.render(bulkRender);
35189         if(!this.rendered){
35190             this.rendered = true;
35191             if(this.expanded){
35192                 this.expanded = false;
35193                 this.expand(false, false);
35194             }
35195         }
35196     },
35197
35198     // private
35199     renderIndent : function(deep, refresh){
35200         if(refresh){
35201             this.ui.childIndent = null;
35202         }
35203         this.ui.renderIndent();
35204         if(deep === true && this.childrenRendered){
35205             var cs = this.childNodes;
35206             for(var i = 0, len = cs.length; i < len; i++){
35207                 cs[i].renderIndent(true, refresh);
35208             }
35209         }
35210     }
35211 });/*
35212  * Based on:
35213  * Ext JS Library 1.1.1
35214  * Copyright(c) 2006-2007, Ext JS, LLC.
35215  *
35216  * Originally Released Under LGPL - original licence link has changed is not relivant.
35217  *
35218  * Fork - LGPL
35219  * <script type="text/javascript">
35220  */
35221  
35222 /**
35223  * @class Roo.tree.AsyncTreeNode
35224  * @extends Roo.tree.TreeNode
35225  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35226  * @constructor
35227  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35228  */
35229  Roo.tree.AsyncTreeNode = function(config){
35230     this.loaded = false;
35231     this.loading = false;
35232     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35233     /**
35234     * @event beforeload
35235     * Fires before this node is loaded, return false to cancel
35236     * @param {Node} this This node
35237     */
35238     this.addEvents({'beforeload':true, 'load': true});
35239     /**
35240     * @event load
35241     * Fires when this node is loaded
35242     * @param {Node} this This node
35243     */
35244     /**
35245      * The loader used by this node (defaults to using the tree's defined loader)
35246      * @type TreeLoader
35247      * @property loader
35248      */
35249 };
35250 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35251     expand : function(deep, anim, callback){
35252         if(this.loading){ // if an async load is already running, waiting til it's done
35253             var timer;
35254             var f = function(){
35255                 if(!this.loading){ // done loading
35256                     clearInterval(timer);
35257                     this.expand(deep, anim, callback);
35258                 }
35259             }.createDelegate(this);
35260             timer = setInterval(f, 200);
35261             return;
35262         }
35263         if(!this.loaded){
35264             if(this.fireEvent("beforeload", this) === false){
35265                 return;
35266             }
35267             this.loading = true;
35268             this.ui.beforeLoad(this);
35269             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35270             if(loader){
35271                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35272                 return;
35273             }
35274         }
35275         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35276     },
35277     
35278     /**
35279      * Returns true if this node is currently loading
35280      * @return {Boolean}
35281      */
35282     isLoading : function(){
35283         return this.loading;  
35284     },
35285     
35286     loadComplete : function(deep, anim, callback){
35287         this.loading = false;
35288         this.loaded = true;
35289         this.ui.afterLoad(this);
35290         this.fireEvent("load", this);
35291         this.expand(deep, anim, callback);
35292     },
35293     
35294     /**
35295      * Returns true if this node has been loaded
35296      * @return {Boolean}
35297      */
35298     isLoaded : function(){
35299         return this.loaded;
35300     },
35301     
35302     hasChildNodes : function(){
35303         if(!this.isLeaf() && !this.loaded){
35304             return true;
35305         }else{
35306             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35307         }
35308     },
35309
35310     /**
35311      * Trigger a reload for this node
35312      * @param {Function} callback
35313      */
35314     reload : function(callback){
35315         this.collapse(false, false);
35316         while(this.firstChild){
35317             this.removeChild(this.firstChild);
35318         }
35319         this.childrenRendered = false;
35320         this.loaded = false;
35321         if(this.isHiddenRoot()){
35322             this.expanded = false;
35323         }
35324         this.expand(false, false, callback);
35325     }
35326 });/*
35327  * Based on:
35328  * Ext JS Library 1.1.1
35329  * Copyright(c) 2006-2007, Ext JS, LLC.
35330  *
35331  * Originally Released Under LGPL - original licence link has changed is not relivant.
35332  *
35333  * Fork - LGPL
35334  * <script type="text/javascript">
35335  */
35336  
35337 /**
35338  * @class Roo.tree.TreeNodeUI
35339  * @constructor
35340  * @param {Object} node The node to render
35341  * The TreeNode UI implementation is separate from the
35342  * tree implementation. Unless you are customizing the tree UI,
35343  * you should never have to use this directly.
35344  */
35345 Roo.tree.TreeNodeUI = function(node){
35346     this.node = node;
35347     this.rendered = false;
35348     this.animating = false;
35349     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35350 };
35351
35352 Roo.tree.TreeNodeUI.prototype = {
35353     removeChild : function(node){
35354         if(this.rendered){
35355             this.ctNode.removeChild(node.ui.getEl());
35356         }
35357     },
35358
35359     beforeLoad : function(){
35360          this.addClass("x-tree-node-loading");
35361     },
35362
35363     afterLoad : function(){
35364          this.removeClass("x-tree-node-loading");
35365     },
35366
35367     onTextChange : function(node, text, oldText){
35368         if(this.rendered){
35369             this.textNode.innerHTML = text;
35370         }
35371     },
35372
35373     onDisableChange : function(node, state){
35374         this.disabled = state;
35375         if(state){
35376             this.addClass("x-tree-node-disabled");
35377         }else{
35378             this.removeClass("x-tree-node-disabled");
35379         }
35380     },
35381
35382     onSelectedChange : function(state){
35383         if(state){
35384             this.focus();
35385             this.addClass("x-tree-selected");
35386         }else{
35387             //this.blur();
35388             this.removeClass("x-tree-selected");
35389         }
35390     },
35391
35392     onMove : function(tree, node, oldParent, newParent, index, refNode){
35393         this.childIndent = null;
35394         if(this.rendered){
35395             var targetNode = newParent.ui.getContainer();
35396             if(!targetNode){//target not rendered
35397                 this.holder = document.createElement("div");
35398                 this.holder.appendChild(this.wrap);
35399                 return;
35400             }
35401             var insertBefore = refNode ? refNode.ui.getEl() : null;
35402             if(insertBefore){
35403                 targetNode.insertBefore(this.wrap, insertBefore);
35404             }else{
35405                 targetNode.appendChild(this.wrap);
35406             }
35407             this.node.renderIndent(true);
35408         }
35409     },
35410
35411     addClass : function(cls){
35412         if(this.elNode){
35413             Roo.fly(this.elNode).addClass(cls);
35414         }
35415     },
35416
35417     removeClass : function(cls){
35418         if(this.elNode){
35419             Roo.fly(this.elNode).removeClass(cls);
35420         }
35421     },
35422
35423     remove : function(){
35424         if(this.rendered){
35425             this.holder = document.createElement("div");
35426             this.holder.appendChild(this.wrap);
35427         }
35428     },
35429
35430     fireEvent : function(){
35431         return this.node.fireEvent.apply(this.node, arguments);
35432     },
35433
35434     initEvents : function(){
35435         this.node.on("move", this.onMove, this);
35436         var E = Roo.EventManager;
35437         var a = this.anchor;
35438
35439         var el = Roo.fly(a, '_treeui');
35440
35441         if(Roo.isOpera){ // opera render bug ignores the CSS
35442             el.setStyle("text-decoration", "none");
35443         }
35444
35445         el.on("click", this.onClick, this);
35446         el.on("dblclick", this.onDblClick, this);
35447
35448         if(this.checkbox){
35449             Roo.EventManager.on(this.checkbox,
35450                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35451         }
35452
35453         el.on("contextmenu", this.onContextMenu, this);
35454
35455         var icon = Roo.fly(this.iconNode);
35456         icon.on("click", this.onClick, this);
35457         icon.on("dblclick", this.onDblClick, this);
35458         icon.on("contextmenu", this.onContextMenu, this);
35459         E.on(this.ecNode, "click", this.ecClick, this, true);
35460
35461         if(this.node.disabled){
35462             this.addClass("x-tree-node-disabled");
35463         }
35464         if(this.node.hidden){
35465             this.addClass("x-tree-node-disabled");
35466         }
35467         var ot = this.node.getOwnerTree();
35468         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35469         if(dd && (!this.node.isRoot || ot.rootVisible)){
35470             Roo.dd.Registry.register(this.elNode, {
35471                 node: this.node,
35472                 handles: this.getDDHandles(),
35473                 isHandle: false
35474             });
35475         }
35476     },
35477
35478     getDDHandles : function(){
35479         return [this.iconNode, this.textNode];
35480     },
35481
35482     hide : function(){
35483         if(this.rendered){
35484             this.wrap.style.display = "none";
35485         }
35486     },
35487
35488     show : function(){
35489         if(this.rendered){
35490             this.wrap.style.display = "";
35491         }
35492     },
35493
35494     onContextMenu : function(e){
35495         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35496             e.preventDefault();
35497             this.focus();
35498             this.fireEvent("contextmenu", this.node, e);
35499         }
35500     },
35501
35502     onClick : function(e){
35503         if(this.dropping){
35504             e.stopEvent();
35505             return;
35506         }
35507         if(this.fireEvent("beforeclick", this.node, e) !== false){
35508             if(!this.disabled && this.node.attributes.href){
35509                 this.fireEvent("click", this.node, e);
35510                 return;
35511             }
35512             e.preventDefault();
35513             if(this.disabled){
35514                 return;
35515             }
35516
35517             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35518                 this.node.toggle();
35519             }
35520
35521             this.fireEvent("click", this.node, e);
35522         }else{
35523             e.stopEvent();
35524         }
35525     },
35526
35527     onDblClick : function(e){
35528         e.preventDefault();
35529         if(this.disabled){
35530             return;
35531         }
35532         if(this.checkbox){
35533             this.toggleCheck();
35534         }
35535         if(!this.animating && this.node.hasChildNodes()){
35536             this.node.toggle();
35537         }
35538         this.fireEvent("dblclick", this.node, e);
35539     },
35540
35541     onCheckChange : function(){
35542         var checked = this.checkbox.checked;
35543         this.node.attributes.checked = checked;
35544         this.fireEvent('checkchange', this.node, checked);
35545     },
35546
35547     ecClick : function(e){
35548         if(!this.animating && this.node.hasChildNodes()){
35549             this.node.toggle();
35550         }
35551     },
35552
35553     startDrop : function(){
35554         this.dropping = true;
35555     },
35556
35557     // delayed drop so the click event doesn't get fired on a drop
35558     endDrop : function(){
35559        setTimeout(function(){
35560            this.dropping = false;
35561        }.createDelegate(this), 50);
35562     },
35563
35564     expand : function(){
35565         this.updateExpandIcon();
35566         this.ctNode.style.display = "";
35567     },
35568
35569     focus : function(){
35570         if(!this.node.preventHScroll){
35571             try{this.anchor.focus();
35572             }catch(e){}
35573         }else if(!Roo.isIE){
35574             try{
35575                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35576                 var l = noscroll.scrollLeft;
35577                 this.anchor.focus();
35578                 noscroll.scrollLeft = l;
35579             }catch(e){}
35580         }
35581     },
35582
35583     toggleCheck : function(value){
35584         var cb = this.checkbox;
35585         if(cb){
35586             cb.checked = (value === undefined ? !cb.checked : value);
35587         }
35588     },
35589
35590     blur : function(){
35591         try{
35592             this.anchor.blur();
35593         }catch(e){}
35594     },
35595
35596     animExpand : function(callback){
35597         var ct = Roo.get(this.ctNode);
35598         ct.stopFx();
35599         if(!this.node.hasChildNodes()){
35600             this.updateExpandIcon();
35601             this.ctNode.style.display = "";
35602             Roo.callback(callback);
35603             return;
35604         }
35605         this.animating = true;
35606         this.updateExpandIcon();
35607
35608         ct.slideIn('t', {
35609            callback : function(){
35610                this.animating = false;
35611                Roo.callback(callback);
35612             },
35613             scope: this,
35614             duration: this.node.ownerTree.duration || .25
35615         });
35616     },
35617
35618     highlight : function(){
35619         var tree = this.node.getOwnerTree();
35620         Roo.fly(this.wrap).highlight(
35621             tree.hlColor || "C3DAF9",
35622             {endColor: tree.hlBaseColor}
35623         );
35624     },
35625
35626     collapse : function(){
35627         this.updateExpandIcon();
35628         this.ctNode.style.display = "none";
35629     },
35630
35631     animCollapse : function(callback){
35632         var ct = Roo.get(this.ctNode);
35633         ct.enableDisplayMode('block');
35634         ct.stopFx();
35635
35636         this.animating = true;
35637         this.updateExpandIcon();
35638
35639         ct.slideOut('t', {
35640             callback : function(){
35641                this.animating = false;
35642                Roo.callback(callback);
35643             },
35644             scope: this,
35645             duration: this.node.ownerTree.duration || .25
35646         });
35647     },
35648
35649     getContainer : function(){
35650         return this.ctNode;
35651     },
35652
35653     getEl : function(){
35654         return this.wrap;
35655     },
35656
35657     appendDDGhost : function(ghostNode){
35658         ghostNode.appendChild(this.elNode.cloneNode(true));
35659     },
35660
35661     getDDRepairXY : function(){
35662         return Roo.lib.Dom.getXY(this.iconNode);
35663     },
35664
35665     onRender : function(){
35666         this.render();
35667     },
35668
35669     render : function(bulkRender){
35670         var n = this.node, a = n.attributes;
35671         var targetNode = n.parentNode ?
35672               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35673
35674         if(!this.rendered){
35675             this.rendered = true;
35676
35677             this.renderElements(n, a, targetNode, bulkRender);
35678
35679             if(a.qtip){
35680                if(this.textNode.setAttributeNS){
35681                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35682                    if(a.qtipTitle){
35683                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35684                    }
35685                }else{
35686                    this.textNode.setAttribute("ext:qtip", a.qtip);
35687                    if(a.qtipTitle){
35688                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35689                    }
35690                }
35691             }else if(a.qtipCfg){
35692                 a.qtipCfg.target = Roo.id(this.textNode);
35693                 Roo.QuickTips.register(a.qtipCfg);
35694             }
35695             this.initEvents();
35696             if(!this.node.expanded){
35697                 this.updateExpandIcon();
35698             }
35699         }else{
35700             if(bulkRender === true) {
35701                 targetNode.appendChild(this.wrap);
35702             }
35703         }
35704     },
35705
35706     renderElements : function(n, a, targetNode, bulkRender)
35707     {
35708         // add some indent caching, this helps performance when rendering a large tree
35709         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35710         var t = n.getOwnerTree();
35711         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35712         if (typeof(n.attributes.html) != 'undefined') {
35713             txt = n.attributes.html;
35714         }
35715         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35716         var cb = typeof a.checked == 'boolean';
35717         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35718         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35719             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35720             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35721             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35722             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35723             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35724              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35725                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35726             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35727             "</li>"];
35728
35729         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35730             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35731                                 n.nextSibling.ui.getEl(), buf.join(""));
35732         }else{
35733             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35734         }
35735
35736         this.elNode = this.wrap.childNodes[0];
35737         this.ctNode = this.wrap.childNodes[1];
35738         var cs = this.elNode.childNodes;
35739         this.indentNode = cs[0];
35740         this.ecNode = cs[1];
35741         this.iconNode = cs[2];
35742         var index = 3;
35743         if(cb){
35744             this.checkbox = cs[3];
35745             index++;
35746         }
35747         this.anchor = cs[index];
35748         this.textNode = cs[index].firstChild;
35749     },
35750
35751     getAnchor : function(){
35752         return this.anchor;
35753     },
35754
35755     getTextEl : function(){
35756         return this.textNode;
35757     },
35758
35759     getIconEl : function(){
35760         return this.iconNode;
35761     },
35762
35763     isChecked : function(){
35764         return this.checkbox ? this.checkbox.checked : false;
35765     },
35766
35767     updateExpandIcon : function(){
35768         if(this.rendered){
35769             var n = this.node, c1, c2;
35770             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35771             var hasChild = n.hasChildNodes();
35772             if(hasChild){
35773                 if(n.expanded){
35774                     cls += "-minus";
35775                     c1 = "x-tree-node-collapsed";
35776                     c2 = "x-tree-node-expanded";
35777                 }else{
35778                     cls += "-plus";
35779                     c1 = "x-tree-node-expanded";
35780                     c2 = "x-tree-node-collapsed";
35781                 }
35782                 if(this.wasLeaf){
35783                     this.removeClass("x-tree-node-leaf");
35784                     this.wasLeaf = false;
35785                 }
35786                 if(this.c1 != c1 || this.c2 != c2){
35787                     Roo.fly(this.elNode).replaceClass(c1, c2);
35788                     this.c1 = c1; this.c2 = c2;
35789                 }
35790             }else{
35791                 // this changes non-leafs into leafs if they have no children.
35792                 // it's not very rational behaviour..
35793                 
35794                 if(!this.wasLeaf && this.node.leaf){
35795                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35796                     delete this.c1;
35797                     delete this.c2;
35798                     this.wasLeaf = true;
35799                 }
35800             }
35801             var ecc = "x-tree-ec-icon "+cls;
35802             if(this.ecc != ecc){
35803                 this.ecNode.className = ecc;
35804                 this.ecc = ecc;
35805             }
35806         }
35807     },
35808
35809     getChildIndent : function(){
35810         if(!this.childIndent){
35811             var buf = [];
35812             var p = this.node;
35813             while(p){
35814                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35815                     if(!p.isLast()) {
35816                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35817                     } else {
35818                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35819                     }
35820                 }
35821                 p = p.parentNode;
35822             }
35823             this.childIndent = buf.join("");
35824         }
35825         return this.childIndent;
35826     },
35827
35828     renderIndent : function(){
35829         if(this.rendered){
35830             var indent = "";
35831             var p = this.node.parentNode;
35832             if(p){
35833                 indent = p.ui.getChildIndent();
35834             }
35835             if(this.indentMarkup != indent){ // don't rerender if not required
35836                 this.indentNode.innerHTML = indent;
35837                 this.indentMarkup = indent;
35838             }
35839             this.updateExpandIcon();
35840         }
35841     }
35842 };
35843
35844 Roo.tree.RootTreeNodeUI = function(){
35845     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35846 };
35847 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35848     render : function(){
35849         if(!this.rendered){
35850             var targetNode = this.node.ownerTree.innerCt.dom;
35851             this.node.expanded = true;
35852             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35853             this.wrap = this.ctNode = targetNode.firstChild;
35854         }
35855     },
35856     collapse : function(){
35857     },
35858     expand : function(){
35859     }
35860 });/*
35861  * Based on:
35862  * Ext JS Library 1.1.1
35863  * Copyright(c) 2006-2007, Ext JS, LLC.
35864  *
35865  * Originally Released Under LGPL - original licence link has changed is not relivant.
35866  *
35867  * Fork - LGPL
35868  * <script type="text/javascript">
35869  */
35870 /**
35871  * @class Roo.tree.TreeLoader
35872  * @extends Roo.util.Observable
35873  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35874  * nodes from a specified URL. The response must be a javascript Array definition
35875  * who's elements are node definition objects. eg:
35876  * <pre><code>
35877 {  success : true,
35878    data :      [
35879    
35880     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35881     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35882     ]
35883 }
35884
35885
35886 </code></pre>
35887  * <br><br>
35888  * The old style respose with just an array is still supported, but not recommended.
35889  * <br><br>
35890  *
35891  * A server request is sent, and child nodes are loaded only when a node is expanded.
35892  * The loading node's id is passed to the server under the parameter name "node" to
35893  * enable the server to produce the correct child nodes.
35894  * <br><br>
35895  * To pass extra parameters, an event handler may be attached to the "beforeload"
35896  * event, and the parameters specified in the TreeLoader's baseParams property:
35897  * <pre><code>
35898     myTreeLoader.on("beforeload", function(treeLoader, node) {
35899         this.baseParams.category = node.attributes.category;
35900     }, this);
35901 </code></pre><
35902  * This would pass an HTTP parameter called "category" to the server containing
35903  * the value of the Node's "category" attribute.
35904  * @constructor
35905  * Creates a new Treeloader.
35906  * @param {Object} config A config object containing config properties.
35907  */
35908 Roo.tree.TreeLoader = function(config){
35909     this.baseParams = {};
35910     this.requestMethod = "POST";
35911     Roo.apply(this, config);
35912
35913     this.addEvents({
35914     
35915         /**
35916          * @event beforeload
35917          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35918          * @param {Object} This TreeLoader object.
35919          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35920          * @param {Object} callback The callback function specified in the {@link #load} call.
35921          */
35922         beforeload : true,
35923         /**
35924          * @event load
35925          * Fires when the node has been successfuly loaded.
35926          * @param {Object} This TreeLoader object.
35927          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35928          * @param {Object} response The response object containing the data from the server.
35929          */
35930         load : true,
35931         /**
35932          * @event loadexception
35933          * Fires if the network request failed.
35934          * @param {Object} This TreeLoader object.
35935          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35936          * @param {Object} response The response object containing the data from the server.
35937          */
35938         loadexception : true,
35939         /**
35940          * @event create
35941          * Fires before a node is created, enabling you to return custom Node types 
35942          * @param {Object} This TreeLoader object.
35943          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35944          */
35945         create : true
35946     });
35947
35948     Roo.tree.TreeLoader.superclass.constructor.call(this);
35949 };
35950
35951 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35952     /**
35953     * @cfg {String} dataUrl The URL from which to request a Json string which
35954     * specifies an array of node definition object representing the child nodes
35955     * to be loaded.
35956     */
35957     /**
35958     * @cfg {String} requestMethod either GET or POST
35959     * defaults to POST (due to BC)
35960     * to be loaded.
35961     */
35962     /**
35963     * @cfg {Object} baseParams (optional) An object containing properties which
35964     * specify HTTP parameters to be passed to each request for child nodes.
35965     */
35966     /**
35967     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35968     * created by this loader. If the attributes sent by the server have an attribute in this object,
35969     * they take priority.
35970     */
35971     /**
35972     * @cfg {Object} uiProviders (optional) An object containing properties which
35973     * 
35974     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35975     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35976     * <i>uiProvider</i> attribute of a returned child node is a string rather
35977     * than a reference to a TreeNodeUI implementation, this that string value
35978     * is used as a property name in the uiProviders object. You can define the provider named
35979     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35980     */
35981     uiProviders : {},
35982
35983     /**
35984     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35985     * child nodes before loading.
35986     */
35987     clearOnLoad : true,
35988
35989     /**
35990     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35991     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35992     * Grid query { data : [ .....] }
35993     */
35994     
35995     root : false,
35996      /**
35997     * @cfg {String} queryParam (optional) 
35998     * Name of the query as it will be passed on the querystring (defaults to 'node')
35999     * eg. the request will be ?node=[id]
36000     */
36001     
36002     
36003     queryParam: false,
36004     
36005     /**
36006      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36007      * This is called automatically when a node is expanded, but may be used to reload
36008      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36009      * @param {Roo.tree.TreeNode} node
36010      * @param {Function} callback
36011      */
36012     load : function(node, callback){
36013         if(this.clearOnLoad){
36014             while(node.firstChild){
36015                 node.removeChild(node.firstChild);
36016             }
36017         }
36018         if(node.attributes.children){ // preloaded json children
36019             var cs = node.attributes.children;
36020             for(var i = 0, len = cs.length; i < len; i++){
36021                 node.appendChild(this.createNode(cs[i]));
36022             }
36023             if(typeof callback == "function"){
36024                 callback();
36025             }
36026         }else if(this.dataUrl){
36027             this.requestData(node, callback);
36028         }
36029     },
36030
36031     getParams: function(node){
36032         var buf = [], bp = this.baseParams;
36033         for(var key in bp){
36034             if(typeof bp[key] != "function"){
36035                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36036             }
36037         }
36038         var n = this.queryParam === false ? 'node' : this.queryParam;
36039         buf.push(n + "=", encodeURIComponent(node.id));
36040         return buf.join("");
36041     },
36042
36043     requestData : function(node, callback){
36044         if(this.fireEvent("beforeload", this, node, callback) !== false){
36045             this.transId = Roo.Ajax.request({
36046                 method:this.requestMethod,
36047                 url: this.dataUrl||this.url,
36048                 success: this.handleResponse,
36049                 failure: this.handleFailure,
36050                 scope: this,
36051                 argument: {callback: callback, node: node},
36052                 params: this.getParams(node)
36053             });
36054         }else{
36055             // if the load is cancelled, make sure we notify
36056             // the node that we are done
36057             if(typeof callback == "function"){
36058                 callback();
36059             }
36060         }
36061     },
36062
36063     isLoading : function(){
36064         return this.transId ? true : false;
36065     },
36066
36067     abort : function(){
36068         if(this.isLoading()){
36069             Roo.Ajax.abort(this.transId);
36070         }
36071     },
36072
36073     // private
36074     createNode : function(attr)
36075     {
36076         // apply baseAttrs, nice idea Corey!
36077         if(this.baseAttrs){
36078             Roo.applyIf(attr, this.baseAttrs);
36079         }
36080         if(this.applyLoader !== false){
36081             attr.loader = this;
36082         }
36083         // uiProvider = depreciated..
36084         
36085         if(typeof(attr.uiProvider) == 'string'){
36086            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36087                 /**  eval:var:attr */ eval(attr.uiProvider);
36088         }
36089         if(typeof(this.uiProviders['default']) != 'undefined') {
36090             attr.uiProvider = this.uiProviders['default'];
36091         }
36092         
36093         this.fireEvent('create', this, attr);
36094         
36095         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36096         return(attr.leaf ?
36097                         new Roo.tree.TreeNode(attr) :
36098                         new Roo.tree.AsyncTreeNode(attr));
36099     },
36100
36101     processResponse : function(response, node, callback)
36102     {
36103         var json = response.responseText;
36104         try {
36105             
36106             var o = Roo.decode(json);
36107             
36108             if (this.root === false && typeof(o.success) != undefined) {
36109                 this.root = 'data'; // the default behaviour for list like data..
36110                 }
36111                 
36112             if (this.root !== false &&  !o.success) {
36113                 // it's a failure condition.
36114                 var a = response.argument;
36115                 this.fireEvent("loadexception", this, a.node, response);
36116                 Roo.log("Load failed - should have a handler really");
36117                 return;
36118             }
36119             
36120             
36121             
36122             if (this.root !== false) {
36123                  o = o[this.root];
36124             }
36125             
36126             for(var i = 0, len = o.length; i < len; i++){
36127                 var n = this.createNode(o[i]);
36128                 if(n){
36129                     node.appendChild(n);
36130                 }
36131             }
36132             if(typeof callback == "function"){
36133                 callback(this, node);
36134             }
36135         }catch(e){
36136             this.handleFailure(response);
36137         }
36138     },
36139
36140     handleResponse : function(response){
36141         this.transId = false;
36142         var a = response.argument;
36143         this.processResponse(response, a.node, a.callback);
36144         this.fireEvent("load", this, a.node, response);
36145     },
36146
36147     handleFailure : function(response)
36148     {
36149         // should handle failure better..
36150         this.transId = false;
36151         var a = response.argument;
36152         this.fireEvent("loadexception", this, a.node, response);
36153         if(typeof a.callback == "function"){
36154             a.callback(this, a.node);
36155         }
36156     }
36157 });/*
36158  * Based on:
36159  * Ext JS Library 1.1.1
36160  * Copyright(c) 2006-2007, Ext JS, LLC.
36161  *
36162  * Originally Released Under LGPL - original licence link has changed is not relivant.
36163  *
36164  * Fork - LGPL
36165  * <script type="text/javascript">
36166  */
36167
36168 /**
36169 * @class Roo.tree.TreeFilter
36170 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36171 * @param {TreePanel} tree
36172 * @param {Object} config (optional)
36173  */
36174 Roo.tree.TreeFilter = function(tree, config){
36175     this.tree = tree;
36176     this.filtered = {};
36177     Roo.apply(this, config);
36178 };
36179
36180 Roo.tree.TreeFilter.prototype = {
36181     clearBlank:false,
36182     reverse:false,
36183     autoClear:false,
36184     remove:false,
36185
36186      /**
36187      * Filter the data by a specific attribute.
36188      * @param {String/RegExp} value Either string that the attribute value
36189      * should start with or a RegExp to test against the attribute
36190      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36191      * @param {TreeNode} startNode (optional) The node to start the filter at.
36192      */
36193     filter : function(value, attr, startNode){
36194         attr = attr || "text";
36195         var f;
36196         if(typeof value == "string"){
36197             var vlen = value.length;
36198             // auto clear empty filter
36199             if(vlen == 0 && this.clearBlank){
36200                 this.clear();
36201                 return;
36202             }
36203             value = value.toLowerCase();
36204             f = function(n){
36205                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36206             };
36207         }else if(value.exec){ // regex?
36208             f = function(n){
36209                 return value.test(n.attributes[attr]);
36210             };
36211         }else{
36212             throw 'Illegal filter type, must be string or regex';
36213         }
36214         this.filterBy(f, null, startNode);
36215         },
36216
36217     /**
36218      * Filter by a function. The passed function will be called with each
36219      * node in the tree (or from the startNode). If the function returns true, the node is kept
36220      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36221      * @param {Function} fn The filter function
36222      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36223      */
36224     filterBy : function(fn, scope, startNode){
36225         startNode = startNode || this.tree.root;
36226         if(this.autoClear){
36227             this.clear();
36228         }
36229         var af = this.filtered, rv = this.reverse;
36230         var f = function(n){
36231             if(n == startNode){
36232                 return true;
36233             }
36234             if(af[n.id]){
36235                 return false;
36236             }
36237             var m = fn.call(scope || n, n);
36238             if(!m || rv){
36239                 af[n.id] = n;
36240                 n.ui.hide();
36241                 return false;
36242             }
36243             return true;
36244         };
36245         startNode.cascade(f);
36246         if(this.remove){
36247            for(var id in af){
36248                if(typeof id != "function"){
36249                    var n = af[id];
36250                    if(n && n.parentNode){
36251                        n.parentNode.removeChild(n);
36252                    }
36253                }
36254            }
36255         }
36256     },
36257
36258     /**
36259      * Clears the current filter. Note: with the "remove" option
36260      * set a filter cannot be cleared.
36261      */
36262     clear : function(){
36263         var t = this.tree;
36264         var af = this.filtered;
36265         for(var id in af){
36266             if(typeof id != "function"){
36267                 var n = af[id];
36268                 if(n){
36269                     n.ui.show();
36270                 }
36271             }
36272         }
36273         this.filtered = {};
36274     }
36275 };
36276 /*
36277  * Based on:
36278  * Ext JS Library 1.1.1
36279  * Copyright(c) 2006-2007, Ext JS, LLC.
36280  *
36281  * Originally Released Under LGPL - original licence link has changed is not relivant.
36282  *
36283  * Fork - LGPL
36284  * <script type="text/javascript">
36285  */
36286  
36287
36288 /**
36289  * @class Roo.tree.TreeSorter
36290  * Provides sorting of nodes in a TreePanel
36291  * 
36292  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36293  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36294  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36295  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36296  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36297  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36298  * @constructor
36299  * @param {TreePanel} tree
36300  * @param {Object} config
36301  */
36302 Roo.tree.TreeSorter = function(tree, config){
36303     Roo.apply(this, config);
36304     tree.on("beforechildrenrendered", this.doSort, this);
36305     tree.on("append", this.updateSort, this);
36306     tree.on("insert", this.updateSort, this);
36307     
36308     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36309     var p = this.property || "text";
36310     var sortType = this.sortType;
36311     var fs = this.folderSort;
36312     var cs = this.caseSensitive === true;
36313     var leafAttr = this.leafAttr || 'leaf';
36314
36315     this.sortFn = function(n1, n2){
36316         if(fs){
36317             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36318                 return 1;
36319             }
36320             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36321                 return -1;
36322             }
36323         }
36324         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36325         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36326         if(v1 < v2){
36327                         return dsc ? +1 : -1;
36328                 }else if(v1 > v2){
36329                         return dsc ? -1 : +1;
36330         }else{
36331                 return 0;
36332         }
36333     };
36334 };
36335
36336 Roo.tree.TreeSorter.prototype = {
36337     doSort : function(node){
36338         node.sort(this.sortFn);
36339     },
36340     
36341     compareNodes : function(n1, n2){
36342         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36343     },
36344     
36345     updateSort : function(tree, node){
36346         if(node.childrenRendered){
36347             this.doSort.defer(1, this, [node]);
36348         }
36349     }
36350 };/*
36351  * Based on:
36352  * Ext JS Library 1.1.1
36353  * Copyright(c) 2006-2007, Ext JS, LLC.
36354  *
36355  * Originally Released Under LGPL - original licence link has changed is not relivant.
36356  *
36357  * Fork - LGPL
36358  * <script type="text/javascript">
36359  */
36360
36361 if(Roo.dd.DropZone){
36362     
36363 Roo.tree.TreeDropZone = function(tree, config){
36364     this.allowParentInsert = false;
36365     this.allowContainerDrop = false;
36366     this.appendOnly = false;
36367     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36368     this.tree = tree;
36369     this.lastInsertClass = "x-tree-no-status";
36370     this.dragOverData = {};
36371 };
36372
36373 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36374     ddGroup : "TreeDD",
36375     scroll:  true,
36376     
36377     expandDelay : 1000,
36378     
36379     expandNode : function(node){
36380         if(node.hasChildNodes() && !node.isExpanded()){
36381             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36382         }
36383     },
36384     
36385     queueExpand : function(node){
36386         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36387     },
36388     
36389     cancelExpand : function(){
36390         if(this.expandProcId){
36391             clearTimeout(this.expandProcId);
36392             this.expandProcId = false;
36393         }
36394     },
36395     
36396     isValidDropPoint : function(n, pt, dd, e, data){
36397         if(!n || !data){ return false; }
36398         var targetNode = n.node;
36399         var dropNode = data.node;
36400         // default drop rules
36401         if(!(targetNode && targetNode.isTarget && pt)){
36402             return false;
36403         }
36404         if(pt == "append" && targetNode.allowChildren === false){
36405             return false;
36406         }
36407         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36408             return false;
36409         }
36410         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36411             return false;
36412         }
36413         // reuse the object
36414         var overEvent = this.dragOverData;
36415         overEvent.tree = this.tree;
36416         overEvent.target = targetNode;
36417         overEvent.data = data;
36418         overEvent.point = pt;
36419         overEvent.source = dd;
36420         overEvent.rawEvent = e;
36421         overEvent.dropNode = dropNode;
36422         overEvent.cancel = false;  
36423         var result = this.tree.fireEvent("nodedragover", overEvent);
36424         return overEvent.cancel === false && result !== false;
36425     },
36426     
36427     getDropPoint : function(e, n, dd)
36428     {
36429         var tn = n.node;
36430         if(tn.isRoot){
36431             return tn.allowChildren !== false ? "append" : false; // always append for root
36432         }
36433         var dragEl = n.ddel;
36434         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36435         var y = Roo.lib.Event.getPageY(e);
36436         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36437         
36438         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36439         var noAppend = tn.allowChildren === false;
36440         if(this.appendOnly || tn.parentNode.allowChildren === false){
36441             return noAppend ? false : "append";
36442         }
36443         var noBelow = false;
36444         if(!this.allowParentInsert){
36445             noBelow = tn.hasChildNodes() && tn.isExpanded();
36446         }
36447         var q = (b - t) / (noAppend ? 2 : 3);
36448         if(y >= t && y < (t + q)){
36449             return "above";
36450         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36451             return "below";
36452         }else{
36453             return "append";
36454         }
36455     },
36456     
36457     onNodeEnter : function(n, dd, e, data)
36458     {
36459         this.cancelExpand();
36460     },
36461     
36462     onNodeOver : function(n, dd, e, data)
36463     {
36464        
36465         var pt = this.getDropPoint(e, n, dd);
36466         var node = n.node;
36467         
36468         // auto node expand check
36469         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36470             this.queueExpand(node);
36471         }else if(pt != "append"){
36472             this.cancelExpand();
36473         }
36474         
36475         // set the insert point style on the target node
36476         var returnCls = this.dropNotAllowed;
36477         if(this.isValidDropPoint(n, pt, dd, e, data)){
36478            if(pt){
36479                var el = n.ddel;
36480                var cls;
36481                if(pt == "above"){
36482                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36483                    cls = "x-tree-drag-insert-above";
36484                }else if(pt == "below"){
36485                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36486                    cls = "x-tree-drag-insert-below";
36487                }else{
36488                    returnCls = "x-tree-drop-ok-append";
36489                    cls = "x-tree-drag-append";
36490                }
36491                if(this.lastInsertClass != cls){
36492                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36493                    this.lastInsertClass = cls;
36494                }
36495            }
36496        }
36497        return returnCls;
36498     },
36499     
36500     onNodeOut : function(n, dd, e, data){
36501         
36502         this.cancelExpand();
36503         this.removeDropIndicators(n);
36504     },
36505     
36506     onNodeDrop : function(n, dd, e, data){
36507         var point = this.getDropPoint(e, n, dd);
36508         var targetNode = n.node;
36509         targetNode.ui.startDrop();
36510         if(!this.isValidDropPoint(n, point, dd, e, data)){
36511             targetNode.ui.endDrop();
36512             return false;
36513         }
36514         // first try to find the drop node
36515         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36516         var dropEvent = {
36517             tree : this.tree,
36518             target: targetNode,
36519             data: data,
36520             point: point,
36521             source: dd,
36522             rawEvent: e,
36523             dropNode: dropNode,
36524             cancel: !dropNode   
36525         };
36526         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36527         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36528             targetNode.ui.endDrop();
36529             return false;
36530         }
36531         // allow target changing
36532         targetNode = dropEvent.target;
36533         if(point == "append" && !targetNode.isExpanded()){
36534             targetNode.expand(false, null, function(){
36535                 this.completeDrop(dropEvent);
36536             }.createDelegate(this));
36537         }else{
36538             this.completeDrop(dropEvent);
36539         }
36540         return true;
36541     },
36542     
36543     completeDrop : function(de){
36544         var ns = de.dropNode, p = de.point, t = de.target;
36545         if(!(ns instanceof Array)){
36546             ns = [ns];
36547         }
36548         var n;
36549         for(var i = 0, len = ns.length; i < len; i++){
36550             n = ns[i];
36551             if(p == "above"){
36552                 t.parentNode.insertBefore(n, t);
36553             }else if(p == "below"){
36554                 t.parentNode.insertBefore(n, t.nextSibling);
36555             }else{
36556                 t.appendChild(n);
36557             }
36558         }
36559         n.ui.focus();
36560         if(this.tree.hlDrop){
36561             n.ui.highlight();
36562         }
36563         t.ui.endDrop();
36564         this.tree.fireEvent("nodedrop", de);
36565     },
36566     
36567     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36568         if(this.tree.hlDrop){
36569             dropNode.ui.focus();
36570             dropNode.ui.highlight();
36571         }
36572         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36573     },
36574     
36575     getTree : function(){
36576         return this.tree;
36577     },
36578     
36579     removeDropIndicators : function(n){
36580         if(n && n.ddel){
36581             var el = n.ddel;
36582             Roo.fly(el).removeClass([
36583                     "x-tree-drag-insert-above",
36584                     "x-tree-drag-insert-below",
36585                     "x-tree-drag-append"]);
36586             this.lastInsertClass = "_noclass";
36587         }
36588     },
36589     
36590     beforeDragDrop : function(target, e, id){
36591         this.cancelExpand();
36592         return true;
36593     },
36594     
36595     afterRepair : function(data){
36596         if(data && Roo.enableFx){
36597             data.node.ui.highlight();
36598         }
36599         this.hideProxy();
36600     } 
36601     
36602 });
36603
36604 }
36605 /*
36606  * Based on:
36607  * Ext JS Library 1.1.1
36608  * Copyright(c) 2006-2007, Ext JS, LLC.
36609  *
36610  * Originally Released Under LGPL - original licence link has changed is not relivant.
36611  *
36612  * Fork - LGPL
36613  * <script type="text/javascript">
36614  */
36615  
36616
36617 if(Roo.dd.DragZone){
36618 Roo.tree.TreeDragZone = function(tree, config){
36619     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36620     this.tree = tree;
36621 };
36622
36623 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36624     ddGroup : "TreeDD",
36625    
36626     onBeforeDrag : function(data, e){
36627         var n = data.node;
36628         return n && n.draggable && !n.disabled;
36629     },
36630      
36631     
36632     onInitDrag : function(e){
36633         var data = this.dragData;
36634         this.tree.getSelectionModel().select(data.node);
36635         this.proxy.update("");
36636         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36637         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36638     },
36639     
36640     getRepairXY : function(e, data){
36641         return data.node.ui.getDDRepairXY();
36642     },
36643     
36644     onEndDrag : function(data, e){
36645         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36646         
36647         
36648     },
36649     
36650     onValidDrop : function(dd, e, id){
36651         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36652         this.hideProxy();
36653     },
36654     
36655     beforeInvalidDrop : function(e, id){
36656         // this scrolls the original position back into view
36657         var sm = this.tree.getSelectionModel();
36658         sm.clearSelections();
36659         sm.select(this.dragData.node);
36660     }
36661 });
36662 }/*
36663  * Based on:
36664  * Ext JS Library 1.1.1
36665  * Copyright(c) 2006-2007, Ext JS, LLC.
36666  *
36667  * Originally Released Under LGPL - original licence link has changed is not relivant.
36668  *
36669  * Fork - LGPL
36670  * <script type="text/javascript">
36671  */
36672 /**
36673  * @class Roo.tree.TreeEditor
36674  * @extends Roo.Editor
36675  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36676  * as the editor field.
36677  * @constructor
36678  * @param {Object} config (used to be the tree panel.)
36679  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36680  * 
36681  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36682  * @cfg {Roo.form.TextField|Object} field The field configuration
36683  *
36684  * 
36685  */
36686 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36687     var tree = config;
36688     var field;
36689     if (oldconfig) { // old style..
36690         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36691     } else {
36692         // new style..
36693         tree = config.tree;
36694         config.field = config.field  || {};
36695         config.field.xtype = 'TextField';
36696         field = Roo.factory(config.field, Roo.form);
36697     }
36698     config = config || {};
36699     
36700     
36701     this.addEvents({
36702         /**
36703          * @event beforenodeedit
36704          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36705          * false from the handler of this event.
36706          * @param {Editor} this
36707          * @param {Roo.tree.Node} node 
36708          */
36709         "beforenodeedit" : true
36710     });
36711     
36712     //Roo.log(config);
36713     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36714
36715     this.tree = tree;
36716
36717     tree.on('beforeclick', this.beforeNodeClick, this);
36718     tree.getTreeEl().on('mousedown', this.hide, this);
36719     this.on('complete', this.updateNode, this);
36720     this.on('beforestartedit', this.fitToTree, this);
36721     this.on('startedit', this.bindScroll, this, {delay:10});
36722     this.on('specialkey', this.onSpecialKey, this);
36723 };
36724
36725 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36726     /**
36727      * @cfg {String} alignment
36728      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36729      */
36730     alignment: "l-l",
36731     // inherit
36732     autoSize: false,
36733     /**
36734      * @cfg {Boolean} hideEl
36735      * True to hide the bound element while the editor is displayed (defaults to false)
36736      */
36737     hideEl : false,
36738     /**
36739      * @cfg {String} cls
36740      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36741      */
36742     cls: "x-small-editor x-tree-editor",
36743     /**
36744      * @cfg {Boolean} shim
36745      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36746      */
36747     shim:false,
36748     // inherit
36749     shadow:"frame",
36750     /**
36751      * @cfg {Number} maxWidth
36752      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36753      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36754      * scroll and client offsets into account prior to each edit.
36755      */
36756     maxWidth: 250,
36757
36758     editDelay : 350,
36759
36760     // private
36761     fitToTree : function(ed, el){
36762         var td = this.tree.getTreeEl().dom, nd = el.dom;
36763         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36764             td.scrollLeft = nd.offsetLeft;
36765         }
36766         var w = Math.min(
36767                 this.maxWidth,
36768                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36769         this.setSize(w, '');
36770         
36771         return this.fireEvent('beforenodeedit', this, this.editNode);
36772         
36773     },
36774
36775     // private
36776     triggerEdit : function(node){
36777         this.completeEdit();
36778         this.editNode = node;
36779         this.startEdit(node.ui.textNode, node.text);
36780     },
36781
36782     // private
36783     bindScroll : function(){
36784         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36785     },
36786
36787     // private
36788     beforeNodeClick : function(node, e){
36789         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36790         this.lastClick = new Date();
36791         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36792             e.stopEvent();
36793             this.triggerEdit(node);
36794             return false;
36795         }
36796         return true;
36797     },
36798
36799     // private
36800     updateNode : function(ed, value){
36801         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36802         this.editNode.setText(value);
36803     },
36804
36805     // private
36806     onHide : function(){
36807         Roo.tree.TreeEditor.superclass.onHide.call(this);
36808         if(this.editNode){
36809             this.editNode.ui.focus();
36810         }
36811     },
36812
36813     // private
36814     onSpecialKey : function(field, e){
36815         var k = e.getKey();
36816         if(k == e.ESC){
36817             e.stopEvent();
36818             this.cancelEdit();
36819         }else if(k == e.ENTER && !e.hasModifier()){
36820             e.stopEvent();
36821             this.completeEdit();
36822         }
36823     }
36824 });//<Script type="text/javascript">
36825 /*
36826  * Based on:
36827  * Ext JS Library 1.1.1
36828  * Copyright(c) 2006-2007, Ext JS, LLC.
36829  *
36830  * Originally Released Under LGPL - original licence link has changed is not relivant.
36831  *
36832  * Fork - LGPL
36833  * <script type="text/javascript">
36834  */
36835  
36836 /**
36837  * Not documented??? - probably should be...
36838  */
36839
36840 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36841     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36842     
36843     renderElements : function(n, a, targetNode, bulkRender){
36844         //consel.log("renderElements?");
36845         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36846
36847         var t = n.getOwnerTree();
36848         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36849         
36850         var cols = t.columns;
36851         var bw = t.borderWidth;
36852         var c = cols[0];
36853         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36854          var cb = typeof a.checked == "boolean";
36855         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36856         var colcls = 'x-t-' + tid + '-c0';
36857         var buf = [
36858             '<li class="x-tree-node">',
36859             
36860                 
36861                 '<div class="x-tree-node-el ', a.cls,'">',
36862                     // extran...
36863                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36864                 
36865                 
36866                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36867                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36868                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36869                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36870                            (a.iconCls ? ' '+a.iconCls : ''),
36871                            '" unselectable="on" />',
36872                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36873                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36874                              
36875                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36876                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36877                             '<span unselectable="on" qtip="' + tx + '">',
36878                              tx,
36879                              '</span></a>' ,
36880                     '</div>',
36881                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36882                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36883                  ];
36884         for(var i = 1, len = cols.length; i < len; i++){
36885             c = cols[i];
36886             colcls = 'x-t-' + tid + '-c' +i;
36887             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36888             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36889                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36890                       "</div>");
36891          }
36892          
36893          buf.push(
36894             '</a>',
36895             '<div class="x-clear"></div></div>',
36896             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36897             "</li>");
36898         
36899         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36900             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36901                                 n.nextSibling.ui.getEl(), buf.join(""));
36902         }else{
36903             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36904         }
36905         var el = this.wrap.firstChild;
36906         this.elRow = el;
36907         this.elNode = el.firstChild;
36908         this.ranchor = el.childNodes[1];
36909         this.ctNode = this.wrap.childNodes[1];
36910         var cs = el.firstChild.childNodes;
36911         this.indentNode = cs[0];
36912         this.ecNode = cs[1];
36913         this.iconNode = cs[2];
36914         var index = 3;
36915         if(cb){
36916             this.checkbox = cs[3];
36917             index++;
36918         }
36919         this.anchor = cs[index];
36920         
36921         this.textNode = cs[index].firstChild;
36922         
36923         //el.on("click", this.onClick, this);
36924         //el.on("dblclick", this.onDblClick, this);
36925         
36926         
36927        // console.log(this);
36928     },
36929     initEvents : function(){
36930         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36931         
36932             
36933         var a = this.ranchor;
36934
36935         var el = Roo.get(a);
36936
36937         if(Roo.isOpera){ // opera render bug ignores the CSS
36938             el.setStyle("text-decoration", "none");
36939         }
36940
36941         el.on("click", this.onClick, this);
36942         el.on("dblclick", this.onDblClick, this);
36943         el.on("contextmenu", this.onContextMenu, this);
36944         
36945     },
36946     
36947     /*onSelectedChange : function(state){
36948         if(state){
36949             this.focus();
36950             this.addClass("x-tree-selected");
36951         }else{
36952             //this.blur();
36953             this.removeClass("x-tree-selected");
36954         }
36955     },*/
36956     addClass : function(cls){
36957         if(this.elRow){
36958             Roo.fly(this.elRow).addClass(cls);
36959         }
36960         
36961     },
36962     
36963     
36964     removeClass : function(cls){
36965         if(this.elRow){
36966             Roo.fly(this.elRow).removeClass(cls);
36967         }
36968     }
36969
36970     
36971     
36972 });//<Script type="text/javascript">
36973
36974 /*
36975  * Based on:
36976  * Ext JS Library 1.1.1
36977  * Copyright(c) 2006-2007, Ext JS, LLC.
36978  *
36979  * Originally Released Under LGPL - original licence link has changed is not relivant.
36980  *
36981  * Fork - LGPL
36982  * <script type="text/javascript">
36983  */
36984  
36985
36986 /**
36987  * @class Roo.tree.ColumnTree
36988  * @extends Roo.data.TreePanel
36989  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36990  * @cfg {int} borderWidth  compined right/left border allowance
36991  * @constructor
36992  * @param {String/HTMLElement/Element} el The container element
36993  * @param {Object} config
36994  */
36995 Roo.tree.ColumnTree =  function(el, config)
36996 {
36997    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36998    this.addEvents({
36999         /**
37000         * @event resize
37001         * Fire this event on a container when it resizes
37002         * @param {int} w Width
37003         * @param {int} h Height
37004         */
37005        "resize" : true
37006     });
37007     this.on('resize', this.onResize, this);
37008 };
37009
37010 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37011     //lines:false,
37012     
37013     
37014     borderWidth: Roo.isBorderBox ? 0 : 2, 
37015     headEls : false,
37016     
37017     render : function(){
37018         // add the header.....
37019        
37020         Roo.tree.ColumnTree.superclass.render.apply(this);
37021         
37022         this.el.addClass('x-column-tree');
37023         
37024         this.headers = this.el.createChild(
37025             {cls:'x-tree-headers'},this.innerCt.dom);
37026    
37027         var cols = this.columns, c;
37028         var totalWidth = 0;
37029         this.headEls = [];
37030         var  len = cols.length;
37031         for(var i = 0; i < len; i++){
37032              c = cols[i];
37033              totalWidth += c.width;
37034             this.headEls.push(this.headers.createChild({
37035                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37036                  cn: {
37037                      cls:'x-tree-hd-text',
37038                      html: c.header
37039                  },
37040                  style:'width:'+(c.width-this.borderWidth)+'px;'
37041              }));
37042         }
37043         this.headers.createChild({cls:'x-clear'});
37044         // prevent floats from wrapping when clipped
37045         this.headers.setWidth(totalWidth);
37046         //this.innerCt.setWidth(totalWidth);
37047         this.innerCt.setStyle({ overflow: 'auto' });
37048         this.onResize(this.width, this.height);
37049              
37050         
37051     },
37052     onResize : function(w,h)
37053     {
37054         this.height = h;
37055         this.width = w;
37056         // resize cols..
37057         this.innerCt.setWidth(this.width);
37058         this.innerCt.setHeight(this.height-20);
37059         
37060         // headers...
37061         var cols = this.columns, c;
37062         var totalWidth = 0;
37063         var expEl = false;
37064         var len = cols.length;
37065         for(var i = 0; i < len; i++){
37066             c = cols[i];
37067             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37068                 // it's the expander..
37069                 expEl  = this.headEls[i];
37070                 continue;
37071             }
37072             totalWidth += c.width;
37073             
37074         }
37075         if (expEl) {
37076             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37077         }
37078         this.headers.setWidth(w-20);
37079
37080         
37081         
37082         
37083     }
37084 });
37085 /*
37086  * Based on:
37087  * Ext JS Library 1.1.1
37088  * Copyright(c) 2006-2007, Ext JS, LLC.
37089  *
37090  * Originally Released Under LGPL - original licence link has changed is not relivant.
37091  *
37092  * Fork - LGPL
37093  * <script type="text/javascript">
37094  */
37095  
37096 /**
37097  * @class Roo.menu.Menu
37098  * @extends Roo.util.Observable
37099  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37100  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37101  * @constructor
37102  * Creates a new Menu
37103  * @param {Object} config Configuration options
37104  */
37105 Roo.menu.Menu = function(config){
37106     Roo.apply(this, config);
37107     this.id = this.id || Roo.id();
37108     this.addEvents({
37109         /**
37110          * @event beforeshow
37111          * Fires before this menu is displayed
37112          * @param {Roo.menu.Menu} this
37113          */
37114         beforeshow : true,
37115         /**
37116          * @event beforehide
37117          * Fires before this menu is hidden
37118          * @param {Roo.menu.Menu} this
37119          */
37120         beforehide : true,
37121         /**
37122          * @event show
37123          * Fires after this menu is displayed
37124          * @param {Roo.menu.Menu} this
37125          */
37126         show : true,
37127         /**
37128          * @event hide
37129          * Fires after this menu is hidden
37130          * @param {Roo.menu.Menu} this
37131          */
37132         hide : true,
37133         /**
37134          * @event click
37135          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37136          * @param {Roo.menu.Menu} this
37137          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37138          * @param {Roo.EventObject} e
37139          */
37140         click : true,
37141         /**
37142          * @event mouseover
37143          * Fires when the mouse is hovering over this menu
37144          * @param {Roo.menu.Menu} this
37145          * @param {Roo.EventObject} e
37146          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37147          */
37148         mouseover : true,
37149         /**
37150          * @event mouseout
37151          * Fires when the mouse exits this menu
37152          * @param {Roo.menu.Menu} this
37153          * @param {Roo.EventObject} e
37154          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37155          */
37156         mouseout : true,
37157         /**
37158          * @event itemclick
37159          * Fires when a menu item contained in this menu is clicked
37160          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37161          * @param {Roo.EventObject} e
37162          */
37163         itemclick: true
37164     });
37165     if (this.registerMenu) {
37166         Roo.menu.MenuMgr.register(this);
37167     }
37168     
37169     var mis = this.items;
37170     this.items = new Roo.util.MixedCollection();
37171     if(mis){
37172         this.add.apply(this, mis);
37173     }
37174 };
37175
37176 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37177     /**
37178      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37179      */
37180     minWidth : 120,
37181     /**
37182      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37183      * for bottom-right shadow (defaults to "sides")
37184      */
37185     shadow : "sides",
37186     /**
37187      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37188      * this menu (defaults to "tl-tr?")
37189      */
37190     subMenuAlign : "tl-tr?",
37191     /**
37192      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37193      * relative to its element of origin (defaults to "tl-bl?")
37194      */
37195     defaultAlign : "tl-bl?",
37196     /**
37197      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37198      */
37199     allowOtherMenus : false,
37200     /**
37201      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37202      */
37203     registerMenu : true,
37204
37205     hidden:true,
37206
37207     // private
37208     render : function(){
37209         if(this.el){
37210             return;
37211         }
37212         var el = this.el = new Roo.Layer({
37213             cls: "x-menu",
37214             shadow:this.shadow,
37215             constrain: false,
37216             parentEl: this.parentEl || document.body,
37217             zindex:15000
37218         });
37219
37220         this.keyNav = new Roo.menu.MenuNav(this);
37221
37222         if(this.plain){
37223             el.addClass("x-menu-plain");
37224         }
37225         if(this.cls){
37226             el.addClass(this.cls);
37227         }
37228         // generic focus element
37229         this.focusEl = el.createChild({
37230             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37231         });
37232         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37233         //disabling touch- as it's causing issues ..
37234         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37235         ul.on('click'   , this.onClick, this);
37236         
37237         
37238         ul.on("mouseover", this.onMouseOver, this);
37239         ul.on("mouseout", this.onMouseOut, this);
37240         this.items.each(function(item){
37241             if (item.hidden) {
37242                 return;
37243             }
37244             
37245             var li = document.createElement("li");
37246             li.className = "x-menu-list-item";
37247             ul.dom.appendChild(li);
37248             item.render(li, this);
37249         }, this);
37250         this.ul = ul;
37251         this.autoWidth();
37252     },
37253
37254     // private
37255     autoWidth : function(){
37256         var el = this.el, ul = this.ul;
37257         if(!el){
37258             return;
37259         }
37260         var w = this.width;
37261         if(w){
37262             el.setWidth(w);
37263         }else if(Roo.isIE){
37264             el.setWidth(this.minWidth);
37265             var t = el.dom.offsetWidth; // force recalc
37266             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37267         }
37268     },
37269
37270     // private
37271     delayAutoWidth : function(){
37272         if(this.rendered){
37273             if(!this.awTask){
37274                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37275             }
37276             this.awTask.delay(20);
37277         }
37278     },
37279
37280     // private
37281     findTargetItem : function(e){
37282         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37283         if(t && t.menuItemId){
37284             return this.items.get(t.menuItemId);
37285         }
37286     },
37287
37288     // private
37289     onClick : function(e){
37290         Roo.log("menu.onClick");
37291         var t = this.findTargetItem(e);
37292         if(!t){
37293             return;
37294         }
37295         Roo.log(e);
37296         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37297             if(t == this.activeItem && t.shouldDeactivate(e)){
37298                 this.activeItem.deactivate();
37299                 delete this.activeItem;
37300                 return;
37301             }
37302             if(t.canActivate){
37303                 this.setActiveItem(t, true);
37304             }
37305             return;
37306             
37307             
37308         }
37309         
37310         t.onClick(e);
37311         this.fireEvent("click", this, t, e);
37312     },
37313
37314     // private
37315     setActiveItem : function(item, autoExpand){
37316         if(item != this.activeItem){
37317             if(this.activeItem){
37318                 this.activeItem.deactivate();
37319             }
37320             this.activeItem = item;
37321             item.activate(autoExpand);
37322         }else if(autoExpand){
37323             item.expandMenu();
37324         }
37325     },
37326
37327     // private
37328     tryActivate : function(start, step){
37329         var items = this.items;
37330         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37331             var item = items.get(i);
37332             if(!item.disabled && item.canActivate){
37333                 this.setActiveItem(item, false);
37334                 return item;
37335             }
37336         }
37337         return false;
37338     },
37339
37340     // private
37341     onMouseOver : function(e){
37342         var t;
37343         if(t = this.findTargetItem(e)){
37344             if(t.canActivate && !t.disabled){
37345                 this.setActiveItem(t, true);
37346             }
37347         }
37348         this.fireEvent("mouseover", this, e, t);
37349     },
37350
37351     // private
37352     onMouseOut : function(e){
37353         var t;
37354         if(t = this.findTargetItem(e)){
37355             if(t == this.activeItem && t.shouldDeactivate(e)){
37356                 this.activeItem.deactivate();
37357                 delete this.activeItem;
37358             }
37359         }
37360         this.fireEvent("mouseout", this, e, t);
37361     },
37362
37363     /**
37364      * Read-only.  Returns true if the menu is currently displayed, else false.
37365      * @type Boolean
37366      */
37367     isVisible : function(){
37368         return this.el && !this.hidden;
37369     },
37370
37371     /**
37372      * Displays this menu relative to another element
37373      * @param {String/HTMLElement/Roo.Element} element The element to align to
37374      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37375      * the element (defaults to this.defaultAlign)
37376      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37377      */
37378     show : function(el, pos, parentMenu){
37379         this.parentMenu = parentMenu;
37380         if(!this.el){
37381             this.render();
37382         }
37383         this.fireEvent("beforeshow", this);
37384         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37385     },
37386
37387     /**
37388      * Displays this menu at a specific xy position
37389      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37390      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37391      */
37392     showAt : function(xy, parentMenu, /* private: */_e){
37393         this.parentMenu = parentMenu;
37394         if(!this.el){
37395             this.render();
37396         }
37397         if(_e !== false){
37398             this.fireEvent("beforeshow", this);
37399             xy = this.el.adjustForConstraints(xy);
37400         }
37401         this.el.setXY(xy);
37402         this.el.show();
37403         this.hidden = false;
37404         this.focus();
37405         this.fireEvent("show", this);
37406     },
37407
37408     focus : function(){
37409         if(!this.hidden){
37410             this.doFocus.defer(50, this);
37411         }
37412     },
37413
37414     doFocus : function(){
37415         if(!this.hidden){
37416             this.focusEl.focus();
37417         }
37418     },
37419
37420     /**
37421      * Hides this menu and optionally all parent menus
37422      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37423      */
37424     hide : function(deep){
37425         if(this.el && this.isVisible()){
37426             this.fireEvent("beforehide", this);
37427             if(this.activeItem){
37428                 this.activeItem.deactivate();
37429                 this.activeItem = null;
37430             }
37431             this.el.hide();
37432             this.hidden = true;
37433             this.fireEvent("hide", this);
37434         }
37435         if(deep === true && this.parentMenu){
37436             this.parentMenu.hide(true);
37437         }
37438     },
37439
37440     /**
37441      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37442      * Any of the following are valid:
37443      * <ul>
37444      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37445      * <li>An HTMLElement object which will be converted to a menu item</li>
37446      * <li>A menu item config object that will be created as a new menu item</li>
37447      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37448      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37449      * </ul>
37450      * Usage:
37451      * <pre><code>
37452 // Create the menu
37453 var menu = new Roo.menu.Menu();
37454
37455 // Create a menu item to add by reference
37456 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37457
37458 // Add a bunch of items at once using different methods.
37459 // Only the last item added will be returned.
37460 var item = menu.add(
37461     menuItem,                // add existing item by ref
37462     'Dynamic Item',          // new TextItem
37463     '-',                     // new separator
37464     { text: 'Config Item' }  // new item by config
37465 );
37466 </code></pre>
37467      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37468      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37469      */
37470     add : function(){
37471         var a = arguments, l = a.length, item;
37472         for(var i = 0; i < l; i++){
37473             var el = a[i];
37474             if ((typeof(el) == "object") && el.xtype && el.xns) {
37475                 el = Roo.factory(el, Roo.menu);
37476             }
37477             
37478             if(el.render){ // some kind of Item
37479                 item = this.addItem(el);
37480             }else if(typeof el == "string"){ // string
37481                 if(el == "separator" || el == "-"){
37482                     item = this.addSeparator();
37483                 }else{
37484                     item = this.addText(el);
37485                 }
37486             }else if(el.tagName || el.el){ // element
37487                 item = this.addElement(el);
37488             }else if(typeof el == "object"){ // must be menu item config?
37489                 item = this.addMenuItem(el);
37490             }
37491         }
37492         return item;
37493     },
37494
37495     /**
37496      * Returns this menu's underlying {@link Roo.Element} object
37497      * @return {Roo.Element} The element
37498      */
37499     getEl : function(){
37500         if(!this.el){
37501             this.render();
37502         }
37503         return this.el;
37504     },
37505
37506     /**
37507      * Adds a separator bar to the menu
37508      * @return {Roo.menu.Item} The menu item that was added
37509      */
37510     addSeparator : function(){
37511         return this.addItem(new Roo.menu.Separator());
37512     },
37513
37514     /**
37515      * Adds an {@link Roo.Element} object to the menu
37516      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37517      * @return {Roo.menu.Item} The menu item that was added
37518      */
37519     addElement : function(el){
37520         return this.addItem(new Roo.menu.BaseItem(el));
37521     },
37522
37523     /**
37524      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37525      * @param {Roo.menu.Item} item The menu item to add
37526      * @return {Roo.menu.Item} The menu item that was added
37527      */
37528     addItem : function(item){
37529         this.items.add(item);
37530         if(this.ul){
37531             var li = document.createElement("li");
37532             li.className = "x-menu-list-item";
37533             this.ul.dom.appendChild(li);
37534             item.render(li, this);
37535             this.delayAutoWidth();
37536         }
37537         return item;
37538     },
37539
37540     /**
37541      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37542      * @param {Object} config A MenuItem config object
37543      * @return {Roo.menu.Item} The menu item that was added
37544      */
37545     addMenuItem : function(config){
37546         if(!(config instanceof Roo.menu.Item)){
37547             if(typeof config.checked == "boolean"){ // must be check menu item config?
37548                 config = new Roo.menu.CheckItem(config);
37549             }else{
37550                 config = new Roo.menu.Item(config);
37551             }
37552         }
37553         return this.addItem(config);
37554     },
37555
37556     /**
37557      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37558      * @param {String} text The text to display in the menu item
37559      * @return {Roo.menu.Item} The menu item that was added
37560      */
37561     addText : function(text){
37562         return this.addItem(new Roo.menu.TextItem({ text : text }));
37563     },
37564
37565     /**
37566      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37567      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37568      * @param {Roo.menu.Item} item The menu item to add
37569      * @return {Roo.menu.Item} The menu item that was added
37570      */
37571     insert : function(index, item){
37572         this.items.insert(index, item);
37573         if(this.ul){
37574             var li = document.createElement("li");
37575             li.className = "x-menu-list-item";
37576             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37577             item.render(li, this);
37578             this.delayAutoWidth();
37579         }
37580         return item;
37581     },
37582
37583     /**
37584      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37585      * @param {Roo.menu.Item} item The menu item to remove
37586      */
37587     remove : function(item){
37588         this.items.removeKey(item.id);
37589         item.destroy();
37590     },
37591
37592     /**
37593      * Removes and destroys all items in the menu
37594      */
37595     removeAll : function(){
37596         var f;
37597         while(f = this.items.first()){
37598             this.remove(f);
37599         }
37600     }
37601 });
37602
37603 // MenuNav is a private utility class used internally by the Menu
37604 Roo.menu.MenuNav = function(menu){
37605     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37606     this.scope = this.menu = menu;
37607 };
37608
37609 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37610     doRelay : function(e, h){
37611         var k = e.getKey();
37612         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37613             this.menu.tryActivate(0, 1);
37614             return false;
37615         }
37616         return h.call(this.scope || this, e, this.menu);
37617     },
37618
37619     up : function(e, m){
37620         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37621             m.tryActivate(m.items.length-1, -1);
37622         }
37623     },
37624
37625     down : function(e, m){
37626         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37627             m.tryActivate(0, 1);
37628         }
37629     },
37630
37631     right : function(e, m){
37632         if(m.activeItem){
37633             m.activeItem.expandMenu(true);
37634         }
37635     },
37636
37637     left : function(e, m){
37638         m.hide();
37639         if(m.parentMenu && m.parentMenu.activeItem){
37640             m.parentMenu.activeItem.activate();
37641         }
37642     },
37643
37644     enter : function(e, m){
37645         if(m.activeItem){
37646             e.stopPropagation();
37647             m.activeItem.onClick(e);
37648             m.fireEvent("click", this, m.activeItem);
37649             return true;
37650         }
37651     }
37652 });/*
37653  * Based on:
37654  * Ext JS Library 1.1.1
37655  * Copyright(c) 2006-2007, Ext JS, LLC.
37656  *
37657  * Originally Released Under LGPL - original licence link has changed is not relivant.
37658  *
37659  * Fork - LGPL
37660  * <script type="text/javascript">
37661  */
37662  
37663 /**
37664  * @class Roo.menu.MenuMgr
37665  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37666  * @singleton
37667  */
37668 Roo.menu.MenuMgr = function(){
37669    var menus, active, groups = {}, attached = false, lastShow = new Date();
37670
37671    // private - called when first menu is created
37672    function init(){
37673        menus = {};
37674        active = new Roo.util.MixedCollection();
37675        Roo.get(document).addKeyListener(27, function(){
37676            if(active.length > 0){
37677                hideAll();
37678            }
37679        });
37680    }
37681
37682    // private
37683    function hideAll(){
37684        if(active && active.length > 0){
37685            var c = active.clone();
37686            c.each(function(m){
37687                m.hide();
37688            });
37689        }
37690    }
37691
37692    // private
37693    function onHide(m){
37694        active.remove(m);
37695        if(active.length < 1){
37696            Roo.get(document).un("mousedown", onMouseDown);
37697            attached = false;
37698        }
37699    }
37700
37701    // private
37702    function onShow(m){
37703        var last = active.last();
37704        lastShow = new Date();
37705        active.add(m);
37706        if(!attached){
37707            Roo.get(document).on("mousedown", onMouseDown);
37708            attached = true;
37709        }
37710        if(m.parentMenu){
37711           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37712           m.parentMenu.activeChild = m;
37713        }else if(last && last.isVisible()){
37714           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37715        }
37716    }
37717
37718    // private
37719    function onBeforeHide(m){
37720        if(m.activeChild){
37721            m.activeChild.hide();
37722        }
37723        if(m.autoHideTimer){
37724            clearTimeout(m.autoHideTimer);
37725            delete m.autoHideTimer;
37726        }
37727    }
37728
37729    // private
37730    function onBeforeShow(m){
37731        var pm = m.parentMenu;
37732        if(!pm && !m.allowOtherMenus){
37733            hideAll();
37734        }else if(pm && pm.activeChild && active != m){
37735            pm.activeChild.hide();
37736        }
37737    }
37738
37739    // private
37740    function onMouseDown(e){
37741        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37742            hideAll();
37743        }
37744    }
37745
37746    // private
37747    function onBeforeCheck(mi, state){
37748        if(state){
37749            var g = groups[mi.group];
37750            for(var i = 0, l = g.length; i < l; i++){
37751                if(g[i] != mi){
37752                    g[i].setChecked(false);
37753                }
37754            }
37755        }
37756    }
37757
37758    return {
37759
37760        /**
37761         * Hides all menus that are currently visible
37762         */
37763        hideAll : function(){
37764             hideAll();  
37765        },
37766
37767        // private
37768        register : function(menu){
37769            if(!menus){
37770                init();
37771            }
37772            menus[menu.id] = menu;
37773            menu.on("beforehide", onBeforeHide);
37774            menu.on("hide", onHide);
37775            menu.on("beforeshow", onBeforeShow);
37776            menu.on("show", onShow);
37777            var g = menu.group;
37778            if(g && menu.events["checkchange"]){
37779                if(!groups[g]){
37780                    groups[g] = [];
37781                }
37782                groups[g].push(menu);
37783                menu.on("checkchange", onCheck);
37784            }
37785        },
37786
37787         /**
37788          * Returns a {@link Roo.menu.Menu} object
37789          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37790          * be used to generate and return a new Menu instance.
37791          */
37792        get : function(menu){
37793            if(typeof menu == "string"){ // menu id
37794                return menus[menu];
37795            }else if(menu.events){  // menu instance
37796                return menu;
37797            }else if(typeof menu.length == 'number'){ // array of menu items?
37798                return new Roo.menu.Menu({items:menu});
37799            }else{ // otherwise, must be a config
37800                return new Roo.menu.Menu(menu);
37801            }
37802        },
37803
37804        // private
37805        unregister : function(menu){
37806            delete menus[menu.id];
37807            menu.un("beforehide", onBeforeHide);
37808            menu.un("hide", onHide);
37809            menu.un("beforeshow", onBeforeShow);
37810            menu.un("show", onShow);
37811            var g = menu.group;
37812            if(g && menu.events["checkchange"]){
37813                groups[g].remove(menu);
37814                menu.un("checkchange", onCheck);
37815            }
37816        },
37817
37818        // private
37819        registerCheckable : function(menuItem){
37820            var g = menuItem.group;
37821            if(g){
37822                if(!groups[g]){
37823                    groups[g] = [];
37824                }
37825                groups[g].push(menuItem);
37826                menuItem.on("beforecheckchange", onBeforeCheck);
37827            }
37828        },
37829
37830        // private
37831        unregisterCheckable : function(menuItem){
37832            var g = menuItem.group;
37833            if(g){
37834                groups[g].remove(menuItem);
37835                menuItem.un("beforecheckchange", onBeforeCheck);
37836            }
37837        }
37838    };
37839 }();/*
37840  * Based on:
37841  * Ext JS Library 1.1.1
37842  * Copyright(c) 2006-2007, Ext JS, LLC.
37843  *
37844  * Originally Released Under LGPL - original licence link has changed is not relivant.
37845  *
37846  * Fork - LGPL
37847  * <script type="text/javascript">
37848  */
37849  
37850
37851 /**
37852  * @class Roo.menu.BaseItem
37853  * @extends Roo.Component
37854  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37855  * management and base configuration options shared by all menu components.
37856  * @constructor
37857  * Creates a new BaseItem
37858  * @param {Object} config Configuration options
37859  */
37860 Roo.menu.BaseItem = function(config){
37861     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37862
37863     this.addEvents({
37864         /**
37865          * @event click
37866          * Fires when this item is clicked
37867          * @param {Roo.menu.BaseItem} this
37868          * @param {Roo.EventObject} e
37869          */
37870         click: true,
37871         /**
37872          * @event activate
37873          * Fires when this item is activated
37874          * @param {Roo.menu.BaseItem} this
37875          */
37876         activate : true,
37877         /**
37878          * @event deactivate
37879          * Fires when this item is deactivated
37880          * @param {Roo.menu.BaseItem} this
37881          */
37882         deactivate : true
37883     });
37884
37885     if(this.handler){
37886         this.on("click", this.handler, this.scope, true);
37887     }
37888 };
37889
37890 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37891     /**
37892      * @cfg {Function} handler
37893      * A function that will handle the click event of this menu item (defaults to undefined)
37894      */
37895     /**
37896      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37897      */
37898     canActivate : false,
37899     
37900      /**
37901      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37902      */
37903     hidden: false,
37904     
37905     /**
37906      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37907      */
37908     activeClass : "x-menu-item-active",
37909     /**
37910      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37911      */
37912     hideOnClick : true,
37913     /**
37914      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37915      */
37916     hideDelay : 100,
37917
37918     // private
37919     ctype: "Roo.menu.BaseItem",
37920
37921     // private
37922     actionMode : "container",
37923
37924     // private
37925     render : function(container, parentMenu){
37926         this.parentMenu = parentMenu;
37927         Roo.menu.BaseItem.superclass.render.call(this, container);
37928         this.container.menuItemId = this.id;
37929     },
37930
37931     // private
37932     onRender : function(container, position){
37933         this.el = Roo.get(this.el);
37934         container.dom.appendChild(this.el.dom);
37935     },
37936
37937     // private
37938     onClick : function(e){
37939         if(!this.disabled && this.fireEvent("click", this, e) !== false
37940                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37941             this.handleClick(e);
37942         }else{
37943             e.stopEvent();
37944         }
37945     },
37946
37947     // private
37948     activate : function(){
37949         if(this.disabled){
37950             return false;
37951         }
37952         var li = this.container;
37953         li.addClass(this.activeClass);
37954         this.region = li.getRegion().adjust(2, 2, -2, -2);
37955         this.fireEvent("activate", this);
37956         return true;
37957     },
37958
37959     // private
37960     deactivate : function(){
37961         this.container.removeClass(this.activeClass);
37962         this.fireEvent("deactivate", this);
37963     },
37964
37965     // private
37966     shouldDeactivate : function(e){
37967         return !this.region || !this.region.contains(e.getPoint());
37968     },
37969
37970     // private
37971     handleClick : function(e){
37972         if(this.hideOnClick){
37973             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37974         }
37975     },
37976
37977     // private
37978     expandMenu : function(autoActivate){
37979         // do nothing
37980     },
37981
37982     // private
37983     hideMenu : function(){
37984         // do nothing
37985     }
37986 });/*
37987  * Based on:
37988  * Ext JS Library 1.1.1
37989  * Copyright(c) 2006-2007, Ext JS, LLC.
37990  *
37991  * Originally Released Under LGPL - original licence link has changed is not relivant.
37992  *
37993  * Fork - LGPL
37994  * <script type="text/javascript">
37995  */
37996  
37997 /**
37998  * @class Roo.menu.Adapter
37999  * @extends Roo.menu.BaseItem
38000  * 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.
38001  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38002  * @constructor
38003  * Creates a new Adapter
38004  * @param {Object} config Configuration options
38005  */
38006 Roo.menu.Adapter = function(component, config){
38007     Roo.menu.Adapter.superclass.constructor.call(this, config);
38008     this.component = component;
38009 };
38010 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38011     // private
38012     canActivate : true,
38013
38014     // private
38015     onRender : function(container, position){
38016         this.component.render(container);
38017         this.el = this.component.getEl();
38018     },
38019
38020     // private
38021     activate : function(){
38022         if(this.disabled){
38023             return false;
38024         }
38025         this.component.focus();
38026         this.fireEvent("activate", this);
38027         return true;
38028     },
38029
38030     // private
38031     deactivate : function(){
38032         this.fireEvent("deactivate", this);
38033     },
38034
38035     // private
38036     disable : function(){
38037         this.component.disable();
38038         Roo.menu.Adapter.superclass.disable.call(this);
38039     },
38040
38041     // private
38042     enable : function(){
38043         this.component.enable();
38044         Roo.menu.Adapter.superclass.enable.call(this);
38045     }
38046 });/*
38047  * Based on:
38048  * Ext JS Library 1.1.1
38049  * Copyright(c) 2006-2007, Ext JS, LLC.
38050  *
38051  * Originally Released Under LGPL - original licence link has changed is not relivant.
38052  *
38053  * Fork - LGPL
38054  * <script type="text/javascript">
38055  */
38056
38057 /**
38058  * @class Roo.menu.TextItem
38059  * @extends Roo.menu.BaseItem
38060  * Adds a static text string to a menu, usually used as either a heading or group separator.
38061  * Note: old style constructor with text is still supported.
38062  * 
38063  * @constructor
38064  * Creates a new TextItem
38065  * @param {Object} cfg Configuration
38066  */
38067 Roo.menu.TextItem = function(cfg){
38068     if (typeof(cfg) == 'string') {
38069         this.text = cfg;
38070     } else {
38071         Roo.apply(this,cfg);
38072     }
38073     
38074     Roo.menu.TextItem.superclass.constructor.call(this);
38075 };
38076
38077 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38078     /**
38079      * @cfg {Boolean} text Text to show on item.
38080      */
38081     text : '',
38082     
38083     /**
38084      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38085      */
38086     hideOnClick : false,
38087     /**
38088      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38089      */
38090     itemCls : "x-menu-text",
38091
38092     // private
38093     onRender : function(){
38094         var s = document.createElement("span");
38095         s.className = this.itemCls;
38096         s.innerHTML = this.text;
38097         this.el = s;
38098         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38099     }
38100 });/*
38101  * Based on:
38102  * Ext JS Library 1.1.1
38103  * Copyright(c) 2006-2007, Ext JS, LLC.
38104  *
38105  * Originally Released Under LGPL - original licence link has changed is not relivant.
38106  *
38107  * Fork - LGPL
38108  * <script type="text/javascript">
38109  */
38110
38111 /**
38112  * @class Roo.menu.Separator
38113  * @extends Roo.menu.BaseItem
38114  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38115  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38116  * @constructor
38117  * @param {Object} config Configuration options
38118  */
38119 Roo.menu.Separator = function(config){
38120     Roo.menu.Separator.superclass.constructor.call(this, config);
38121 };
38122
38123 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38124     /**
38125      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38126      */
38127     itemCls : "x-menu-sep",
38128     /**
38129      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38130      */
38131     hideOnClick : false,
38132
38133     // private
38134     onRender : function(li){
38135         var s = document.createElement("span");
38136         s.className = this.itemCls;
38137         s.innerHTML = "&#160;";
38138         this.el = s;
38139         li.addClass("x-menu-sep-li");
38140         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38141     }
38142 });/*
38143  * Based on:
38144  * Ext JS Library 1.1.1
38145  * Copyright(c) 2006-2007, Ext JS, LLC.
38146  *
38147  * Originally Released Under LGPL - original licence link has changed is not relivant.
38148  *
38149  * Fork - LGPL
38150  * <script type="text/javascript">
38151  */
38152 /**
38153  * @class Roo.menu.Item
38154  * @extends Roo.menu.BaseItem
38155  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38156  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38157  * activation and click handling.
38158  * @constructor
38159  * Creates a new Item
38160  * @param {Object} config Configuration options
38161  */
38162 Roo.menu.Item = function(config){
38163     Roo.menu.Item.superclass.constructor.call(this, config);
38164     if(this.menu){
38165         this.menu = Roo.menu.MenuMgr.get(this.menu);
38166     }
38167 };
38168 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38169     
38170     /**
38171      * @cfg {String} text
38172      * The text to show on the menu item.
38173      */
38174     text: '',
38175      /**
38176      * @cfg {String} HTML to render in menu
38177      * The text to show on the menu item (HTML version).
38178      */
38179     html: '',
38180     /**
38181      * @cfg {String} icon
38182      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38183      */
38184     icon: undefined,
38185     /**
38186      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38187      */
38188     itemCls : "x-menu-item",
38189     /**
38190      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38191      */
38192     canActivate : true,
38193     /**
38194      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38195      */
38196     showDelay: 200,
38197     // doc'd in BaseItem
38198     hideDelay: 200,
38199
38200     // private
38201     ctype: "Roo.menu.Item",
38202     
38203     // private
38204     onRender : function(container, position){
38205         var el = document.createElement("a");
38206         el.hideFocus = true;
38207         el.unselectable = "on";
38208         el.href = this.href || "#";
38209         if(this.hrefTarget){
38210             el.target = this.hrefTarget;
38211         }
38212         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38213         
38214         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38215         
38216         el.innerHTML = String.format(
38217                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38218                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38219         this.el = el;
38220         Roo.menu.Item.superclass.onRender.call(this, container, position);
38221     },
38222
38223     /**
38224      * Sets the text to display in this menu item
38225      * @param {String} text The text to display
38226      * @param {Boolean} isHTML true to indicate text is pure html.
38227      */
38228     setText : function(text, isHTML){
38229         if (isHTML) {
38230             this.html = text;
38231         } else {
38232             this.text = text;
38233             this.html = '';
38234         }
38235         if(this.rendered){
38236             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38237      
38238             this.el.update(String.format(
38239                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38240                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38241             this.parentMenu.autoWidth();
38242         }
38243     },
38244
38245     // private
38246     handleClick : function(e){
38247         if(!this.href){ // if no link defined, stop the event automatically
38248             e.stopEvent();
38249         }
38250         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38251     },
38252
38253     // private
38254     activate : function(autoExpand){
38255         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38256             this.focus();
38257             if(autoExpand){
38258                 this.expandMenu();
38259             }
38260         }
38261         return true;
38262     },
38263
38264     // private
38265     shouldDeactivate : function(e){
38266         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38267             if(this.menu && this.menu.isVisible()){
38268                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38269             }
38270             return true;
38271         }
38272         return false;
38273     },
38274
38275     // private
38276     deactivate : function(){
38277         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38278         this.hideMenu();
38279     },
38280
38281     // private
38282     expandMenu : function(autoActivate){
38283         if(!this.disabled && this.menu){
38284             clearTimeout(this.hideTimer);
38285             delete this.hideTimer;
38286             if(!this.menu.isVisible() && !this.showTimer){
38287                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38288             }else if (this.menu.isVisible() && autoActivate){
38289                 this.menu.tryActivate(0, 1);
38290             }
38291         }
38292     },
38293
38294     // private
38295     deferExpand : function(autoActivate){
38296         delete this.showTimer;
38297         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38298         if(autoActivate){
38299             this.menu.tryActivate(0, 1);
38300         }
38301     },
38302
38303     // private
38304     hideMenu : function(){
38305         clearTimeout(this.showTimer);
38306         delete this.showTimer;
38307         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38308             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38309         }
38310     },
38311
38312     // private
38313     deferHide : function(){
38314         delete this.hideTimer;
38315         this.menu.hide();
38316     }
38317 });/*
38318  * Based on:
38319  * Ext JS Library 1.1.1
38320  * Copyright(c) 2006-2007, Ext JS, LLC.
38321  *
38322  * Originally Released Under LGPL - original licence link has changed is not relivant.
38323  *
38324  * Fork - LGPL
38325  * <script type="text/javascript">
38326  */
38327  
38328 /**
38329  * @class Roo.menu.CheckItem
38330  * @extends Roo.menu.Item
38331  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38332  * @constructor
38333  * Creates a new CheckItem
38334  * @param {Object} config Configuration options
38335  */
38336 Roo.menu.CheckItem = function(config){
38337     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38338     this.addEvents({
38339         /**
38340          * @event beforecheckchange
38341          * Fires before the checked value is set, providing an opportunity to cancel if needed
38342          * @param {Roo.menu.CheckItem} this
38343          * @param {Boolean} checked The new checked value that will be set
38344          */
38345         "beforecheckchange" : true,
38346         /**
38347          * @event checkchange
38348          * Fires after the checked value has been set
38349          * @param {Roo.menu.CheckItem} this
38350          * @param {Boolean} checked The checked value that was set
38351          */
38352         "checkchange" : true
38353     });
38354     if(this.checkHandler){
38355         this.on('checkchange', this.checkHandler, this.scope);
38356     }
38357 };
38358 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38359     /**
38360      * @cfg {String} group
38361      * All check items with the same group name will automatically be grouped into a single-select
38362      * radio button group (defaults to '')
38363      */
38364     /**
38365      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38366      */
38367     itemCls : "x-menu-item x-menu-check-item",
38368     /**
38369      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38370      */
38371     groupClass : "x-menu-group-item",
38372
38373     /**
38374      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38375      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38376      * initialized with checked = true will be rendered as checked.
38377      */
38378     checked: false,
38379
38380     // private
38381     ctype: "Roo.menu.CheckItem",
38382
38383     // private
38384     onRender : function(c){
38385         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38386         if(this.group){
38387             this.el.addClass(this.groupClass);
38388         }
38389         Roo.menu.MenuMgr.registerCheckable(this);
38390         if(this.checked){
38391             this.checked = false;
38392             this.setChecked(true, true);
38393         }
38394     },
38395
38396     // private
38397     destroy : function(){
38398         if(this.rendered){
38399             Roo.menu.MenuMgr.unregisterCheckable(this);
38400         }
38401         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38402     },
38403
38404     /**
38405      * Set the checked state of this item
38406      * @param {Boolean} checked The new checked value
38407      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38408      */
38409     setChecked : function(state, suppressEvent){
38410         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38411             if(this.container){
38412                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38413             }
38414             this.checked = state;
38415             if(suppressEvent !== true){
38416                 this.fireEvent("checkchange", this, state);
38417             }
38418         }
38419     },
38420
38421     // private
38422     handleClick : function(e){
38423        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38424            this.setChecked(!this.checked);
38425        }
38426        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38427     }
38428 });/*
38429  * Based on:
38430  * Ext JS Library 1.1.1
38431  * Copyright(c) 2006-2007, Ext JS, LLC.
38432  *
38433  * Originally Released Under LGPL - original licence link has changed is not relivant.
38434  *
38435  * Fork - LGPL
38436  * <script type="text/javascript">
38437  */
38438  
38439 /**
38440  * @class Roo.menu.DateItem
38441  * @extends Roo.menu.Adapter
38442  * A menu item that wraps the {@link Roo.DatPicker} component.
38443  * @constructor
38444  * Creates a new DateItem
38445  * @param {Object} config Configuration options
38446  */
38447 Roo.menu.DateItem = function(config){
38448     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38449     /** The Roo.DatePicker object @type Roo.DatePicker */
38450     this.picker = this.component;
38451     this.addEvents({select: true});
38452     
38453     this.picker.on("render", function(picker){
38454         picker.getEl().swallowEvent("click");
38455         picker.container.addClass("x-menu-date-item");
38456     });
38457
38458     this.picker.on("select", this.onSelect, this);
38459 };
38460
38461 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38462     // private
38463     onSelect : function(picker, date){
38464         this.fireEvent("select", this, date, picker);
38465         Roo.menu.DateItem.superclass.handleClick.call(this);
38466     }
38467 });/*
38468  * Based on:
38469  * Ext JS Library 1.1.1
38470  * Copyright(c) 2006-2007, Ext JS, LLC.
38471  *
38472  * Originally Released Under LGPL - original licence link has changed is not relivant.
38473  *
38474  * Fork - LGPL
38475  * <script type="text/javascript">
38476  */
38477  
38478 /**
38479  * @class Roo.menu.ColorItem
38480  * @extends Roo.menu.Adapter
38481  * A menu item that wraps the {@link Roo.ColorPalette} component.
38482  * @constructor
38483  * Creates a new ColorItem
38484  * @param {Object} config Configuration options
38485  */
38486 Roo.menu.ColorItem = function(config){
38487     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38488     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38489     this.palette = this.component;
38490     this.relayEvents(this.palette, ["select"]);
38491     if(this.selectHandler){
38492         this.on('select', this.selectHandler, this.scope);
38493     }
38494 };
38495 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38496  * Based on:
38497  * Ext JS Library 1.1.1
38498  * Copyright(c) 2006-2007, Ext JS, LLC.
38499  *
38500  * Originally Released Under LGPL - original licence link has changed is not relivant.
38501  *
38502  * Fork - LGPL
38503  * <script type="text/javascript">
38504  */
38505  
38506
38507 /**
38508  * @class Roo.menu.DateMenu
38509  * @extends Roo.menu.Menu
38510  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38511  * @constructor
38512  * Creates a new DateMenu
38513  * @param {Object} config Configuration options
38514  */
38515 Roo.menu.DateMenu = function(config){
38516     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38517     this.plain = true;
38518     var di = new Roo.menu.DateItem(config);
38519     this.add(di);
38520     /**
38521      * The {@link Roo.DatePicker} instance for this DateMenu
38522      * @type DatePicker
38523      */
38524     this.picker = di.picker;
38525     /**
38526      * @event select
38527      * @param {DatePicker} picker
38528      * @param {Date} date
38529      */
38530     this.relayEvents(di, ["select"]);
38531     this.on('beforeshow', function(){
38532         if(this.picker){
38533             this.picker.hideMonthPicker(false);
38534         }
38535     }, this);
38536 };
38537 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38538     cls:'x-date-menu'
38539 });/*
38540  * Based on:
38541  * Ext JS Library 1.1.1
38542  * Copyright(c) 2006-2007, Ext JS, LLC.
38543  *
38544  * Originally Released Under LGPL - original licence link has changed is not relivant.
38545  *
38546  * Fork - LGPL
38547  * <script type="text/javascript">
38548  */
38549  
38550
38551 /**
38552  * @class Roo.menu.ColorMenu
38553  * @extends Roo.menu.Menu
38554  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38555  * @constructor
38556  * Creates a new ColorMenu
38557  * @param {Object} config Configuration options
38558  */
38559 Roo.menu.ColorMenu = function(config){
38560     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38561     this.plain = true;
38562     var ci = new Roo.menu.ColorItem(config);
38563     this.add(ci);
38564     /**
38565      * The {@link Roo.ColorPalette} instance for this ColorMenu
38566      * @type ColorPalette
38567      */
38568     this.palette = ci.palette;
38569     /**
38570      * @event select
38571      * @param {ColorPalette} palette
38572      * @param {String} color
38573      */
38574     this.relayEvents(ci, ["select"]);
38575 };
38576 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38577  * Based on:
38578  * Ext JS Library 1.1.1
38579  * Copyright(c) 2006-2007, Ext JS, LLC.
38580  *
38581  * Originally Released Under LGPL - original licence link has changed is not relivant.
38582  *
38583  * Fork - LGPL
38584  * <script type="text/javascript">
38585  */
38586  
38587 /**
38588  * @class Roo.form.Field
38589  * @extends Roo.BoxComponent
38590  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38591  * @constructor
38592  * Creates a new Field
38593  * @param {Object} config Configuration options
38594  */
38595 Roo.form.Field = function(config){
38596     Roo.form.Field.superclass.constructor.call(this, config);
38597 };
38598
38599 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38600     /**
38601      * @cfg {String} fieldLabel Label to use when rendering a form.
38602      */
38603        /**
38604      * @cfg {String} qtip Mouse over tip
38605      */
38606      
38607     /**
38608      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38609      */
38610     invalidClass : "x-form-invalid",
38611     /**
38612      * @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")
38613      */
38614     invalidText : "The value in this field is invalid",
38615     /**
38616      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38617      */
38618     focusClass : "x-form-focus",
38619     /**
38620      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38621       automatic validation (defaults to "keyup").
38622      */
38623     validationEvent : "keyup",
38624     /**
38625      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38626      */
38627     validateOnBlur : true,
38628     /**
38629      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38630      */
38631     validationDelay : 250,
38632     /**
38633      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38634      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38635      */
38636     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38637     /**
38638      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38639      */
38640     fieldClass : "x-form-field",
38641     /**
38642      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38643      *<pre>
38644 Value         Description
38645 -----------   ----------------------------------------------------------------------
38646 qtip          Display a quick tip when the user hovers over the field
38647 title         Display a default browser title attribute popup
38648 under         Add a block div beneath the field containing the error text
38649 side          Add an error icon to the right of the field with a popup on hover
38650 [element id]  Add the error text directly to the innerHTML of the specified element
38651 </pre>
38652      */
38653     msgTarget : 'qtip',
38654     /**
38655      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38656      */
38657     msgFx : 'normal',
38658
38659     /**
38660      * @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.
38661      */
38662     readOnly : false,
38663
38664     /**
38665      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38666      */
38667     disabled : false,
38668
38669     /**
38670      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38671      */
38672     inputType : undefined,
38673     
38674     /**
38675      * @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).
38676          */
38677         tabIndex : undefined,
38678         
38679     // private
38680     isFormField : true,
38681
38682     // private
38683     hasFocus : false,
38684     /**
38685      * @property {Roo.Element} fieldEl
38686      * Element Containing the rendered Field (with label etc.)
38687      */
38688     /**
38689      * @cfg {Mixed} value A value to initialize this field with.
38690      */
38691     value : undefined,
38692
38693     /**
38694      * @cfg {String} name The field's HTML name attribute.
38695      */
38696     /**
38697      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38698      */
38699     // private
38700     loadedValue : false,
38701      
38702      
38703         // private ??
38704         initComponent : function(){
38705         Roo.form.Field.superclass.initComponent.call(this);
38706         this.addEvents({
38707             /**
38708              * @event focus
38709              * Fires when this field receives input focus.
38710              * @param {Roo.form.Field} this
38711              */
38712             focus : true,
38713             /**
38714              * @event blur
38715              * Fires when this field loses input focus.
38716              * @param {Roo.form.Field} this
38717              */
38718             blur : true,
38719             /**
38720              * @event specialkey
38721              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38722              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38723              * @param {Roo.form.Field} this
38724              * @param {Roo.EventObject} e The event object
38725              */
38726             specialkey : true,
38727             /**
38728              * @event change
38729              * Fires just before the field blurs if the field value has changed.
38730              * @param {Roo.form.Field} this
38731              * @param {Mixed} newValue The new value
38732              * @param {Mixed} oldValue The original value
38733              */
38734             change : true,
38735             /**
38736              * @event invalid
38737              * Fires after the field has been marked as invalid.
38738              * @param {Roo.form.Field} this
38739              * @param {String} msg The validation message
38740              */
38741             invalid : true,
38742             /**
38743              * @event valid
38744              * Fires after the field has been validated with no errors.
38745              * @param {Roo.form.Field} this
38746              */
38747             valid : true,
38748              /**
38749              * @event keyup
38750              * Fires after the key up
38751              * @param {Roo.form.Field} this
38752              * @param {Roo.EventObject}  e The event Object
38753              */
38754             keyup : true
38755         });
38756     },
38757
38758     /**
38759      * Returns the name attribute of the field if available
38760      * @return {String} name The field name
38761      */
38762     getName: function(){
38763          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38764     },
38765
38766     // private
38767     onRender : function(ct, position){
38768         Roo.form.Field.superclass.onRender.call(this, ct, position);
38769         if(!this.el){
38770             var cfg = this.getAutoCreate();
38771             if(!cfg.name){
38772                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38773             }
38774             if (!cfg.name.length) {
38775                 delete cfg.name;
38776             }
38777             if(this.inputType){
38778                 cfg.type = this.inputType;
38779             }
38780             this.el = ct.createChild(cfg, position);
38781         }
38782         var type = this.el.dom.type;
38783         if(type){
38784             if(type == 'password'){
38785                 type = 'text';
38786             }
38787             this.el.addClass('x-form-'+type);
38788         }
38789         if(this.readOnly){
38790             this.el.dom.readOnly = true;
38791         }
38792         if(this.tabIndex !== undefined){
38793             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38794         }
38795
38796         this.el.addClass([this.fieldClass, this.cls]);
38797         this.initValue();
38798     },
38799
38800     /**
38801      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38802      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38803      * @return {Roo.form.Field} this
38804      */
38805     applyTo : function(target){
38806         this.allowDomMove = false;
38807         this.el = Roo.get(target);
38808         this.render(this.el.dom.parentNode);
38809         return this;
38810     },
38811
38812     // private
38813     initValue : function(){
38814         if(this.value !== undefined){
38815             this.setValue(this.value);
38816         }else if(this.el.dom.value.length > 0){
38817             this.setValue(this.el.dom.value);
38818         }
38819     },
38820
38821     /**
38822      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38823      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38824      */
38825     isDirty : function() {
38826         if(this.disabled) {
38827             return false;
38828         }
38829         return String(this.getValue()) !== String(this.originalValue);
38830     },
38831
38832     /**
38833      * stores the current value in loadedValue
38834      */
38835     resetHasChanged : function()
38836     {
38837         this.loadedValue = String(this.getValue());
38838     },
38839     /**
38840      * checks the current value against the 'loaded' value.
38841      * Note - will return false if 'resetHasChanged' has not been called first.
38842      */
38843     hasChanged : function()
38844     {
38845         if(this.disabled || this.readOnly) {
38846             return false;
38847         }
38848         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38849     },
38850     
38851     
38852     
38853     // private
38854     afterRender : function(){
38855         Roo.form.Field.superclass.afterRender.call(this);
38856         this.initEvents();
38857     },
38858
38859     // private
38860     fireKey : function(e){
38861         //Roo.log('field ' + e.getKey());
38862         if(e.isNavKeyPress()){
38863             this.fireEvent("specialkey", this, e);
38864         }
38865     },
38866
38867     /**
38868      * Resets the current field value to the originally loaded value and clears any validation messages
38869      */
38870     reset : function(){
38871         this.setValue(this.resetValue);
38872         this.clearInvalid();
38873     },
38874
38875     // private
38876     initEvents : function(){
38877         // safari killled keypress - so keydown is now used..
38878         this.el.on("keydown" , this.fireKey,  this);
38879         this.el.on("focus", this.onFocus,  this);
38880         this.el.on("blur", this.onBlur,  this);
38881         this.el.relayEvent('keyup', this);
38882
38883         // reference to original value for reset
38884         this.originalValue = this.getValue();
38885         this.resetValue =  this.getValue();
38886     },
38887
38888     // private
38889     onFocus : function(){
38890         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38891             this.el.addClass(this.focusClass);
38892         }
38893         if(!this.hasFocus){
38894             this.hasFocus = true;
38895             this.startValue = this.getValue();
38896             this.fireEvent("focus", this);
38897         }
38898     },
38899
38900     beforeBlur : Roo.emptyFn,
38901
38902     // private
38903     onBlur : function(){
38904         this.beforeBlur();
38905         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38906             this.el.removeClass(this.focusClass);
38907         }
38908         this.hasFocus = false;
38909         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38910             this.validate();
38911         }
38912         var v = this.getValue();
38913         if(String(v) !== String(this.startValue)){
38914             this.fireEvent('change', this, v, this.startValue);
38915         }
38916         this.fireEvent("blur", this);
38917     },
38918
38919     /**
38920      * Returns whether or not the field value is currently valid
38921      * @param {Boolean} preventMark True to disable marking the field invalid
38922      * @return {Boolean} True if the value is valid, else false
38923      */
38924     isValid : function(preventMark){
38925         if(this.disabled){
38926             return true;
38927         }
38928         var restore = this.preventMark;
38929         this.preventMark = preventMark === true;
38930         var v = this.validateValue(this.processValue(this.getRawValue()));
38931         this.preventMark = restore;
38932         return v;
38933     },
38934
38935     /**
38936      * Validates the field value
38937      * @return {Boolean} True if the value is valid, else false
38938      */
38939     validate : function(){
38940         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38941             this.clearInvalid();
38942             return true;
38943         }
38944         return false;
38945     },
38946
38947     processValue : function(value){
38948         return value;
38949     },
38950
38951     // private
38952     // Subclasses should provide the validation implementation by overriding this
38953     validateValue : function(value){
38954         return true;
38955     },
38956
38957     /**
38958      * Mark this field as invalid
38959      * @param {String} msg The validation message
38960      */
38961     markInvalid : function(msg){
38962         if(!this.rendered || this.preventMark){ // not rendered
38963             return;
38964         }
38965         
38966         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38967         
38968         obj.el.addClass(this.invalidClass);
38969         msg = msg || this.invalidText;
38970         switch(this.msgTarget){
38971             case 'qtip':
38972                 obj.el.dom.qtip = msg;
38973                 obj.el.dom.qclass = 'x-form-invalid-tip';
38974                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38975                     Roo.QuickTips.enable();
38976                 }
38977                 break;
38978             case 'title':
38979                 this.el.dom.title = msg;
38980                 break;
38981             case 'under':
38982                 if(!this.errorEl){
38983                     var elp = this.el.findParent('.x-form-element', 5, true);
38984                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38985                     this.errorEl.setWidth(elp.getWidth(true)-20);
38986                 }
38987                 this.errorEl.update(msg);
38988                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38989                 break;
38990             case 'side':
38991                 if(!this.errorIcon){
38992                     var elp = this.el.findParent('.x-form-element', 5, true);
38993                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38994                 }
38995                 this.alignErrorIcon();
38996                 this.errorIcon.dom.qtip = msg;
38997                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38998                 this.errorIcon.show();
38999                 this.on('resize', this.alignErrorIcon, this);
39000                 break;
39001             default:
39002                 var t = Roo.getDom(this.msgTarget);
39003                 t.innerHTML = msg;
39004                 t.style.display = this.msgDisplay;
39005                 break;
39006         }
39007         this.fireEvent('invalid', this, msg);
39008     },
39009
39010     // private
39011     alignErrorIcon : function(){
39012         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39013     },
39014
39015     /**
39016      * Clear any invalid styles/messages for this field
39017      */
39018     clearInvalid : function(){
39019         if(!this.rendered || this.preventMark){ // not rendered
39020             return;
39021         }
39022         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39023         
39024         obj.el.removeClass(this.invalidClass);
39025         switch(this.msgTarget){
39026             case 'qtip':
39027                 obj.el.dom.qtip = '';
39028                 break;
39029             case 'title':
39030                 this.el.dom.title = '';
39031                 break;
39032             case 'under':
39033                 if(this.errorEl){
39034                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39035                 }
39036                 break;
39037             case 'side':
39038                 if(this.errorIcon){
39039                     this.errorIcon.dom.qtip = '';
39040                     this.errorIcon.hide();
39041                     this.un('resize', this.alignErrorIcon, this);
39042                 }
39043                 break;
39044             default:
39045                 var t = Roo.getDom(this.msgTarget);
39046                 t.innerHTML = '';
39047                 t.style.display = 'none';
39048                 break;
39049         }
39050         this.fireEvent('valid', this);
39051     },
39052
39053     /**
39054      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39055      * @return {Mixed} value The field value
39056      */
39057     getRawValue : function(){
39058         var v = this.el.getValue();
39059         
39060         return v;
39061     },
39062
39063     /**
39064      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39065      * @return {Mixed} value The field value
39066      */
39067     getValue : function(){
39068         var v = this.el.getValue();
39069          
39070         return v;
39071     },
39072
39073     /**
39074      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39075      * @param {Mixed} value The value to set
39076      */
39077     setRawValue : function(v){
39078         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39079     },
39080
39081     /**
39082      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39083      * @param {Mixed} value The value to set
39084      */
39085     setValue : function(v){
39086         this.value = v;
39087         if(this.rendered){
39088             this.el.dom.value = (v === null || v === undefined ? '' : v);
39089              this.validate();
39090         }
39091     },
39092
39093     adjustSize : function(w, h){
39094         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39095         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39096         return s;
39097     },
39098
39099     adjustWidth : function(tag, w){
39100         tag = tag.toLowerCase();
39101         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39102             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39103                 if(tag == 'input'){
39104                     return w + 2;
39105                 }
39106                 if(tag == 'textarea'){
39107                     return w-2;
39108                 }
39109             }else if(Roo.isOpera){
39110                 if(tag == 'input'){
39111                     return w + 2;
39112                 }
39113                 if(tag == 'textarea'){
39114                     return w-2;
39115                 }
39116             }
39117         }
39118         return w;
39119     }
39120 });
39121
39122
39123 // anything other than normal should be considered experimental
39124 Roo.form.Field.msgFx = {
39125     normal : {
39126         show: function(msgEl, f){
39127             msgEl.setDisplayed('block');
39128         },
39129
39130         hide : function(msgEl, f){
39131             msgEl.setDisplayed(false).update('');
39132         }
39133     },
39134
39135     slide : {
39136         show: function(msgEl, f){
39137             msgEl.slideIn('t', {stopFx:true});
39138         },
39139
39140         hide : function(msgEl, f){
39141             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39142         }
39143     },
39144
39145     slideRight : {
39146         show: function(msgEl, f){
39147             msgEl.fixDisplay();
39148             msgEl.alignTo(f.el, 'tl-tr');
39149             msgEl.slideIn('l', {stopFx:true});
39150         },
39151
39152         hide : function(msgEl, f){
39153             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39154         }
39155     }
39156 };/*
39157  * Based on:
39158  * Ext JS Library 1.1.1
39159  * Copyright(c) 2006-2007, Ext JS, LLC.
39160  *
39161  * Originally Released Under LGPL - original licence link has changed is not relivant.
39162  *
39163  * Fork - LGPL
39164  * <script type="text/javascript">
39165  */
39166  
39167
39168 /**
39169  * @class Roo.form.TextField
39170  * @extends Roo.form.Field
39171  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39172  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39173  * @constructor
39174  * Creates a new TextField
39175  * @param {Object} config Configuration options
39176  */
39177 Roo.form.TextField = function(config){
39178     Roo.form.TextField.superclass.constructor.call(this, config);
39179     this.addEvents({
39180         /**
39181          * @event autosize
39182          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39183          * according to the default logic, but this event provides a hook for the developer to apply additional
39184          * logic at runtime to resize the field if needed.
39185              * @param {Roo.form.Field} this This text field
39186              * @param {Number} width The new field width
39187              */
39188         autosize : true
39189     });
39190 };
39191
39192 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39193     /**
39194      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39195      */
39196     grow : false,
39197     /**
39198      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39199      */
39200     growMin : 30,
39201     /**
39202      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39203      */
39204     growMax : 800,
39205     /**
39206      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39207      */
39208     vtype : null,
39209     /**
39210      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39211      */
39212     maskRe : null,
39213     /**
39214      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39215      */
39216     disableKeyFilter : false,
39217     /**
39218      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39219      */
39220     allowBlank : true,
39221     /**
39222      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39223      */
39224     minLength : 0,
39225     /**
39226      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39227      */
39228     maxLength : Number.MAX_VALUE,
39229     /**
39230      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39231      */
39232     minLengthText : "The minimum length for this field is {0}",
39233     /**
39234      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39235      */
39236     maxLengthText : "The maximum length for this field is {0}",
39237     /**
39238      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39239      */
39240     selectOnFocus : false,
39241     /**
39242      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39243      */
39244     blankText : "This field is required",
39245     /**
39246      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39247      * If available, this function will be called only after the basic validators all return true, and will be passed the
39248      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39249      */
39250     validator : null,
39251     /**
39252      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39253      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39254      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39255      */
39256     regex : null,
39257     /**
39258      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39259      */
39260     regexText : "",
39261     /**
39262      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39263      */
39264     emptyText : null,
39265    
39266
39267     // private
39268     initEvents : function()
39269     {
39270         if (this.emptyText) {
39271             this.el.attr('placeholder', this.emptyText);
39272         }
39273         
39274         Roo.form.TextField.superclass.initEvents.call(this);
39275         if(this.validationEvent == 'keyup'){
39276             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39277             this.el.on('keyup', this.filterValidation, this);
39278         }
39279         else if(this.validationEvent !== false){
39280             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39281         }
39282         
39283         if(this.selectOnFocus){
39284             this.on("focus", this.preFocus, this);
39285             
39286         }
39287         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39288             this.el.on("keypress", this.filterKeys, this);
39289         }
39290         if(this.grow){
39291             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39292             this.el.on("click", this.autoSize,  this);
39293         }
39294         if(this.el.is('input[type=password]') && Roo.isSafari){
39295             this.el.on('keydown', this.SafariOnKeyDown, this);
39296         }
39297     },
39298
39299     processValue : function(value){
39300         if(this.stripCharsRe){
39301             var newValue = value.replace(this.stripCharsRe, '');
39302             if(newValue !== value){
39303                 this.setRawValue(newValue);
39304                 return newValue;
39305             }
39306         }
39307         return value;
39308     },
39309
39310     filterValidation : function(e){
39311         if(!e.isNavKeyPress()){
39312             this.validationTask.delay(this.validationDelay);
39313         }
39314     },
39315
39316     // private
39317     onKeyUp : function(e){
39318         if(!e.isNavKeyPress()){
39319             this.autoSize();
39320         }
39321     },
39322
39323     /**
39324      * Resets the current field value to the originally-loaded value and clears any validation messages.
39325      *  
39326      */
39327     reset : function(){
39328         Roo.form.TextField.superclass.reset.call(this);
39329        
39330     },
39331
39332     
39333     // private
39334     preFocus : function(){
39335         
39336         if(this.selectOnFocus){
39337             this.el.dom.select();
39338         }
39339     },
39340
39341     
39342     // private
39343     filterKeys : function(e){
39344         var k = e.getKey();
39345         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39346             return;
39347         }
39348         var c = e.getCharCode(), cc = String.fromCharCode(c);
39349         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39350             return;
39351         }
39352         if(!this.maskRe.test(cc)){
39353             e.stopEvent();
39354         }
39355     },
39356
39357     setValue : function(v){
39358         
39359         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39360         
39361         this.autoSize();
39362     },
39363
39364     /**
39365      * Validates a value according to the field's validation rules and marks the field as invalid
39366      * if the validation fails
39367      * @param {Mixed} value The value to validate
39368      * @return {Boolean} True if the value is valid, else false
39369      */
39370     validateValue : function(value){
39371         if(value.length < 1)  { // if it's blank
39372              if(this.allowBlank){
39373                 this.clearInvalid();
39374                 return true;
39375              }else{
39376                 this.markInvalid(this.blankText);
39377                 return false;
39378              }
39379         }
39380         if(value.length < this.minLength){
39381             this.markInvalid(String.format(this.minLengthText, this.minLength));
39382             return false;
39383         }
39384         if(value.length > this.maxLength){
39385             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39386             return false;
39387         }
39388         if(this.vtype){
39389             var vt = Roo.form.VTypes;
39390             if(!vt[this.vtype](value, this)){
39391                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39392                 return false;
39393             }
39394         }
39395         if(typeof this.validator == "function"){
39396             var msg = this.validator(value);
39397             if(msg !== true){
39398                 this.markInvalid(msg);
39399                 return false;
39400             }
39401         }
39402         if(this.regex && !this.regex.test(value)){
39403             this.markInvalid(this.regexText);
39404             return false;
39405         }
39406         return true;
39407     },
39408
39409     /**
39410      * Selects text in this field
39411      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39412      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39413      */
39414     selectText : function(start, end){
39415         var v = this.getRawValue();
39416         if(v.length > 0){
39417             start = start === undefined ? 0 : start;
39418             end = end === undefined ? v.length : end;
39419             var d = this.el.dom;
39420             if(d.setSelectionRange){
39421                 d.setSelectionRange(start, end);
39422             }else if(d.createTextRange){
39423                 var range = d.createTextRange();
39424                 range.moveStart("character", start);
39425                 range.moveEnd("character", v.length-end);
39426                 range.select();
39427             }
39428         }
39429     },
39430
39431     /**
39432      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39433      * This only takes effect if grow = true, and fires the autosize event.
39434      */
39435     autoSize : function(){
39436         if(!this.grow || !this.rendered){
39437             return;
39438         }
39439         if(!this.metrics){
39440             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39441         }
39442         var el = this.el;
39443         var v = el.dom.value;
39444         var d = document.createElement('div');
39445         d.appendChild(document.createTextNode(v));
39446         v = d.innerHTML;
39447         d = null;
39448         v += "&#160;";
39449         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39450         this.el.setWidth(w);
39451         this.fireEvent("autosize", this, w);
39452     },
39453     
39454     // private
39455     SafariOnKeyDown : function(event)
39456     {
39457         // this is a workaround for a password hang bug on chrome/ webkit.
39458         
39459         var isSelectAll = false;
39460         
39461         if(this.el.dom.selectionEnd > 0){
39462             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39463         }
39464         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39465             event.preventDefault();
39466             this.setValue('');
39467             return;
39468         }
39469         
39470         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39471             
39472             event.preventDefault();
39473             // this is very hacky as keydown always get's upper case.
39474             
39475             var cc = String.fromCharCode(event.getCharCode());
39476             
39477             
39478             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39479             
39480         }
39481         
39482         
39483     }
39484 });/*
39485  * Based on:
39486  * Ext JS Library 1.1.1
39487  * Copyright(c) 2006-2007, Ext JS, LLC.
39488  *
39489  * Originally Released Under LGPL - original licence link has changed is not relivant.
39490  *
39491  * Fork - LGPL
39492  * <script type="text/javascript">
39493  */
39494  
39495 /**
39496  * @class Roo.form.Hidden
39497  * @extends Roo.form.TextField
39498  * Simple Hidden element used on forms 
39499  * 
39500  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39501  * 
39502  * @constructor
39503  * Creates a new Hidden form element.
39504  * @param {Object} config Configuration options
39505  */
39506
39507
39508
39509 // easy hidden field...
39510 Roo.form.Hidden = function(config){
39511     Roo.form.Hidden.superclass.constructor.call(this, config);
39512 };
39513   
39514 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39515     fieldLabel:      '',
39516     inputType:      'hidden',
39517     width:          50,
39518     allowBlank:     true,
39519     labelSeparator: '',
39520     hidden:         true,
39521     itemCls :       'x-form-item-display-none'
39522
39523
39524 });
39525
39526
39527 /*
39528  * Based on:
39529  * Ext JS Library 1.1.1
39530  * Copyright(c) 2006-2007, Ext JS, LLC.
39531  *
39532  * Originally Released Under LGPL - original licence link has changed is not relivant.
39533  *
39534  * Fork - LGPL
39535  * <script type="text/javascript">
39536  */
39537  
39538 /**
39539  * @class Roo.form.TriggerField
39540  * @extends Roo.form.TextField
39541  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39542  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39543  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39544  * for which you can provide a custom implementation.  For example:
39545  * <pre><code>
39546 var trigger = new Roo.form.TriggerField();
39547 trigger.onTriggerClick = myTriggerFn;
39548 trigger.applyTo('my-field');
39549 </code></pre>
39550  *
39551  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39552  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39553  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39554  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39555  * @constructor
39556  * Create a new TriggerField.
39557  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39558  * to the base TextField)
39559  */
39560 Roo.form.TriggerField = function(config){
39561     this.mimicing = false;
39562     Roo.form.TriggerField.superclass.constructor.call(this, config);
39563 };
39564
39565 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39566     /**
39567      * @cfg {String} triggerClass A CSS class to apply to the trigger
39568      */
39569     /**
39570      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39571      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39572      */
39573     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39574     /**
39575      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39576      */
39577     hideTrigger:false,
39578
39579     /** @cfg {Boolean} grow @hide */
39580     /** @cfg {Number} growMin @hide */
39581     /** @cfg {Number} growMax @hide */
39582
39583     /**
39584      * @hide 
39585      * @method
39586      */
39587     autoSize: Roo.emptyFn,
39588     // private
39589     monitorTab : true,
39590     // private
39591     deferHeight : true,
39592
39593     
39594     actionMode : 'wrap',
39595     // private
39596     onResize : function(w, h){
39597         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39598         if(typeof w == 'number'){
39599             var x = w - this.trigger.getWidth();
39600             this.el.setWidth(this.adjustWidth('input', x));
39601             this.trigger.setStyle('left', x+'px');
39602         }
39603     },
39604
39605     // private
39606     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39607
39608     // private
39609     getResizeEl : function(){
39610         return this.wrap;
39611     },
39612
39613     // private
39614     getPositionEl : function(){
39615         return this.wrap;
39616     },
39617
39618     // private
39619     alignErrorIcon : function(){
39620         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39621     },
39622
39623     // private
39624     onRender : function(ct, position){
39625         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39626         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39627         this.trigger = this.wrap.createChild(this.triggerConfig ||
39628                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39629         if(this.hideTrigger){
39630             this.trigger.setDisplayed(false);
39631         }
39632         this.initTrigger();
39633         if(!this.width){
39634             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39635         }
39636     },
39637
39638     // private
39639     initTrigger : function(){
39640         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39641         this.trigger.addClassOnOver('x-form-trigger-over');
39642         this.trigger.addClassOnClick('x-form-trigger-click');
39643     },
39644
39645     // private
39646     onDestroy : function(){
39647         if(this.trigger){
39648             this.trigger.removeAllListeners();
39649             this.trigger.remove();
39650         }
39651         if(this.wrap){
39652             this.wrap.remove();
39653         }
39654         Roo.form.TriggerField.superclass.onDestroy.call(this);
39655     },
39656
39657     // private
39658     onFocus : function(){
39659         Roo.form.TriggerField.superclass.onFocus.call(this);
39660         if(!this.mimicing){
39661             this.wrap.addClass('x-trigger-wrap-focus');
39662             this.mimicing = true;
39663             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39664             if(this.monitorTab){
39665                 this.el.on("keydown", this.checkTab, this);
39666             }
39667         }
39668     },
39669
39670     // private
39671     checkTab : function(e){
39672         if(e.getKey() == e.TAB){
39673             this.triggerBlur();
39674         }
39675     },
39676
39677     // private
39678     onBlur : function(){
39679         // do nothing
39680     },
39681
39682     // private
39683     mimicBlur : function(e, t){
39684         if(!this.wrap.contains(t) && this.validateBlur()){
39685             this.triggerBlur();
39686         }
39687     },
39688
39689     // private
39690     triggerBlur : function(){
39691         this.mimicing = false;
39692         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39693         if(this.monitorTab){
39694             this.el.un("keydown", this.checkTab, this);
39695         }
39696         this.wrap.removeClass('x-trigger-wrap-focus');
39697         Roo.form.TriggerField.superclass.onBlur.call(this);
39698     },
39699
39700     // private
39701     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39702     validateBlur : function(e, t){
39703         return true;
39704     },
39705
39706     // private
39707     onDisable : function(){
39708         Roo.form.TriggerField.superclass.onDisable.call(this);
39709         if(this.wrap){
39710             this.wrap.addClass('x-item-disabled');
39711         }
39712     },
39713
39714     // private
39715     onEnable : function(){
39716         Roo.form.TriggerField.superclass.onEnable.call(this);
39717         if(this.wrap){
39718             this.wrap.removeClass('x-item-disabled');
39719         }
39720     },
39721
39722     // private
39723     onShow : function(){
39724         var ae = this.getActionEl();
39725         
39726         if(ae){
39727             ae.dom.style.display = '';
39728             ae.dom.style.visibility = 'visible';
39729         }
39730     },
39731
39732     // private
39733     
39734     onHide : function(){
39735         var ae = this.getActionEl();
39736         ae.dom.style.display = 'none';
39737     },
39738
39739     /**
39740      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39741      * by an implementing function.
39742      * @method
39743      * @param {EventObject} e
39744      */
39745     onTriggerClick : Roo.emptyFn
39746 });
39747
39748 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39749 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39750 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39751 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39752     initComponent : function(){
39753         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39754
39755         this.triggerConfig = {
39756             tag:'span', cls:'x-form-twin-triggers', cn:[
39757             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39758             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39759         ]};
39760     },
39761
39762     getTrigger : function(index){
39763         return this.triggers[index];
39764     },
39765
39766     initTrigger : function(){
39767         var ts = this.trigger.select('.x-form-trigger', true);
39768         this.wrap.setStyle('overflow', 'hidden');
39769         var triggerField = this;
39770         ts.each(function(t, all, index){
39771             t.hide = function(){
39772                 var w = triggerField.wrap.getWidth();
39773                 this.dom.style.display = 'none';
39774                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39775             };
39776             t.show = function(){
39777                 var w = triggerField.wrap.getWidth();
39778                 this.dom.style.display = '';
39779                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39780             };
39781             var triggerIndex = 'Trigger'+(index+1);
39782
39783             if(this['hide'+triggerIndex]){
39784                 t.dom.style.display = 'none';
39785             }
39786             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39787             t.addClassOnOver('x-form-trigger-over');
39788             t.addClassOnClick('x-form-trigger-click');
39789         }, this);
39790         this.triggers = ts.elements;
39791     },
39792
39793     onTrigger1Click : Roo.emptyFn,
39794     onTrigger2Click : Roo.emptyFn
39795 });/*
39796  * Based on:
39797  * Ext JS Library 1.1.1
39798  * Copyright(c) 2006-2007, Ext JS, LLC.
39799  *
39800  * Originally Released Under LGPL - original licence link has changed is not relivant.
39801  *
39802  * Fork - LGPL
39803  * <script type="text/javascript">
39804  */
39805  
39806 /**
39807  * @class Roo.form.TextArea
39808  * @extends Roo.form.TextField
39809  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39810  * support for auto-sizing.
39811  * @constructor
39812  * Creates a new TextArea
39813  * @param {Object} config Configuration options
39814  */
39815 Roo.form.TextArea = function(config){
39816     Roo.form.TextArea.superclass.constructor.call(this, config);
39817     // these are provided exchanges for backwards compat
39818     // minHeight/maxHeight were replaced by growMin/growMax to be
39819     // compatible with TextField growing config values
39820     if(this.minHeight !== undefined){
39821         this.growMin = this.minHeight;
39822     }
39823     if(this.maxHeight !== undefined){
39824         this.growMax = this.maxHeight;
39825     }
39826 };
39827
39828 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39829     /**
39830      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39831      */
39832     growMin : 60,
39833     /**
39834      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39835      */
39836     growMax: 1000,
39837     /**
39838      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39839      * in the field (equivalent to setting overflow: hidden, defaults to false)
39840      */
39841     preventScrollbars: false,
39842     /**
39843      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39844      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39845      */
39846
39847     // private
39848     onRender : function(ct, position){
39849         if(!this.el){
39850             this.defaultAutoCreate = {
39851                 tag: "textarea",
39852                 style:"width:300px;height:60px;",
39853                 autocomplete: "new-password"
39854             };
39855         }
39856         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39857         if(this.grow){
39858             this.textSizeEl = Roo.DomHelper.append(document.body, {
39859                 tag: "pre", cls: "x-form-grow-sizer"
39860             });
39861             if(this.preventScrollbars){
39862                 this.el.setStyle("overflow", "hidden");
39863             }
39864             this.el.setHeight(this.growMin);
39865         }
39866     },
39867
39868     onDestroy : function(){
39869         if(this.textSizeEl){
39870             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39871         }
39872         Roo.form.TextArea.superclass.onDestroy.call(this);
39873     },
39874
39875     // private
39876     onKeyUp : function(e){
39877         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39878             this.autoSize();
39879         }
39880     },
39881
39882     /**
39883      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39884      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39885      */
39886     autoSize : function(){
39887         if(!this.grow || !this.textSizeEl){
39888             return;
39889         }
39890         var el = this.el;
39891         var v = el.dom.value;
39892         var ts = this.textSizeEl;
39893
39894         ts.innerHTML = '';
39895         ts.appendChild(document.createTextNode(v));
39896         v = ts.innerHTML;
39897
39898         Roo.fly(ts).setWidth(this.el.getWidth());
39899         if(v.length < 1){
39900             v = "&#160;&#160;";
39901         }else{
39902             if(Roo.isIE){
39903                 v = v.replace(/\n/g, '<p>&#160;</p>');
39904             }
39905             v += "&#160;\n&#160;";
39906         }
39907         ts.innerHTML = v;
39908         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39909         if(h != this.lastHeight){
39910             this.lastHeight = h;
39911             this.el.setHeight(h);
39912             this.fireEvent("autosize", this, h);
39913         }
39914     }
39915 });/*
39916  * Based on:
39917  * Ext JS Library 1.1.1
39918  * Copyright(c) 2006-2007, Ext JS, LLC.
39919  *
39920  * Originally Released Under LGPL - original licence link has changed is not relivant.
39921  *
39922  * Fork - LGPL
39923  * <script type="text/javascript">
39924  */
39925  
39926
39927 /**
39928  * @class Roo.form.NumberField
39929  * @extends Roo.form.TextField
39930  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39931  * @constructor
39932  * Creates a new NumberField
39933  * @param {Object} config Configuration options
39934  */
39935 Roo.form.NumberField = function(config){
39936     Roo.form.NumberField.superclass.constructor.call(this, config);
39937 };
39938
39939 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39940     /**
39941      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39942      */
39943     fieldClass: "x-form-field x-form-num-field",
39944     /**
39945      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39946      */
39947     allowDecimals : true,
39948     /**
39949      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39950      */
39951     decimalSeparator : ".",
39952     /**
39953      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39954      */
39955     decimalPrecision : 2,
39956     /**
39957      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39958      */
39959     allowNegative : true,
39960     /**
39961      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39962      */
39963     minValue : Number.NEGATIVE_INFINITY,
39964     /**
39965      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39966      */
39967     maxValue : Number.MAX_VALUE,
39968     /**
39969      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39970      */
39971     minText : "The minimum value for this field is {0}",
39972     /**
39973      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39974      */
39975     maxText : "The maximum value for this field is {0}",
39976     /**
39977      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39978      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39979      */
39980     nanText : "{0} is not a valid number",
39981
39982     // private
39983     initEvents : function(){
39984         Roo.form.NumberField.superclass.initEvents.call(this);
39985         var allowed = "0123456789";
39986         if(this.allowDecimals){
39987             allowed += this.decimalSeparator;
39988         }
39989         if(this.allowNegative){
39990             allowed += "-";
39991         }
39992         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39993         var keyPress = function(e){
39994             var k = e.getKey();
39995             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39996                 return;
39997             }
39998             var c = e.getCharCode();
39999             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40000                 e.stopEvent();
40001             }
40002         };
40003         this.el.on("keypress", keyPress, this);
40004     },
40005
40006     // private
40007     validateValue : function(value){
40008         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40009             return false;
40010         }
40011         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40012              return true;
40013         }
40014         var num = this.parseValue(value);
40015         if(isNaN(num)){
40016             this.markInvalid(String.format(this.nanText, value));
40017             return false;
40018         }
40019         if(num < this.minValue){
40020             this.markInvalid(String.format(this.minText, this.minValue));
40021             return false;
40022         }
40023         if(num > this.maxValue){
40024             this.markInvalid(String.format(this.maxText, this.maxValue));
40025             return false;
40026         }
40027         return true;
40028     },
40029
40030     getValue : function(){
40031         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40032     },
40033
40034     // private
40035     parseValue : function(value){
40036         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40037         return isNaN(value) ? '' : value;
40038     },
40039
40040     // private
40041     fixPrecision : function(value){
40042         var nan = isNaN(value);
40043         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40044             return nan ? '' : value;
40045         }
40046         return parseFloat(value).toFixed(this.decimalPrecision);
40047     },
40048
40049     setValue : function(v){
40050         v = this.fixPrecision(v);
40051         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40052     },
40053
40054     // private
40055     decimalPrecisionFcn : function(v){
40056         return Math.floor(v);
40057     },
40058
40059     beforeBlur : function(){
40060         var v = this.parseValue(this.getRawValue());
40061         if(v){
40062             this.setValue(v);
40063         }
40064     }
40065 });/*
40066  * Based on:
40067  * Ext JS Library 1.1.1
40068  * Copyright(c) 2006-2007, Ext JS, LLC.
40069  *
40070  * Originally Released Under LGPL - original licence link has changed is not relivant.
40071  *
40072  * Fork - LGPL
40073  * <script type="text/javascript">
40074  */
40075  
40076 /**
40077  * @class Roo.form.DateField
40078  * @extends Roo.form.TriggerField
40079  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40080 * @constructor
40081 * Create a new DateField
40082 * @param {Object} config
40083  */
40084 Roo.form.DateField = function(config){
40085     Roo.form.DateField.superclass.constructor.call(this, config);
40086     
40087       this.addEvents({
40088          
40089         /**
40090          * @event select
40091          * Fires when a date is selected
40092              * @param {Roo.form.DateField} combo This combo box
40093              * @param {Date} date The date selected
40094              */
40095         'select' : true
40096          
40097     });
40098     
40099     
40100     if(typeof this.minValue == "string") {
40101         this.minValue = this.parseDate(this.minValue);
40102     }
40103     if(typeof this.maxValue == "string") {
40104         this.maxValue = this.parseDate(this.maxValue);
40105     }
40106     this.ddMatch = null;
40107     if(this.disabledDates){
40108         var dd = this.disabledDates;
40109         var re = "(?:";
40110         for(var i = 0; i < dd.length; i++){
40111             re += dd[i];
40112             if(i != dd.length-1) {
40113                 re += "|";
40114             }
40115         }
40116         this.ddMatch = new RegExp(re + ")");
40117     }
40118 };
40119
40120 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40121     /**
40122      * @cfg {String} format
40123      * The default date format string which can be overriden for localization support.  The format must be
40124      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40125      */
40126     format : "m/d/y",
40127     /**
40128      * @cfg {String} altFormats
40129      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40130      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40131      */
40132     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40133     /**
40134      * @cfg {Array} disabledDays
40135      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40136      */
40137     disabledDays : null,
40138     /**
40139      * @cfg {String} disabledDaysText
40140      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40141      */
40142     disabledDaysText : "Disabled",
40143     /**
40144      * @cfg {Array} disabledDates
40145      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40146      * expression so they are very powerful. Some examples:
40147      * <ul>
40148      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40149      * <li>["03/08", "09/16"] would disable those days for every year</li>
40150      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40151      * <li>["03/../2006"] would disable every day in March 2006</li>
40152      * <li>["^03"] would disable every day in every March</li>
40153      * </ul>
40154      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40155      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40156      */
40157     disabledDates : null,
40158     /**
40159      * @cfg {String} disabledDatesText
40160      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40161      */
40162     disabledDatesText : "Disabled",
40163     /**
40164      * @cfg {Date/String} minValue
40165      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40166      * valid format (defaults to null).
40167      */
40168     minValue : null,
40169     /**
40170      * @cfg {Date/String} maxValue
40171      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40172      * valid format (defaults to null).
40173      */
40174     maxValue : null,
40175     /**
40176      * @cfg {String} minText
40177      * The error text to display when the date in the cell is before minValue (defaults to
40178      * 'The date in this field must be after {minValue}').
40179      */
40180     minText : "The date in this field must be equal to or after {0}",
40181     /**
40182      * @cfg {String} maxText
40183      * The error text to display when the date in the cell is after maxValue (defaults to
40184      * 'The date in this field must be before {maxValue}').
40185      */
40186     maxText : "The date in this field must be equal to or before {0}",
40187     /**
40188      * @cfg {String} invalidText
40189      * The error text to display when the date in the field is invalid (defaults to
40190      * '{value} is not a valid date - it must be in the format {format}').
40191      */
40192     invalidText : "{0} is not a valid date - it must be in the format {1}",
40193     /**
40194      * @cfg {String} triggerClass
40195      * An additional CSS class used to style the trigger button.  The trigger will always get the
40196      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40197      * which displays a calendar icon).
40198      */
40199     triggerClass : 'x-form-date-trigger',
40200     
40201
40202     /**
40203      * @cfg {Boolean} useIso
40204      * if enabled, then the date field will use a hidden field to store the 
40205      * real value as iso formated date. default (false)
40206      */ 
40207     useIso : false,
40208     /**
40209      * @cfg {String/Object} autoCreate
40210      * A DomHelper element spec, or true for a default element spec (defaults to
40211      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40212      */ 
40213     // private
40214     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40215     
40216     // private
40217     hiddenField: false,
40218     
40219     onRender : function(ct, position)
40220     {
40221         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40222         if (this.useIso) {
40223             //this.el.dom.removeAttribute('name'); 
40224             Roo.log("Changing name?");
40225             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40226             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40227                     'before', true);
40228             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40229             // prevent input submission
40230             this.hiddenName = this.name;
40231         }
40232             
40233             
40234     },
40235     
40236     // private
40237     validateValue : function(value)
40238     {
40239         value = this.formatDate(value);
40240         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40241             Roo.log('super failed');
40242             return false;
40243         }
40244         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40245              return true;
40246         }
40247         var svalue = value;
40248         value = this.parseDate(value);
40249         if(!value){
40250             Roo.log('parse date failed' + svalue);
40251             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40252             return false;
40253         }
40254         var time = value.getTime();
40255         if(this.minValue && time < this.minValue.getTime()){
40256             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40257             return false;
40258         }
40259         if(this.maxValue && time > this.maxValue.getTime()){
40260             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40261             return false;
40262         }
40263         if(this.disabledDays){
40264             var day = value.getDay();
40265             for(var i = 0; i < this.disabledDays.length; i++) {
40266                 if(day === this.disabledDays[i]){
40267                     this.markInvalid(this.disabledDaysText);
40268                     return false;
40269                 }
40270             }
40271         }
40272         var fvalue = this.formatDate(value);
40273         if(this.ddMatch && this.ddMatch.test(fvalue)){
40274             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40275             return false;
40276         }
40277         return true;
40278     },
40279
40280     // private
40281     // Provides logic to override the default TriggerField.validateBlur which just returns true
40282     validateBlur : function(){
40283         return !this.menu || !this.menu.isVisible();
40284     },
40285     
40286     getName: function()
40287     {
40288         // returns hidden if it's set..
40289         if (!this.rendered) {return ''};
40290         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40291         
40292     },
40293
40294     /**
40295      * Returns the current date value of the date field.
40296      * @return {Date} The date value
40297      */
40298     getValue : function(){
40299         
40300         return  this.hiddenField ?
40301                 this.hiddenField.value :
40302                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40303     },
40304
40305     /**
40306      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40307      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40308      * (the default format used is "m/d/y").
40309      * <br />Usage:
40310      * <pre><code>
40311 //All of these calls set the same date value (May 4, 2006)
40312
40313 //Pass a date object:
40314 var dt = new Date('5/4/06');
40315 dateField.setValue(dt);
40316
40317 //Pass a date string (default format):
40318 dateField.setValue('5/4/06');
40319
40320 //Pass a date string (custom format):
40321 dateField.format = 'Y-m-d';
40322 dateField.setValue('2006-5-4');
40323 </code></pre>
40324      * @param {String/Date} date The date or valid date string
40325      */
40326     setValue : function(date){
40327         if (this.hiddenField) {
40328             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40329         }
40330         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40331         // make sure the value field is always stored as a date..
40332         this.value = this.parseDate(date);
40333         
40334         
40335     },
40336
40337     // private
40338     parseDate : function(value){
40339         if(!value || value instanceof Date){
40340             return value;
40341         }
40342         var v = Date.parseDate(value, this.format);
40343          if (!v && this.useIso) {
40344             v = Date.parseDate(value, 'Y-m-d');
40345         }
40346         if(!v && this.altFormats){
40347             if(!this.altFormatsArray){
40348                 this.altFormatsArray = this.altFormats.split("|");
40349             }
40350             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40351                 v = Date.parseDate(value, this.altFormatsArray[i]);
40352             }
40353         }
40354         return v;
40355     },
40356
40357     // private
40358     formatDate : function(date, fmt){
40359         return (!date || !(date instanceof Date)) ?
40360                date : date.dateFormat(fmt || this.format);
40361     },
40362
40363     // private
40364     menuListeners : {
40365         select: function(m, d){
40366             
40367             this.setValue(d);
40368             this.fireEvent('select', this, d);
40369         },
40370         show : function(){ // retain focus styling
40371             this.onFocus();
40372         },
40373         hide : function(){
40374             this.focus.defer(10, this);
40375             var ml = this.menuListeners;
40376             this.menu.un("select", ml.select,  this);
40377             this.menu.un("show", ml.show,  this);
40378             this.menu.un("hide", ml.hide,  this);
40379         }
40380     },
40381
40382     // private
40383     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40384     onTriggerClick : function(){
40385         if(this.disabled){
40386             return;
40387         }
40388         if(this.menu == null){
40389             this.menu = new Roo.menu.DateMenu();
40390         }
40391         Roo.apply(this.menu.picker,  {
40392             showClear: this.allowBlank,
40393             minDate : this.minValue,
40394             maxDate : this.maxValue,
40395             disabledDatesRE : this.ddMatch,
40396             disabledDatesText : this.disabledDatesText,
40397             disabledDays : this.disabledDays,
40398             disabledDaysText : this.disabledDaysText,
40399             format : this.useIso ? 'Y-m-d' : this.format,
40400             minText : String.format(this.minText, this.formatDate(this.minValue)),
40401             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40402         });
40403         this.menu.on(Roo.apply({}, this.menuListeners, {
40404             scope:this
40405         }));
40406         this.menu.picker.setValue(this.getValue() || new Date());
40407         this.menu.show(this.el, "tl-bl?");
40408     },
40409
40410     beforeBlur : function(){
40411         var v = this.parseDate(this.getRawValue());
40412         if(v){
40413             this.setValue(v);
40414         }
40415     },
40416
40417     /*@
40418      * overide
40419      * 
40420      */
40421     isDirty : function() {
40422         if(this.disabled) {
40423             return false;
40424         }
40425         
40426         if(typeof(this.startValue) === 'undefined'){
40427             return false;
40428         }
40429         
40430         return String(this.getValue()) !== String(this.startValue);
40431         
40432     }
40433 });/*
40434  * Based on:
40435  * Ext JS Library 1.1.1
40436  * Copyright(c) 2006-2007, Ext JS, LLC.
40437  *
40438  * Originally Released Under LGPL - original licence link has changed is not relivant.
40439  *
40440  * Fork - LGPL
40441  * <script type="text/javascript">
40442  */
40443  
40444 /**
40445  * @class Roo.form.MonthField
40446  * @extends Roo.form.TriggerField
40447  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40448 * @constructor
40449 * Create a new MonthField
40450 * @param {Object} config
40451  */
40452 Roo.form.MonthField = function(config){
40453     
40454     Roo.form.MonthField.superclass.constructor.call(this, config);
40455     
40456       this.addEvents({
40457          
40458         /**
40459          * @event select
40460          * Fires when a date is selected
40461              * @param {Roo.form.MonthFieeld} combo This combo box
40462              * @param {Date} date The date selected
40463              */
40464         'select' : true
40465          
40466     });
40467     
40468     
40469     if(typeof this.minValue == "string") {
40470         this.minValue = this.parseDate(this.minValue);
40471     }
40472     if(typeof this.maxValue == "string") {
40473         this.maxValue = this.parseDate(this.maxValue);
40474     }
40475     this.ddMatch = null;
40476     if(this.disabledDates){
40477         var dd = this.disabledDates;
40478         var re = "(?:";
40479         for(var i = 0; i < dd.length; i++){
40480             re += dd[i];
40481             if(i != dd.length-1) {
40482                 re += "|";
40483             }
40484         }
40485         this.ddMatch = new RegExp(re + ")");
40486     }
40487 };
40488
40489 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40490     /**
40491      * @cfg {String} format
40492      * The default date format string which can be overriden for localization support.  The format must be
40493      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40494      */
40495     format : "M Y",
40496     /**
40497      * @cfg {String} altFormats
40498      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40499      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40500      */
40501     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40502     /**
40503      * @cfg {Array} disabledDays
40504      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40505      */
40506     disabledDays : [0,1,2,3,4,5,6],
40507     /**
40508      * @cfg {String} disabledDaysText
40509      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40510      */
40511     disabledDaysText : "Disabled",
40512     /**
40513      * @cfg {Array} disabledDates
40514      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40515      * expression so they are very powerful. Some examples:
40516      * <ul>
40517      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40518      * <li>["03/08", "09/16"] would disable those days for every year</li>
40519      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40520      * <li>["03/../2006"] would disable every day in March 2006</li>
40521      * <li>["^03"] would disable every day in every March</li>
40522      * </ul>
40523      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40524      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40525      */
40526     disabledDates : null,
40527     /**
40528      * @cfg {String} disabledDatesText
40529      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40530      */
40531     disabledDatesText : "Disabled",
40532     /**
40533      * @cfg {Date/String} minValue
40534      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40535      * valid format (defaults to null).
40536      */
40537     minValue : null,
40538     /**
40539      * @cfg {Date/String} maxValue
40540      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40541      * valid format (defaults to null).
40542      */
40543     maxValue : null,
40544     /**
40545      * @cfg {String} minText
40546      * The error text to display when the date in the cell is before minValue (defaults to
40547      * 'The date in this field must be after {minValue}').
40548      */
40549     minText : "The date in this field must be equal to or after {0}",
40550     /**
40551      * @cfg {String} maxTextf
40552      * The error text to display when the date in the cell is after maxValue (defaults to
40553      * 'The date in this field must be before {maxValue}').
40554      */
40555     maxText : "The date in this field must be equal to or before {0}",
40556     /**
40557      * @cfg {String} invalidText
40558      * The error text to display when the date in the field is invalid (defaults to
40559      * '{value} is not a valid date - it must be in the format {format}').
40560      */
40561     invalidText : "{0} is not a valid date - it must be in the format {1}",
40562     /**
40563      * @cfg {String} triggerClass
40564      * An additional CSS class used to style the trigger button.  The trigger will always get the
40565      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40566      * which displays a calendar icon).
40567      */
40568     triggerClass : 'x-form-date-trigger',
40569     
40570
40571     /**
40572      * @cfg {Boolean} useIso
40573      * if enabled, then the date field will use a hidden field to store the 
40574      * real value as iso formated date. default (true)
40575      */ 
40576     useIso : true,
40577     /**
40578      * @cfg {String/Object} autoCreate
40579      * A DomHelper element spec, or true for a default element spec (defaults to
40580      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40581      */ 
40582     // private
40583     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40584     
40585     // private
40586     hiddenField: false,
40587     
40588     hideMonthPicker : false,
40589     
40590     onRender : function(ct, position)
40591     {
40592         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40593         if (this.useIso) {
40594             this.el.dom.removeAttribute('name'); 
40595             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40596                     'before', true);
40597             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40598             // prevent input submission
40599             this.hiddenName = this.name;
40600         }
40601             
40602             
40603     },
40604     
40605     // private
40606     validateValue : function(value)
40607     {
40608         value = this.formatDate(value);
40609         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40610             return false;
40611         }
40612         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40613              return true;
40614         }
40615         var svalue = value;
40616         value = this.parseDate(value);
40617         if(!value){
40618             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40619             return false;
40620         }
40621         var time = value.getTime();
40622         if(this.minValue && time < this.minValue.getTime()){
40623             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40624             return false;
40625         }
40626         if(this.maxValue && time > this.maxValue.getTime()){
40627             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40628             return false;
40629         }
40630         /*if(this.disabledDays){
40631             var day = value.getDay();
40632             for(var i = 0; i < this.disabledDays.length; i++) {
40633                 if(day === this.disabledDays[i]){
40634                     this.markInvalid(this.disabledDaysText);
40635                     return false;
40636                 }
40637             }
40638         }
40639         */
40640         var fvalue = this.formatDate(value);
40641         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40642             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40643             return false;
40644         }
40645         */
40646         return true;
40647     },
40648
40649     // private
40650     // Provides logic to override the default TriggerField.validateBlur which just returns true
40651     validateBlur : function(){
40652         return !this.menu || !this.menu.isVisible();
40653     },
40654
40655     /**
40656      * Returns the current date value of the date field.
40657      * @return {Date} The date value
40658      */
40659     getValue : function(){
40660         
40661         
40662         
40663         return  this.hiddenField ?
40664                 this.hiddenField.value :
40665                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40666     },
40667
40668     /**
40669      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40670      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40671      * (the default format used is "m/d/y").
40672      * <br />Usage:
40673      * <pre><code>
40674 //All of these calls set the same date value (May 4, 2006)
40675
40676 //Pass a date object:
40677 var dt = new Date('5/4/06');
40678 monthField.setValue(dt);
40679
40680 //Pass a date string (default format):
40681 monthField.setValue('5/4/06');
40682
40683 //Pass a date string (custom format):
40684 monthField.format = 'Y-m-d';
40685 monthField.setValue('2006-5-4');
40686 </code></pre>
40687      * @param {String/Date} date The date or valid date string
40688      */
40689     setValue : function(date){
40690         Roo.log('month setValue' + date);
40691         // can only be first of month..
40692         
40693         var val = this.parseDate(date);
40694         
40695         if (this.hiddenField) {
40696             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40697         }
40698         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40699         this.value = this.parseDate(date);
40700     },
40701
40702     // private
40703     parseDate : function(value){
40704         if(!value || value instanceof Date){
40705             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40706             return value;
40707         }
40708         var v = Date.parseDate(value, this.format);
40709         if (!v && this.useIso) {
40710             v = Date.parseDate(value, 'Y-m-d');
40711         }
40712         if (v) {
40713             // 
40714             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40715         }
40716         
40717         
40718         if(!v && this.altFormats){
40719             if(!this.altFormatsArray){
40720                 this.altFormatsArray = this.altFormats.split("|");
40721             }
40722             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40723                 v = Date.parseDate(value, this.altFormatsArray[i]);
40724             }
40725         }
40726         return v;
40727     },
40728
40729     // private
40730     formatDate : function(date, fmt){
40731         return (!date || !(date instanceof Date)) ?
40732                date : date.dateFormat(fmt || this.format);
40733     },
40734
40735     // private
40736     menuListeners : {
40737         select: function(m, d){
40738             this.setValue(d);
40739             this.fireEvent('select', this, d);
40740         },
40741         show : function(){ // retain focus styling
40742             this.onFocus();
40743         },
40744         hide : function(){
40745             this.focus.defer(10, this);
40746             var ml = this.menuListeners;
40747             this.menu.un("select", ml.select,  this);
40748             this.menu.un("show", ml.show,  this);
40749             this.menu.un("hide", ml.hide,  this);
40750         }
40751     },
40752     // private
40753     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40754     onTriggerClick : function(){
40755         if(this.disabled){
40756             return;
40757         }
40758         if(this.menu == null){
40759             this.menu = new Roo.menu.DateMenu();
40760            
40761         }
40762         
40763         Roo.apply(this.menu.picker,  {
40764             
40765             showClear: this.allowBlank,
40766             minDate : this.minValue,
40767             maxDate : this.maxValue,
40768             disabledDatesRE : this.ddMatch,
40769             disabledDatesText : this.disabledDatesText,
40770             
40771             format : this.useIso ? 'Y-m-d' : this.format,
40772             minText : String.format(this.minText, this.formatDate(this.minValue)),
40773             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40774             
40775         });
40776          this.menu.on(Roo.apply({}, this.menuListeners, {
40777             scope:this
40778         }));
40779        
40780         
40781         var m = this.menu;
40782         var p = m.picker;
40783         
40784         // hide month picker get's called when we called by 'before hide';
40785         
40786         var ignorehide = true;
40787         p.hideMonthPicker  = function(disableAnim){
40788             if (ignorehide) {
40789                 return;
40790             }
40791              if(this.monthPicker){
40792                 Roo.log("hideMonthPicker called");
40793                 if(disableAnim === true){
40794                     this.monthPicker.hide();
40795                 }else{
40796                     this.monthPicker.slideOut('t', {duration:.2});
40797                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40798                     p.fireEvent("select", this, this.value);
40799                     m.hide();
40800                 }
40801             }
40802         }
40803         
40804         Roo.log('picker set value');
40805         Roo.log(this.getValue());
40806         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40807         m.show(this.el, 'tl-bl?');
40808         ignorehide  = false;
40809         // this will trigger hideMonthPicker..
40810         
40811         
40812         // hidden the day picker
40813         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40814         
40815         
40816         
40817       
40818         
40819         p.showMonthPicker.defer(100, p);
40820     
40821         
40822        
40823     },
40824
40825     beforeBlur : function(){
40826         var v = this.parseDate(this.getRawValue());
40827         if(v){
40828             this.setValue(v);
40829         }
40830     }
40831
40832     /** @cfg {Boolean} grow @hide */
40833     /** @cfg {Number} growMin @hide */
40834     /** @cfg {Number} growMax @hide */
40835     /**
40836      * @hide
40837      * @method autoSize
40838      */
40839 });/*
40840  * Based on:
40841  * Ext JS Library 1.1.1
40842  * Copyright(c) 2006-2007, Ext JS, LLC.
40843  *
40844  * Originally Released Under LGPL - original licence link has changed is not relivant.
40845  *
40846  * Fork - LGPL
40847  * <script type="text/javascript">
40848  */
40849  
40850
40851 /**
40852  * @class Roo.form.ComboBox
40853  * @extends Roo.form.TriggerField
40854  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40855  * @constructor
40856  * Create a new ComboBox.
40857  * @param {Object} config Configuration options
40858  */
40859 Roo.form.ComboBox = function(config){
40860     Roo.form.ComboBox.superclass.constructor.call(this, config);
40861     this.addEvents({
40862         /**
40863          * @event expand
40864          * Fires when the dropdown list is expanded
40865              * @param {Roo.form.ComboBox} combo This combo box
40866              */
40867         'expand' : true,
40868         /**
40869          * @event collapse
40870          * Fires when the dropdown list is collapsed
40871              * @param {Roo.form.ComboBox} combo This combo box
40872              */
40873         'collapse' : true,
40874         /**
40875          * @event beforeselect
40876          * Fires before a list item is selected. Return false to cancel the selection.
40877              * @param {Roo.form.ComboBox} combo This combo box
40878              * @param {Roo.data.Record} record The data record returned from the underlying store
40879              * @param {Number} index The index of the selected item in the dropdown list
40880              */
40881         'beforeselect' : true,
40882         /**
40883          * @event select
40884          * Fires when a list item is selected
40885              * @param {Roo.form.ComboBox} combo This combo box
40886              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40887              * @param {Number} index The index of the selected item in the dropdown list
40888              */
40889         'select' : true,
40890         /**
40891          * @event beforequery
40892          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40893          * The event object passed has these properties:
40894              * @param {Roo.form.ComboBox} combo This combo box
40895              * @param {String} query The query
40896              * @param {Boolean} forceAll true to force "all" query
40897              * @param {Boolean} cancel true to cancel the query
40898              * @param {Object} e The query event object
40899              */
40900         'beforequery': true,
40901          /**
40902          * @event add
40903          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40904              * @param {Roo.form.ComboBox} combo This combo box
40905              */
40906         'add' : true,
40907         /**
40908          * @event edit
40909          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40910              * @param {Roo.form.ComboBox} combo This combo box
40911              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40912              */
40913         'edit' : true
40914         
40915         
40916     });
40917     if(this.transform){
40918         this.allowDomMove = false;
40919         var s = Roo.getDom(this.transform);
40920         if(!this.hiddenName){
40921             this.hiddenName = s.name;
40922         }
40923         if(!this.store){
40924             this.mode = 'local';
40925             var d = [], opts = s.options;
40926             for(var i = 0, len = opts.length;i < len; i++){
40927                 var o = opts[i];
40928                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40929                 if(o.selected) {
40930                     this.value = value;
40931                 }
40932                 d.push([value, o.text]);
40933             }
40934             this.store = new Roo.data.SimpleStore({
40935                 'id': 0,
40936                 fields: ['value', 'text'],
40937                 data : d
40938             });
40939             this.valueField = 'value';
40940             this.displayField = 'text';
40941         }
40942         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40943         if(!this.lazyRender){
40944             this.target = true;
40945             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40946             s.parentNode.removeChild(s); // remove it
40947             this.render(this.el.parentNode);
40948         }else{
40949             s.parentNode.removeChild(s); // remove it
40950         }
40951
40952     }
40953     if (this.store) {
40954         this.store = Roo.factory(this.store, Roo.data);
40955     }
40956     
40957     this.selectedIndex = -1;
40958     if(this.mode == 'local'){
40959         if(config.queryDelay === undefined){
40960             this.queryDelay = 10;
40961         }
40962         if(config.minChars === undefined){
40963             this.minChars = 0;
40964         }
40965     }
40966 };
40967
40968 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40969     /**
40970      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40971      */
40972     /**
40973      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40974      * rendering into an Roo.Editor, defaults to false)
40975      */
40976     /**
40977      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40978      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40979      */
40980     /**
40981      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40982      */
40983     /**
40984      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40985      * the dropdown list (defaults to undefined, with no header element)
40986      */
40987
40988      /**
40989      * @cfg {String/Roo.Template} tpl The template to use to render the output
40990      */
40991      
40992     // private
40993     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40994     /**
40995      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40996      */
40997     listWidth: undefined,
40998     /**
40999      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41000      * mode = 'remote' or 'text' if mode = 'local')
41001      */
41002     displayField: undefined,
41003     /**
41004      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41005      * mode = 'remote' or 'value' if mode = 'local'). 
41006      * Note: use of a valueField requires the user make a selection
41007      * in order for a value to be mapped.
41008      */
41009     valueField: undefined,
41010     
41011     
41012     /**
41013      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41014      * field's data value (defaults to the underlying DOM element's name)
41015      */
41016     hiddenName: undefined,
41017     /**
41018      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41019      */
41020     listClass: '',
41021     /**
41022      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41023      */
41024     selectedClass: 'x-combo-selected',
41025     /**
41026      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41027      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41028      * which displays a downward arrow icon).
41029      */
41030     triggerClass : 'x-form-arrow-trigger',
41031     /**
41032      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41033      */
41034     shadow:'sides',
41035     /**
41036      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41037      * anchor positions (defaults to 'tl-bl')
41038      */
41039     listAlign: 'tl-bl?',
41040     /**
41041      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41042      */
41043     maxHeight: 300,
41044     /**
41045      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41046      * query specified by the allQuery config option (defaults to 'query')
41047      */
41048     triggerAction: 'query',
41049     /**
41050      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41051      * (defaults to 4, does not apply if editable = false)
41052      */
41053     minChars : 4,
41054     /**
41055      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41056      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41057      */
41058     typeAhead: false,
41059     /**
41060      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41061      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41062      */
41063     queryDelay: 500,
41064     /**
41065      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41066      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41067      */
41068     pageSize: 0,
41069     /**
41070      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41071      * when editable = true (defaults to false)
41072      */
41073     selectOnFocus:false,
41074     /**
41075      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41076      */
41077     queryParam: 'query',
41078     /**
41079      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41080      * when mode = 'remote' (defaults to 'Loading...')
41081      */
41082     loadingText: 'Loading...',
41083     /**
41084      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41085      */
41086     resizable: false,
41087     /**
41088      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41089      */
41090     handleHeight : 8,
41091     /**
41092      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41093      * traditional select (defaults to true)
41094      */
41095     editable: true,
41096     /**
41097      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41098      */
41099     allQuery: '',
41100     /**
41101      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41102      */
41103     mode: 'remote',
41104     /**
41105      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41106      * listWidth has a higher value)
41107      */
41108     minListWidth : 70,
41109     /**
41110      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41111      * allow the user to set arbitrary text into the field (defaults to false)
41112      */
41113     forceSelection:false,
41114     /**
41115      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41116      * if typeAhead = true (defaults to 250)
41117      */
41118     typeAheadDelay : 250,
41119     /**
41120      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41121      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41122      */
41123     valueNotFoundText : undefined,
41124     /**
41125      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41126      */
41127     blockFocus : false,
41128     
41129     /**
41130      * @cfg {Boolean} disableClear Disable showing of clear button.
41131      */
41132     disableClear : false,
41133     /**
41134      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41135      */
41136     alwaysQuery : false,
41137     
41138     //private
41139     addicon : false,
41140     editicon: false,
41141     
41142     // element that contains real text value.. (when hidden is used..)
41143      
41144     // private
41145     onRender : function(ct, position){
41146         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41147         if(this.hiddenName){
41148             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41149                     'before', true);
41150             this.hiddenField.value =
41151                 this.hiddenValue !== undefined ? this.hiddenValue :
41152                 this.value !== undefined ? this.value : '';
41153
41154             // prevent input submission
41155             this.el.dom.removeAttribute('name');
41156              
41157              
41158         }
41159         if(Roo.isGecko){
41160             this.el.dom.setAttribute('autocomplete', 'off');
41161         }
41162
41163         var cls = 'x-combo-list';
41164
41165         this.list = new Roo.Layer({
41166             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41167         });
41168
41169         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41170         this.list.setWidth(lw);
41171         this.list.swallowEvent('mousewheel');
41172         this.assetHeight = 0;
41173
41174         if(this.title){
41175             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41176             this.assetHeight += this.header.getHeight();
41177         }
41178
41179         this.innerList = this.list.createChild({cls:cls+'-inner'});
41180         this.innerList.on('mouseover', this.onViewOver, this);
41181         this.innerList.on('mousemove', this.onViewMove, this);
41182         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41183         
41184         if(this.allowBlank && !this.pageSize && !this.disableClear){
41185             this.footer = this.list.createChild({cls:cls+'-ft'});
41186             this.pageTb = new Roo.Toolbar(this.footer);
41187            
41188         }
41189         if(this.pageSize){
41190             this.footer = this.list.createChild({cls:cls+'-ft'});
41191             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41192                     {pageSize: this.pageSize});
41193             
41194         }
41195         
41196         if (this.pageTb && this.allowBlank && !this.disableClear) {
41197             var _this = this;
41198             this.pageTb.add(new Roo.Toolbar.Fill(), {
41199                 cls: 'x-btn-icon x-btn-clear',
41200                 text: '&#160;',
41201                 handler: function()
41202                 {
41203                     _this.collapse();
41204                     _this.clearValue();
41205                     _this.onSelect(false, -1);
41206                 }
41207             });
41208         }
41209         if (this.footer) {
41210             this.assetHeight += this.footer.getHeight();
41211         }
41212         
41213
41214         if(!this.tpl){
41215             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41216         }
41217
41218         this.view = new Roo.View(this.innerList, this.tpl, {
41219             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41220         });
41221
41222         this.view.on('click', this.onViewClick, this);
41223
41224         this.store.on('beforeload', this.onBeforeLoad, this);
41225         this.store.on('load', this.onLoad, this);
41226         this.store.on('loadexception', this.onLoadException, this);
41227
41228         if(this.resizable){
41229             this.resizer = new Roo.Resizable(this.list,  {
41230                pinned:true, handles:'se'
41231             });
41232             this.resizer.on('resize', function(r, w, h){
41233                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41234                 this.listWidth = w;
41235                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41236                 this.restrictHeight();
41237             }, this);
41238             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41239         }
41240         if(!this.editable){
41241             this.editable = true;
41242             this.setEditable(false);
41243         }  
41244         
41245         
41246         if (typeof(this.events.add.listeners) != 'undefined') {
41247             
41248             this.addicon = this.wrap.createChild(
41249                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41250        
41251             this.addicon.on('click', function(e) {
41252                 this.fireEvent('add', this);
41253             }, this);
41254         }
41255         if (typeof(this.events.edit.listeners) != 'undefined') {
41256             
41257             this.editicon = this.wrap.createChild(
41258                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41259             if (this.addicon) {
41260                 this.editicon.setStyle('margin-left', '40px');
41261             }
41262             this.editicon.on('click', function(e) {
41263                 
41264                 // we fire even  if inothing is selected..
41265                 this.fireEvent('edit', this, this.lastData );
41266                 
41267             }, this);
41268         }
41269         
41270         
41271         
41272     },
41273
41274     // private
41275     initEvents : function(){
41276         Roo.form.ComboBox.superclass.initEvents.call(this);
41277
41278         this.keyNav = new Roo.KeyNav(this.el, {
41279             "up" : function(e){
41280                 this.inKeyMode = true;
41281                 this.selectPrev();
41282             },
41283
41284             "down" : function(e){
41285                 if(!this.isExpanded()){
41286                     this.onTriggerClick();
41287                 }else{
41288                     this.inKeyMode = true;
41289                     this.selectNext();
41290                 }
41291             },
41292
41293             "enter" : function(e){
41294                 this.onViewClick();
41295                 //return true;
41296             },
41297
41298             "esc" : function(e){
41299                 this.collapse();
41300             },
41301
41302             "tab" : function(e){
41303                 this.onViewClick(false);
41304                 this.fireEvent("specialkey", this, e);
41305                 return true;
41306             },
41307
41308             scope : this,
41309
41310             doRelay : function(foo, bar, hname){
41311                 if(hname == 'down' || this.scope.isExpanded()){
41312                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41313                 }
41314                 return true;
41315             },
41316
41317             forceKeyDown: true
41318         });
41319         this.queryDelay = Math.max(this.queryDelay || 10,
41320                 this.mode == 'local' ? 10 : 250);
41321         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41322         if(this.typeAhead){
41323             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41324         }
41325         if(this.editable !== false){
41326             this.el.on("keyup", this.onKeyUp, this);
41327         }
41328         if(this.forceSelection){
41329             this.on('blur', this.doForce, this);
41330         }
41331     },
41332
41333     onDestroy : function(){
41334         if(this.view){
41335             this.view.setStore(null);
41336             this.view.el.removeAllListeners();
41337             this.view.el.remove();
41338             this.view.purgeListeners();
41339         }
41340         if(this.list){
41341             this.list.destroy();
41342         }
41343         if(this.store){
41344             this.store.un('beforeload', this.onBeforeLoad, this);
41345             this.store.un('load', this.onLoad, this);
41346             this.store.un('loadexception', this.onLoadException, this);
41347         }
41348         Roo.form.ComboBox.superclass.onDestroy.call(this);
41349     },
41350
41351     // private
41352     fireKey : function(e){
41353         if(e.isNavKeyPress() && !this.list.isVisible()){
41354             this.fireEvent("specialkey", this, e);
41355         }
41356     },
41357
41358     // private
41359     onResize: function(w, h){
41360         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41361         
41362         if(typeof w != 'number'){
41363             // we do not handle it!?!?
41364             return;
41365         }
41366         var tw = this.trigger.getWidth();
41367         tw += this.addicon ? this.addicon.getWidth() : 0;
41368         tw += this.editicon ? this.editicon.getWidth() : 0;
41369         var x = w - tw;
41370         this.el.setWidth( this.adjustWidth('input', x));
41371             
41372         this.trigger.setStyle('left', x+'px');
41373         
41374         if(this.list && this.listWidth === undefined){
41375             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41376             this.list.setWidth(lw);
41377             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41378         }
41379         
41380     
41381         
41382     },
41383
41384     /**
41385      * Allow or prevent the user from directly editing the field text.  If false is passed,
41386      * the user will only be able to select from the items defined in the dropdown list.  This method
41387      * is the runtime equivalent of setting the 'editable' config option at config time.
41388      * @param {Boolean} value True to allow the user to directly edit the field text
41389      */
41390     setEditable : function(value){
41391         if(value == this.editable){
41392             return;
41393         }
41394         this.editable = value;
41395         if(!value){
41396             this.el.dom.setAttribute('readOnly', true);
41397             this.el.on('mousedown', this.onTriggerClick,  this);
41398             this.el.addClass('x-combo-noedit');
41399         }else{
41400             this.el.dom.setAttribute('readOnly', false);
41401             this.el.un('mousedown', this.onTriggerClick,  this);
41402             this.el.removeClass('x-combo-noedit');
41403         }
41404     },
41405
41406     // private
41407     onBeforeLoad : function(){
41408         if(!this.hasFocus){
41409             return;
41410         }
41411         this.innerList.update(this.loadingText ?
41412                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41413         this.restrictHeight();
41414         this.selectedIndex = -1;
41415     },
41416
41417     // private
41418     onLoad : function(){
41419         if(!this.hasFocus){
41420             return;
41421         }
41422         if(this.store.getCount() > 0){
41423             this.expand();
41424             this.restrictHeight();
41425             if(this.lastQuery == this.allQuery){
41426                 if(this.editable){
41427                     this.el.dom.select();
41428                 }
41429                 if(!this.selectByValue(this.value, true)){
41430                     this.select(0, true);
41431                 }
41432             }else{
41433                 this.selectNext();
41434                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41435                     this.taTask.delay(this.typeAheadDelay);
41436                 }
41437             }
41438         }else{
41439             this.onEmptyResults();
41440         }
41441         //this.el.focus();
41442     },
41443     // private
41444     onLoadException : function()
41445     {
41446         this.collapse();
41447         Roo.log(this.store.reader.jsonData);
41448         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41449             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41450         }
41451         
41452         
41453     },
41454     // private
41455     onTypeAhead : function(){
41456         if(this.store.getCount() > 0){
41457             var r = this.store.getAt(0);
41458             var newValue = r.data[this.displayField];
41459             var len = newValue.length;
41460             var selStart = this.getRawValue().length;
41461             if(selStart != len){
41462                 this.setRawValue(newValue);
41463                 this.selectText(selStart, newValue.length);
41464             }
41465         }
41466     },
41467
41468     // private
41469     onSelect : function(record, index){
41470         if(this.fireEvent('beforeselect', this, record, index) !== false){
41471             this.setFromData(index > -1 ? record.data : false);
41472             this.collapse();
41473             this.fireEvent('select', this, record, index);
41474         }
41475     },
41476
41477     /**
41478      * Returns the currently selected field value or empty string if no value is set.
41479      * @return {String} value The selected value
41480      */
41481     getValue : function(){
41482         if(this.valueField){
41483             return typeof this.value != 'undefined' ? this.value : '';
41484         }
41485         return Roo.form.ComboBox.superclass.getValue.call(this);
41486     },
41487
41488     /**
41489      * Clears any text/value currently set in the field
41490      */
41491     clearValue : function(){
41492         if(this.hiddenField){
41493             this.hiddenField.value = '';
41494         }
41495         this.value = '';
41496         this.setRawValue('');
41497         this.lastSelectionText = '';
41498         
41499     },
41500
41501     /**
41502      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41503      * will be displayed in the field.  If the value does not match the data value of an existing item,
41504      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41505      * Otherwise the field will be blank (although the value will still be set).
41506      * @param {String} value The value to match
41507      */
41508     setValue : function(v){
41509         var text = v;
41510         if(this.valueField){
41511             var r = this.findRecord(this.valueField, v);
41512             if(r){
41513                 text = r.data[this.displayField];
41514             }else if(this.valueNotFoundText !== undefined){
41515                 text = this.valueNotFoundText;
41516             }
41517         }
41518         this.lastSelectionText = text;
41519         if(this.hiddenField){
41520             this.hiddenField.value = v;
41521         }
41522         Roo.form.ComboBox.superclass.setValue.call(this, text);
41523         this.value = v;
41524     },
41525     /**
41526      * @property {Object} the last set data for the element
41527      */
41528     
41529     lastData : false,
41530     /**
41531      * Sets the value of the field based on a object which is related to the record format for the store.
41532      * @param {Object} value the value to set as. or false on reset?
41533      */
41534     setFromData : function(o){
41535         var dv = ''; // display value
41536         var vv = ''; // value value..
41537         this.lastData = o;
41538         if (this.displayField) {
41539             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41540         } else {
41541             // this is an error condition!!!
41542             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41543         }
41544         
41545         if(this.valueField){
41546             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41547         }
41548         if(this.hiddenField){
41549             this.hiddenField.value = vv;
41550             
41551             this.lastSelectionText = dv;
41552             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41553             this.value = vv;
41554             return;
41555         }
41556         // no hidden field.. - we store the value in 'value', but still display
41557         // display field!!!!
41558         this.lastSelectionText = dv;
41559         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41560         this.value = vv;
41561         
41562         
41563     },
41564     // private
41565     reset : function(){
41566         // overridden so that last data is reset..
41567         this.setValue(this.resetValue);
41568         this.clearInvalid();
41569         this.lastData = false;
41570         if (this.view) {
41571             this.view.clearSelections();
41572         }
41573     },
41574     // private
41575     findRecord : function(prop, value){
41576         var record;
41577         if(this.store.getCount() > 0){
41578             this.store.each(function(r){
41579                 if(r.data[prop] == value){
41580                     record = r;
41581                     return false;
41582                 }
41583                 return true;
41584             });
41585         }
41586         return record;
41587     },
41588     
41589     getName: function()
41590     {
41591         // returns hidden if it's set..
41592         if (!this.rendered) {return ''};
41593         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41594         
41595     },
41596     // private
41597     onViewMove : function(e, t){
41598         this.inKeyMode = false;
41599     },
41600
41601     // private
41602     onViewOver : function(e, t){
41603         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41604             return;
41605         }
41606         var item = this.view.findItemFromChild(t);
41607         if(item){
41608             var index = this.view.indexOf(item);
41609             this.select(index, false);
41610         }
41611     },
41612
41613     // private
41614     onViewClick : function(doFocus)
41615     {
41616         var index = this.view.getSelectedIndexes()[0];
41617         var r = this.store.getAt(index);
41618         if(r){
41619             this.onSelect(r, index);
41620         }
41621         if(doFocus !== false && !this.blockFocus){
41622             this.el.focus();
41623         }
41624     },
41625
41626     // private
41627     restrictHeight : function(){
41628         this.innerList.dom.style.height = '';
41629         var inner = this.innerList.dom;
41630         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41631         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41632         this.list.beginUpdate();
41633         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41634         this.list.alignTo(this.el, this.listAlign);
41635         this.list.endUpdate();
41636     },
41637
41638     // private
41639     onEmptyResults : function(){
41640         this.collapse();
41641     },
41642
41643     /**
41644      * Returns true if the dropdown list is expanded, else false.
41645      */
41646     isExpanded : function(){
41647         return this.list.isVisible();
41648     },
41649
41650     /**
41651      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41652      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41653      * @param {String} value The data value of the item to select
41654      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41655      * selected item if it is not currently in view (defaults to true)
41656      * @return {Boolean} True if the value matched an item in the list, else false
41657      */
41658     selectByValue : function(v, scrollIntoView){
41659         if(v !== undefined && v !== null){
41660             var r = this.findRecord(this.valueField || this.displayField, v);
41661             if(r){
41662                 this.select(this.store.indexOf(r), scrollIntoView);
41663                 return true;
41664             }
41665         }
41666         return false;
41667     },
41668
41669     /**
41670      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41671      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41672      * @param {Number} index The zero-based index of the list item to select
41673      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41674      * selected item if it is not currently in view (defaults to true)
41675      */
41676     select : function(index, scrollIntoView){
41677         this.selectedIndex = index;
41678         this.view.select(index);
41679         if(scrollIntoView !== false){
41680             var el = this.view.getNode(index);
41681             if(el){
41682                 this.innerList.scrollChildIntoView(el, false);
41683             }
41684         }
41685     },
41686
41687     // private
41688     selectNext : function(){
41689         var ct = this.store.getCount();
41690         if(ct > 0){
41691             if(this.selectedIndex == -1){
41692                 this.select(0);
41693             }else if(this.selectedIndex < ct-1){
41694                 this.select(this.selectedIndex+1);
41695             }
41696         }
41697     },
41698
41699     // private
41700     selectPrev : function(){
41701         var ct = this.store.getCount();
41702         if(ct > 0){
41703             if(this.selectedIndex == -1){
41704                 this.select(0);
41705             }else if(this.selectedIndex != 0){
41706                 this.select(this.selectedIndex-1);
41707             }
41708         }
41709     },
41710
41711     // private
41712     onKeyUp : function(e){
41713         if(this.editable !== false && !e.isSpecialKey()){
41714             this.lastKey = e.getKey();
41715             this.dqTask.delay(this.queryDelay);
41716         }
41717     },
41718
41719     // private
41720     validateBlur : function(){
41721         return !this.list || !this.list.isVisible();   
41722     },
41723
41724     // private
41725     initQuery : function(){
41726         this.doQuery(this.getRawValue());
41727     },
41728
41729     // private
41730     doForce : function(){
41731         if(this.el.dom.value.length > 0){
41732             this.el.dom.value =
41733                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41734              
41735         }
41736     },
41737
41738     /**
41739      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41740      * query allowing the query action to be canceled if needed.
41741      * @param {String} query The SQL query to execute
41742      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41743      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41744      * saved in the current store (defaults to false)
41745      */
41746     doQuery : function(q, forceAll){
41747         if(q === undefined || q === null){
41748             q = '';
41749         }
41750         var qe = {
41751             query: q,
41752             forceAll: forceAll,
41753             combo: this,
41754             cancel:false
41755         };
41756         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41757             return false;
41758         }
41759         q = qe.query;
41760         forceAll = qe.forceAll;
41761         if(forceAll === true || (q.length >= this.minChars)){
41762             if(this.lastQuery != q || this.alwaysQuery){
41763                 this.lastQuery = q;
41764                 if(this.mode == 'local'){
41765                     this.selectedIndex = -1;
41766                     if(forceAll){
41767                         this.store.clearFilter();
41768                     }else{
41769                         this.store.filter(this.displayField, q);
41770                     }
41771                     this.onLoad();
41772                 }else{
41773                     this.store.baseParams[this.queryParam] = q;
41774                     this.store.load({
41775                         params: this.getParams(q)
41776                     });
41777                     this.expand();
41778                 }
41779             }else{
41780                 this.selectedIndex = -1;
41781                 this.onLoad();   
41782             }
41783         }
41784     },
41785
41786     // private
41787     getParams : function(q){
41788         var p = {};
41789         //p[this.queryParam] = q;
41790         if(this.pageSize){
41791             p.start = 0;
41792             p.limit = this.pageSize;
41793         }
41794         return p;
41795     },
41796
41797     /**
41798      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41799      */
41800     collapse : function(){
41801         if(!this.isExpanded()){
41802             return;
41803         }
41804         this.list.hide();
41805         Roo.get(document).un('mousedown', this.collapseIf, this);
41806         Roo.get(document).un('mousewheel', this.collapseIf, this);
41807         if (!this.editable) {
41808             Roo.get(document).un('keydown', this.listKeyPress, this);
41809         }
41810         this.fireEvent('collapse', this);
41811     },
41812
41813     // private
41814     collapseIf : function(e){
41815         if(!e.within(this.wrap) && !e.within(this.list)){
41816             this.collapse();
41817         }
41818     },
41819
41820     /**
41821      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41822      */
41823     expand : function(){
41824         if(this.isExpanded() || !this.hasFocus){
41825             return;
41826         }
41827         this.list.alignTo(this.el, this.listAlign);
41828         this.list.show();
41829         Roo.get(document).on('mousedown', this.collapseIf, this);
41830         Roo.get(document).on('mousewheel', this.collapseIf, this);
41831         if (!this.editable) {
41832             Roo.get(document).on('keydown', this.listKeyPress, this);
41833         }
41834         
41835         this.fireEvent('expand', this);
41836     },
41837
41838     // private
41839     // Implements the default empty TriggerField.onTriggerClick function
41840     onTriggerClick : function(){
41841         if(this.disabled){
41842             return;
41843         }
41844         if(this.isExpanded()){
41845             this.collapse();
41846             if (!this.blockFocus) {
41847                 this.el.focus();
41848             }
41849             
41850         }else {
41851             this.hasFocus = true;
41852             if(this.triggerAction == 'all') {
41853                 this.doQuery(this.allQuery, true);
41854             } else {
41855                 this.doQuery(this.getRawValue());
41856             }
41857             if (!this.blockFocus) {
41858                 this.el.focus();
41859             }
41860         }
41861     },
41862     listKeyPress : function(e)
41863     {
41864         //Roo.log('listkeypress');
41865         // scroll to first matching element based on key pres..
41866         if (e.isSpecialKey()) {
41867             return false;
41868         }
41869         var k = String.fromCharCode(e.getKey()).toUpperCase();
41870         //Roo.log(k);
41871         var match  = false;
41872         var csel = this.view.getSelectedNodes();
41873         var cselitem = false;
41874         if (csel.length) {
41875             var ix = this.view.indexOf(csel[0]);
41876             cselitem  = this.store.getAt(ix);
41877             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41878                 cselitem = false;
41879             }
41880             
41881         }
41882         
41883         this.store.each(function(v) { 
41884             if (cselitem) {
41885                 // start at existing selection.
41886                 if (cselitem.id == v.id) {
41887                     cselitem = false;
41888                 }
41889                 return;
41890             }
41891                 
41892             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41893                 match = this.store.indexOf(v);
41894                 return false;
41895             }
41896         }, this);
41897         
41898         if (match === false) {
41899             return true; // no more action?
41900         }
41901         // scroll to?
41902         this.view.select(match);
41903         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41904         sn.scrollIntoView(sn.dom.parentNode, false);
41905     }
41906
41907     /** 
41908     * @cfg {Boolean} grow 
41909     * @hide 
41910     */
41911     /** 
41912     * @cfg {Number} growMin 
41913     * @hide 
41914     */
41915     /** 
41916     * @cfg {Number} growMax 
41917     * @hide 
41918     */
41919     /**
41920      * @hide
41921      * @method autoSize
41922      */
41923 });/*
41924  * Copyright(c) 2010-2012, Roo J Solutions Limited
41925  *
41926  * Licence LGPL
41927  *
41928  */
41929
41930 /**
41931  * @class Roo.form.ComboBoxArray
41932  * @extends Roo.form.TextField
41933  * A facebook style adder... for lists of email / people / countries  etc...
41934  * pick multiple items from a combo box, and shows each one.
41935  *
41936  *  Fred [x]  Brian [x]  [Pick another |v]
41937  *
41938  *
41939  *  For this to work: it needs various extra information
41940  *    - normal combo problay has
41941  *      name, hiddenName
41942  *    + displayField, valueField
41943  *
41944  *    For our purpose...
41945  *
41946  *
41947  *   If we change from 'extends' to wrapping...
41948  *   
41949  *  
41950  *
41951  
41952  
41953  * @constructor
41954  * Create a new ComboBoxArray.
41955  * @param {Object} config Configuration options
41956  */
41957  
41958
41959 Roo.form.ComboBoxArray = function(config)
41960 {
41961     this.addEvents({
41962         /**
41963          * @event beforeremove
41964          * Fires before remove the value from the list
41965              * @param {Roo.form.ComboBoxArray} _self This combo box array
41966              * @param {Roo.form.ComboBoxArray.Item} item removed item
41967              */
41968         'beforeremove' : true,
41969         /**
41970          * @event remove
41971          * Fires when remove the value from the list
41972              * @param {Roo.form.ComboBoxArray} _self This combo box array
41973              * @param {Roo.form.ComboBoxArray.Item} item removed item
41974              */
41975         'remove' : true
41976         
41977         
41978     });
41979     
41980     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41981     
41982     this.items = new Roo.util.MixedCollection(false);
41983     
41984     // construct the child combo...
41985     
41986     
41987     
41988     
41989    
41990     
41991 }
41992
41993  
41994 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41995
41996     /**
41997      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41998      */
41999     
42000     lastData : false,
42001     
42002     // behavies liek a hiddne field
42003     inputType:      'hidden',
42004     /**
42005      * @cfg {Number} width The width of the box that displays the selected element
42006      */ 
42007     width:          300,
42008
42009     
42010     
42011     /**
42012      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42013      */
42014     name : false,
42015     /**
42016      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42017      */
42018     hiddenName : false,
42019     
42020     
42021     // private the array of items that are displayed..
42022     items  : false,
42023     // private - the hidden field el.
42024     hiddenEl : false,
42025     // private - the filed el..
42026     el : false,
42027     
42028     //validateValue : function() { return true; }, // all values are ok!
42029     //onAddClick: function() { },
42030     
42031     onRender : function(ct, position) 
42032     {
42033         
42034         // create the standard hidden element
42035         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42036         
42037         
42038         // give fake names to child combo;
42039         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42040         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42041         
42042         this.combo = Roo.factory(this.combo, Roo.form);
42043         this.combo.onRender(ct, position);
42044         if (typeof(this.combo.width) != 'undefined') {
42045             this.combo.onResize(this.combo.width,0);
42046         }
42047         
42048         this.combo.initEvents();
42049         
42050         // assigned so form know we need to do this..
42051         this.store          = this.combo.store;
42052         this.valueField     = this.combo.valueField;
42053         this.displayField   = this.combo.displayField ;
42054         
42055         
42056         this.combo.wrap.addClass('x-cbarray-grp');
42057         
42058         var cbwrap = this.combo.wrap.createChild(
42059             {tag: 'div', cls: 'x-cbarray-cb'},
42060             this.combo.el.dom
42061         );
42062         
42063              
42064         this.hiddenEl = this.combo.wrap.createChild({
42065             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42066         });
42067         this.el = this.combo.wrap.createChild({
42068             tag: 'input',  type:'hidden' , name: this.name, value : ''
42069         });
42070          //   this.el.dom.removeAttribute("name");
42071         
42072         
42073         this.outerWrap = this.combo.wrap;
42074         this.wrap = cbwrap;
42075         
42076         this.outerWrap.setWidth(this.width);
42077         this.outerWrap.dom.removeChild(this.el.dom);
42078         
42079         this.wrap.dom.appendChild(this.el.dom);
42080         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42081         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42082         
42083         this.combo.trigger.setStyle('position','relative');
42084         this.combo.trigger.setStyle('left', '0px');
42085         this.combo.trigger.setStyle('top', '2px');
42086         
42087         this.combo.el.setStyle('vertical-align', 'text-bottom');
42088         
42089         //this.trigger.setStyle('vertical-align', 'top');
42090         
42091         // this should use the code from combo really... on('add' ....)
42092         if (this.adder) {
42093             
42094         
42095             this.adder = this.outerWrap.createChild(
42096                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42097             var _t = this;
42098             this.adder.on('click', function(e) {
42099                 _t.fireEvent('adderclick', this, e);
42100             }, _t);
42101         }
42102         //var _t = this;
42103         //this.adder.on('click', this.onAddClick, _t);
42104         
42105         
42106         this.combo.on('select', function(cb, rec, ix) {
42107             this.addItem(rec.data);
42108             
42109             cb.setValue('');
42110             cb.el.dom.value = '';
42111             //cb.lastData = rec.data;
42112             // add to list
42113             
42114         }, this);
42115         
42116         
42117     },
42118     
42119     
42120     getName: function()
42121     {
42122         // returns hidden if it's set..
42123         if (!this.rendered) {return ''};
42124         return  this.hiddenName ? this.hiddenName : this.name;
42125         
42126     },
42127     
42128     
42129     onResize: function(w, h){
42130         
42131         return;
42132         // not sure if this is needed..
42133         //this.combo.onResize(w,h);
42134         
42135         if(typeof w != 'number'){
42136             // we do not handle it!?!?
42137             return;
42138         }
42139         var tw = this.combo.trigger.getWidth();
42140         tw += this.addicon ? this.addicon.getWidth() : 0;
42141         tw += this.editicon ? this.editicon.getWidth() : 0;
42142         var x = w - tw;
42143         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42144             
42145         this.combo.trigger.setStyle('left', '0px');
42146         
42147         if(this.list && this.listWidth === undefined){
42148             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42149             this.list.setWidth(lw);
42150             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42151         }
42152         
42153     
42154         
42155     },
42156     
42157     addItem: function(rec)
42158     {
42159         var valueField = this.combo.valueField;
42160         var displayField = this.combo.displayField;
42161         if (this.items.indexOfKey(rec[valueField]) > -1) {
42162             //console.log("GOT " + rec.data.id);
42163             return;
42164         }
42165         
42166         var x = new Roo.form.ComboBoxArray.Item({
42167             //id : rec[this.idField],
42168             data : rec,
42169             displayField : displayField ,
42170             tipField : displayField ,
42171             cb : this
42172         });
42173         // use the 
42174         this.items.add(rec[valueField],x);
42175         // add it before the element..
42176         this.updateHiddenEl();
42177         x.render(this.outerWrap, this.wrap.dom);
42178         // add the image handler..
42179     },
42180     
42181     updateHiddenEl : function()
42182     {
42183         this.validate();
42184         if (!this.hiddenEl) {
42185             return;
42186         }
42187         var ar = [];
42188         var idField = this.combo.valueField;
42189         
42190         this.items.each(function(f) {
42191             ar.push(f.data[idField]);
42192            
42193         });
42194         this.hiddenEl.dom.value = ar.join(',');
42195         this.validate();
42196     },
42197     
42198     reset : function()
42199     {
42200         this.items.clear();
42201         
42202         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42203            el.remove();
42204         });
42205         
42206         this.el.dom.value = '';
42207         if (this.hiddenEl) {
42208             this.hiddenEl.dom.value = '';
42209         }
42210         
42211     },
42212     getValue: function()
42213     {
42214         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42215     },
42216     setValue: function(v) // not a valid action - must use addItems..
42217     {
42218          
42219         this.reset();
42220         
42221         
42222         
42223         if (this.store.isLocal && (typeof(v) == 'string')) {
42224             // then we can use the store to find the values..
42225             // comma seperated at present.. this needs to allow JSON based encoding..
42226             this.hiddenEl.value  = v;
42227             var v_ar = [];
42228             Roo.each(v.split(','), function(k) {
42229                 Roo.log("CHECK " + this.valueField + ',' + k);
42230                 var li = this.store.query(this.valueField, k);
42231                 if (!li.length) {
42232                     return;
42233                 }
42234                 var add = {};
42235                 add[this.valueField] = k;
42236                 add[this.displayField] = li.item(0).data[this.displayField];
42237                 
42238                 this.addItem(add);
42239             }, this) 
42240              
42241         }
42242         if (typeof(v) == 'object' ) {
42243             // then let's assume it's an array of objects..
42244             Roo.each(v, function(l) {
42245                 this.addItem(l);
42246             }, this);
42247              
42248         }
42249         
42250         
42251     },
42252     setFromData: function(v)
42253     {
42254         // this recieves an object, if setValues is called.
42255         this.reset();
42256         this.el.dom.value = v[this.displayField];
42257         this.hiddenEl.dom.value = v[this.valueField];
42258         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42259             return;
42260         }
42261         var kv = v[this.valueField];
42262         var dv = v[this.displayField];
42263         kv = typeof(kv) != 'string' ? '' : kv;
42264         dv = typeof(dv) != 'string' ? '' : dv;
42265         
42266         
42267         var keys = kv.split(',');
42268         var display = dv.split(',');
42269         for (var i = 0 ; i < keys.length; i++) {
42270             
42271             add = {};
42272             add[this.valueField] = keys[i];
42273             add[this.displayField] = display[i];
42274             this.addItem(add);
42275         }
42276       
42277         
42278     },
42279     
42280     /**
42281      * Validates the combox array value
42282      * @return {Boolean} True if the value is valid, else false
42283      */
42284     validate : function(){
42285         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42286             this.clearInvalid();
42287             return true;
42288         }
42289         return false;
42290     },
42291     
42292     validateValue : function(value){
42293         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42294         
42295     },
42296     
42297     /*@
42298      * overide
42299      * 
42300      */
42301     isDirty : function() {
42302         if(this.disabled) {
42303             return false;
42304         }
42305         
42306         try {
42307             var d = Roo.decode(String(this.originalValue));
42308         } catch (e) {
42309             return String(this.getValue()) !== String(this.originalValue);
42310         }
42311         
42312         var originalValue = [];
42313         
42314         for (var i = 0; i < d.length; i++){
42315             originalValue.push(d[i][this.valueField]);
42316         }
42317         
42318         return String(this.getValue()) !== String(originalValue.join(','));
42319         
42320     }
42321     
42322 });
42323
42324
42325
42326 /**
42327  * @class Roo.form.ComboBoxArray.Item
42328  * @extends Roo.BoxComponent
42329  * A selected item in the list
42330  *  Fred [x]  Brian [x]  [Pick another |v]
42331  * 
42332  * @constructor
42333  * Create a new item.
42334  * @param {Object} config Configuration options
42335  */
42336  
42337 Roo.form.ComboBoxArray.Item = function(config) {
42338     config.id = Roo.id();
42339     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42340 }
42341
42342 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42343     data : {},
42344     cb: false,
42345     displayField : false,
42346     tipField : false,
42347     
42348     
42349     defaultAutoCreate : {
42350         tag: 'div',
42351         cls: 'x-cbarray-item',
42352         cn : [ 
42353             { tag: 'div' },
42354             {
42355                 tag: 'img',
42356                 width:16,
42357                 height : 16,
42358                 src : Roo.BLANK_IMAGE_URL ,
42359                 align: 'center'
42360             }
42361         ]
42362         
42363     },
42364     
42365  
42366     onRender : function(ct, position)
42367     {
42368         Roo.form.Field.superclass.onRender.call(this, ct, position);
42369         
42370         if(!this.el){
42371             var cfg = this.getAutoCreate();
42372             this.el = ct.createChild(cfg, position);
42373         }
42374         
42375         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42376         
42377         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42378             this.cb.renderer(this.data) :
42379             String.format('{0}',this.data[this.displayField]);
42380         
42381             
42382         this.el.child('div').dom.setAttribute('qtip',
42383                         String.format('{0}',this.data[this.tipField])
42384         );
42385         
42386         this.el.child('img').on('click', this.remove, this);
42387         
42388     },
42389    
42390     remove : function()
42391     {
42392         if(this.cb.disabled){
42393             return;
42394         }
42395         
42396         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42397             this.cb.items.remove(this);
42398             this.el.child('img').un('click', this.remove, this);
42399             this.el.remove();
42400             this.cb.updateHiddenEl();
42401
42402             this.cb.fireEvent('remove', this.cb, this);
42403         }
42404         
42405     }
42406 });/*
42407  * Based on:
42408  * Ext JS Library 1.1.1
42409  * Copyright(c) 2006-2007, Ext JS, LLC.
42410  *
42411  * Originally Released Under LGPL - original licence link has changed is not relivant.
42412  *
42413  * Fork - LGPL
42414  * <script type="text/javascript">
42415  */
42416 /**
42417  * @class Roo.form.Checkbox
42418  * @extends Roo.form.Field
42419  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42420  * @constructor
42421  * Creates a new Checkbox
42422  * @param {Object} config Configuration options
42423  */
42424 Roo.form.Checkbox = function(config){
42425     Roo.form.Checkbox.superclass.constructor.call(this, config);
42426     this.addEvents({
42427         /**
42428          * @event check
42429          * Fires when the checkbox is checked or unchecked.
42430              * @param {Roo.form.Checkbox} this This checkbox
42431              * @param {Boolean} checked The new checked value
42432              */
42433         check : true
42434     });
42435 };
42436
42437 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42438     /**
42439      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42440      */
42441     focusClass : undefined,
42442     /**
42443      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42444      */
42445     fieldClass: "x-form-field",
42446     /**
42447      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42448      */
42449     checked: false,
42450     /**
42451      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42452      * {tag: "input", type: "checkbox", autocomplete: "off"})
42453      */
42454     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42455     /**
42456      * @cfg {String} boxLabel The text that appears beside the checkbox
42457      */
42458     boxLabel : "",
42459     /**
42460      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42461      */  
42462     inputValue : '1',
42463     /**
42464      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42465      */
42466      valueOff: '0', // value when not checked..
42467
42468     actionMode : 'viewEl', 
42469     //
42470     // private
42471     itemCls : 'x-menu-check-item x-form-item',
42472     groupClass : 'x-menu-group-item',
42473     inputType : 'hidden',
42474     
42475     
42476     inSetChecked: false, // check that we are not calling self...
42477     
42478     inputElement: false, // real input element?
42479     basedOn: false, // ????
42480     
42481     isFormField: true, // not sure where this is needed!!!!
42482
42483     onResize : function(){
42484         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42485         if(!this.boxLabel){
42486             this.el.alignTo(this.wrap, 'c-c');
42487         }
42488     },
42489
42490     initEvents : function(){
42491         Roo.form.Checkbox.superclass.initEvents.call(this);
42492         this.el.on("click", this.onClick,  this);
42493         this.el.on("change", this.onClick,  this);
42494     },
42495
42496
42497     getResizeEl : function(){
42498         return this.wrap;
42499     },
42500
42501     getPositionEl : function(){
42502         return this.wrap;
42503     },
42504
42505     // private
42506     onRender : function(ct, position){
42507         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42508         /*
42509         if(this.inputValue !== undefined){
42510             this.el.dom.value = this.inputValue;
42511         }
42512         */
42513         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42514         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42515         var viewEl = this.wrap.createChild({ 
42516             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42517         this.viewEl = viewEl;   
42518         this.wrap.on('click', this.onClick,  this); 
42519         
42520         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42521         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42522         
42523         
42524         
42525         if(this.boxLabel){
42526             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42527         //    viewEl.on('click', this.onClick,  this); 
42528         }
42529         //if(this.checked){
42530             this.setChecked(this.checked);
42531         //}else{
42532             //this.checked = this.el.dom;
42533         //}
42534
42535     },
42536
42537     // private
42538     initValue : Roo.emptyFn,
42539
42540     /**
42541      * Returns the checked state of the checkbox.
42542      * @return {Boolean} True if checked, else false
42543      */
42544     getValue : function(){
42545         if(this.el){
42546             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42547         }
42548         return this.valueOff;
42549         
42550     },
42551
42552         // private
42553     onClick : function(){ 
42554         if (this.disabled) {
42555             return;
42556         }
42557         this.setChecked(!this.checked);
42558
42559         //if(this.el.dom.checked != this.checked){
42560         //    this.setValue(this.el.dom.checked);
42561        // }
42562     },
42563
42564     /**
42565      * Sets the checked state of the checkbox.
42566      * On is always based on a string comparison between inputValue and the param.
42567      * @param {Boolean/String} value - the value to set 
42568      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42569      */
42570     setValue : function(v,suppressEvent){
42571         
42572         
42573         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42574         //if(this.el && this.el.dom){
42575         //    this.el.dom.checked = this.checked;
42576         //    this.el.dom.defaultChecked = this.checked;
42577         //}
42578         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42579         //this.fireEvent("check", this, this.checked);
42580     },
42581     // private..
42582     setChecked : function(state,suppressEvent)
42583     {
42584         if (this.inSetChecked) {
42585             this.checked = state;
42586             return;
42587         }
42588         
42589     
42590         if(this.wrap){
42591             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42592         }
42593         this.checked = state;
42594         if(suppressEvent !== true){
42595             this.fireEvent('check', this, state);
42596         }
42597         this.inSetChecked = true;
42598         this.el.dom.value = state ? this.inputValue : this.valueOff;
42599         this.inSetChecked = false;
42600         
42601     },
42602     // handle setting of hidden value by some other method!!?!?
42603     setFromHidden: function()
42604     {
42605         if(!this.el){
42606             return;
42607         }
42608         //console.log("SET FROM HIDDEN");
42609         //alert('setFrom hidden');
42610         this.setValue(this.el.dom.value);
42611     },
42612     
42613     onDestroy : function()
42614     {
42615         if(this.viewEl){
42616             Roo.get(this.viewEl).remove();
42617         }
42618          
42619         Roo.form.Checkbox.superclass.onDestroy.call(this);
42620     },
42621     
42622     setBoxLabel : function(str)
42623     {
42624         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42625     }
42626
42627 });/*
42628  * Based on:
42629  * Ext JS Library 1.1.1
42630  * Copyright(c) 2006-2007, Ext JS, LLC.
42631  *
42632  * Originally Released Under LGPL - original licence link has changed is not relivant.
42633  *
42634  * Fork - LGPL
42635  * <script type="text/javascript">
42636  */
42637  
42638 /**
42639  * @class Roo.form.Radio
42640  * @extends Roo.form.Checkbox
42641  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42642  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42643  * @constructor
42644  * Creates a new Radio
42645  * @param {Object} config Configuration options
42646  */
42647 Roo.form.Radio = function(){
42648     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42649 };
42650 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42651     inputType: 'radio',
42652
42653     /**
42654      * If this radio is part of a group, it will return the selected value
42655      * @return {String}
42656      */
42657     getGroupValue : function(){
42658         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42659     },
42660     
42661     
42662     onRender : function(ct, position){
42663         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42664         
42665         if(this.inputValue !== undefined){
42666             this.el.dom.value = this.inputValue;
42667         }
42668          
42669         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42670         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42671         //var viewEl = this.wrap.createChild({ 
42672         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42673         //this.viewEl = viewEl;   
42674         //this.wrap.on('click', this.onClick,  this); 
42675         
42676         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42677         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42678         
42679         
42680         
42681         if(this.boxLabel){
42682             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42683         //    viewEl.on('click', this.onClick,  this); 
42684         }
42685          if(this.checked){
42686             this.el.dom.checked =   'checked' ;
42687         }
42688          
42689     } 
42690     
42691     
42692 });//<script type="text/javascript">
42693
42694 /*
42695  * Based  Ext JS Library 1.1.1
42696  * Copyright(c) 2006-2007, Ext JS, LLC.
42697  * LGPL
42698  *
42699  */
42700  
42701 /**
42702  * @class Roo.HtmlEditorCore
42703  * @extends Roo.Component
42704  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42705  *
42706  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42707  */
42708
42709 Roo.HtmlEditorCore = function(config){
42710     
42711     
42712     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42713     
42714     
42715     this.addEvents({
42716         /**
42717          * @event initialize
42718          * Fires when the editor is fully initialized (including the iframe)
42719          * @param {Roo.HtmlEditorCore} this
42720          */
42721         initialize: true,
42722         /**
42723          * @event activate
42724          * Fires when the editor is first receives the focus. Any insertion must wait
42725          * until after this event.
42726          * @param {Roo.HtmlEditorCore} this
42727          */
42728         activate: true,
42729          /**
42730          * @event beforesync
42731          * Fires before the textarea is updated with content from the editor iframe. Return false
42732          * to cancel the sync.
42733          * @param {Roo.HtmlEditorCore} this
42734          * @param {String} html
42735          */
42736         beforesync: true,
42737          /**
42738          * @event beforepush
42739          * Fires before the iframe editor is updated with content from the textarea. Return false
42740          * to cancel the push.
42741          * @param {Roo.HtmlEditorCore} this
42742          * @param {String} html
42743          */
42744         beforepush: true,
42745          /**
42746          * @event sync
42747          * Fires when the textarea is updated with content from the editor iframe.
42748          * @param {Roo.HtmlEditorCore} this
42749          * @param {String} html
42750          */
42751         sync: true,
42752          /**
42753          * @event push
42754          * Fires when the iframe editor is updated with content from the textarea.
42755          * @param {Roo.HtmlEditorCore} this
42756          * @param {String} html
42757          */
42758         push: true,
42759         
42760         /**
42761          * @event editorevent
42762          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42763          * @param {Roo.HtmlEditorCore} this
42764          */
42765         editorevent: true
42766         
42767     });
42768     
42769     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42770     
42771     // defaults : white / black...
42772     this.applyBlacklists();
42773     
42774     
42775     
42776 };
42777
42778
42779 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42780
42781
42782      /**
42783      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42784      */
42785     
42786     owner : false,
42787     
42788      /**
42789      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42790      *                        Roo.resizable.
42791      */
42792     resizable : false,
42793      /**
42794      * @cfg {Number} height (in pixels)
42795      */   
42796     height: 300,
42797    /**
42798      * @cfg {Number} width (in pixels)
42799      */   
42800     width: 500,
42801     
42802     /**
42803      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42804      * 
42805      */
42806     stylesheets: false,
42807     
42808     // id of frame..
42809     frameId: false,
42810     
42811     // private properties
42812     validationEvent : false,
42813     deferHeight: true,
42814     initialized : false,
42815     activated : false,
42816     sourceEditMode : false,
42817     onFocus : Roo.emptyFn,
42818     iframePad:3,
42819     hideMode:'offsets',
42820     
42821     clearUp: true,
42822     
42823     // blacklist + whitelisted elements..
42824     black: false,
42825     white: false,
42826      
42827     
42828
42829     /**
42830      * Protected method that will not generally be called directly. It
42831      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42832      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42833      */
42834     getDocMarkup : function(){
42835         // body styles..
42836         var st = '';
42837         
42838         // inherit styels from page...?? 
42839         if (this.stylesheets === false) {
42840             
42841             Roo.get(document.head).select('style').each(function(node) {
42842                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42843             });
42844             
42845             Roo.get(document.head).select('link').each(function(node) { 
42846                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42847             });
42848             
42849         } else if (!this.stylesheets.length) {
42850                 // simple..
42851                 st = '<style type="text/css">' +
42852                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42853                    '</style>';
42854         } else { 
42855             
42856         }
42857         
42858         st +=  '<style type="text/css">' +
42859             'IMG { cursor: pointer } ' +
42860         '</style>';
42861
42862         
42863         return '<html><head>' + st  +
42864             //<style type="text/css">' +
42865             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42866             //'</style>' +
42867             ' </head><body class="roo-htmleditor-body"></body></html>';
42868     },
42869
42870     // private
42871     onRender : function(ct, position)
42872     {
42873         var _t = this;
42874         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42875         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42876         
42877         
42878         this.el.dom.style.border = '0 none';
42879         this.el.dom.setAttribute('tabIndex', -1);
42880         this.el.addClass('x-hidden hide');
42881         
42882         
42883         
42884         if(Roo.isIE){ // fix IE 1px bogus margin
42885             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42886         }
42887        
42888         
42889         this.frameId = Roo.id();
42890         
42891          
42892         
42893         var iframe = this.owner.wrap.createChild({
42894             tag: 'iframe',
42895             cls: 'form-control', // bootstrap..
42896             id: this.frameId,
42897             name: this.frameId,
42898             frameBorder : 'no',
42899             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42900         }, this.el
42901         );
42902         
42903         
42904         this.iframe = iframe.dom;
42905
42906          this.assignDocWin();
42907         
42908         this.doc.designMode = 'on';
42909        
42910         this.doc.open();
42911         this.doc.write(this.getDocMarkup());
42912         this.doc.close();
42913
42914         
42915         var task = { // must defer to wait for browser to be ready
42916             run : function(){
42917                 //console.log("run task?" + this.doc.readyState);
42918                 this.assignDocWin();
42919                 if(this.doc.body || this.doc.readyState == 'complete'){
42920                     try {
42921                         this.doc.designMode="on";
42922                     } catch (e) {
42923                         return;
42924                     }
42925                     Roo.TaskMgr.stop(task);
42926                     this.initEditor.defer(10, this);
42927                 }
42928             },
42929             interval : 10,
42930             duration: 10000,
42931             scope: this
42932         };
42933         Roo.TaskMgr.start(task);
42934
42935     },
42936
42937     // private
42938     onResize : function(w, h)
42939     {
42940          Roo.log('resize: ' +w + ',' + h );
42941         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42942         if(!this.iframe){
42943             return;
42944         }
42945         if(typeof w == 'number'){
42946             
42947             this.iframe.style.width = w + 'px';
42948         }
42949         if(typeof h == 'number'){
42950             
42951             this.iframe.style.height = h + 'px';
42952             if(this.doc){
42953                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42954             }
42955         }
42956         
42957     },
42958
42959     /**
42960      * Toggles the editor between standard and source edit mode.
42961      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42962      */
42963     toggleSourceEdit : function(sourceEditMode){
42964         
42965         this.sourceEditMode = sourceEditMode === true;
42966         
42967         if(this.sourceEditMode){
42968  
42969             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42970             
42971         }else{
42972             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42973             //this.iframe.className = '';
42974             this.deferFocus();
42975         }
42976         //this.setSize(this.owner.wrap.getSize());
42977         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42978     },
42979
42980     
42981   
42982
42983     /**
42984      * Protected method that will not generally be called directly. If you need/want
42985      * custom HTML cleanup, this is the method you should override.
42986      * @param {String} html The HTML to be cleaned
42987      * return {String} The cleaned HTML
42988      */
42989     cleanHtml : function(html){
42990         html = String(html);
42991         if(html.length > 5){
42992             if(Roo.isSafari){ // strip safari nonsense
42993                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42994             }
42995         }
42996         if(html == '&nbsp;'){
42997             html = '';
42998         }
42999         return html;
43000     },
43001
43002     /**
43003      * HTML Editor -> Textarea
43004      * Protected method that will not generally be called directly. Syncs the contents
43005      * of the editor iframe with the textarea.
43006      */
43007     syncValue : function(){
43008         if(this.initialized){
43009             var bd = (this.doc.body || this.doc.documentElement);
43010             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43011             var html = bd.innerHTML;
43012             if(Roo.isSafari){
43013                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43014                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43015                 if(m && m[1]){
43016                     html = '<div style="'+m[0]+'">' + html + '</div>';
43017                 }
43018             }
43019             html = this.cleanHtml(html);
43020             // fix up the special chars.. normaly like back quotes in word...
43021             // however we do not want to do this with chinese..
43022             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43023                 var cc = b.charCodeAt();
43024                 if (
43025                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43026                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43027                     (cc >= 0xf900 && cc < 0xfb00 )
43028                 ) {
43029                         return b;
43030                 }
43031                 return "&#"+cc+";" 
43032             });
43033             if(this.owner.fireEvent('beforesync', this, html) !== false){
43034                 this.el.dom.value = html;
43035                 this.owner.fireEvent('sync', this, html);
43036             }
43037         }
43038     },
43039
43040     /**
43041      * Protected method that will not generally be called directly. Pushes the value of the textarea
43042      * into the iframe editor.
43043      */
43044     pushValue : function(){
43045         if(this.initialized){
43046             var v = this.el.dom.value.trim();
43047             
43048 //            if(v.length < 1){
43049 //                v = '&#160;';
43050 //            }
43051             
43052             if(this.owner.fireEvent('beforepush', this, v) !== false){
43053                 var d = (this.doc.body || this.doc.documentElement);
43054                 d.innerHTML = v;
43055                 this.cleanUpPaste();
43056                 this.el.dom.value = d.innerHTML;
43057                 this.owner.fireEvent('push', this, v);
43058             }
43059         }
43060     },
43061
43062     // private
43063     deferFocus : function(){
43064         this.focus.defer(10, this);
43065     },
43066
43067     // doc'ed in Field
43068     focus : function(){
43069         if(this.win && !this.sourceEditMode){
43070             this.win.focus();
43071         }else{
43072             this.el.focus();
43073         }
43074     },
43075     
43076     assignDocWin: function()
43077     {
43078         var iframe = this.iframe;
43079         
43080          if(Roo.isIE){
43081             this.doc = iframe.contentWindow.document;
43082             this.win = iframe.contentWindow;
43083         } else {
43084 //            if (!Roo.get(this.frameId)) {
43085 //                return;
43086 //            }
43087 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43088 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43089             
43090             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43091                 return;
43092             }
43093             
43094             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43095             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43096         }
43097     },
43098     
43099     // private
43100     initEditor : function(){
43101         //console.log("INIT EDITOR");
43102         this.assignDocWin();
43103         
43104         
43105         
43106         this.doc.designMode="on";
43107         this.doc.open();
43108         this.doc.write(this.getDocMarkup());
43109         this.doc.close();
43110         
43111         var dbody = (this.doc.body || this.doc.documentElement);
43112         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43113         // this copies styles from the containing element into thsi one..
43114         // not sure why we need all of this..
43115         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43116         
43117         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43118         //ss['background-attachment'] = 'fixed'; // w3c
43119         dbody.bgProperties = 'fixed'; // ie
43120         //Roo.DomHelper.applyStyles(dbody, ss);
43121         Roo.EventManager.on(this.doc, {
43122             //'mousedown': this.onEditorEvent,
43123             'mouseup': this.onEditorEvent,
43124             'dblclick': this.onEditorEvent,
43125             'click': this.onEditorEvent,
43126             'keyup': this.onEditorEvent,
43127             buffer:100,
43128             scope: this
43129         });
43130         if(Roo.isGecko){
43131             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43132         }
43133         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43134             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43135         }
43136         this.initialized = true;
43137
43138         this.owner.fireEvent('initialize', this);
43139         this.pushValue();
43140     },
43141
43142     // private
43143     onDestroy : function(){
43144         
43145         
43146         
43147         if(this.rendered){
43148             
43149             //for (var i =0; i < this.toolbars.length;i++) {
43150             //    // fixme - ask toolbars for heights?
43151             //    this.toolbars[i].onDestroy();
43152            // }
43153             
43154             //this.wrap.dom.innerHTML = '';
43155             //this.wrap.remove();
43156         }
43157     },
43158
43159     // private
43160     onFirstFocus : function(){
43161         
43162         this.assignDocWin();
43163         
43164         
43165         this.activated = true;
43166          
43167     
43168         if(Roo.isGecko){ // prevent silly gecko errors
43169             this.win.focus();
43170             var s = this.win.getSelection();
43171             if(!s.focusNode || s.focusNode.nodeType != 3){
43172                 var r = s.getRangeAt(0);
43173                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43174                 r.collapse(true);
43175                 this.deferFocus();
43176             }
43177             try{
43178                 this.execCmd('useCSS', true);
43179                 this.execCmd('styleWithCSS', false);
43180             }catch(e){}
43181         }
43182         this.owner.fireEvent('activate', this);
43183     },
43184
43185     // private
43186     adjustFont: function(btn){
43187         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43188         //if(Roo.isSafari){ // safari
43189         //    adjust *= 2;
43190        // }
43191         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43192         if(Roo.isSafari){ // safari
43193             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43194             v =  (v < 10) ? 10 : v;
43195             v =  (v > 48) ? 48 : v;
43196             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43197             
43198         }
43199         
43200         
43201         v = Math.max(1, v+adjust);
43202         
43203         this.execCmd('FontSize', v  );
43204     },
43205
43206     onEditorEvent : function(e)
43207     {
43208         this.owner.fireEvent('editorevent', this, e);
43209       //  this.updateToolbar();
43210         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43211     },
43212
43213     insertTag : function(tg)
43214     {
43215         // could be a bit smarter... -> wrap the current selected tRoo..
43216         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43217             
43218             range = this.createRange(this.getSelection());
43219             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43220             wrappingNode.appendChild(range.extractContents());
43221             range.insertNode(wrappingNode);
43222
43223             return;
43224             
43225             
43226             
43227         }
43228         this.execCmd("formatblock",   tg);
43229         
43230     },
43231     
43232     insertText : function(txt)
43233     {
43234         
43235         
43236         var range = this.createRange();
43237         range.deleteContents();
43238                //alert(Sender.getAttribute('label'));
43239                
43240         range.insertNode(this.doc.createTextNode(txt));
43241     } ,
43242     
43243      
43244
43245     /**
43246      * Executes a Midas editor command on the editor document and performs necessary focus and
43247      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43248      * @param {String} cmd The Midas command
43249      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43250      */
43251     relayCmd : function(cmd, value){
43252         this.win.focus();
43253         this.execCmd(cmd, value);
43254         this.owner.fireEvent('editorevent', this);
43255         //this.updateToolbar();
43256         this.owner.deferFocus();
43257     },
43258
43259     /**
43260      * Executes a Midas editor command directly on the editor document.
43261      * For visual commands, you should use {@link #relayCmd} instead.
43262      * <b>This should only be called after the editor is initialized.</b>
43263      * @param {String} cmd The Midas command
43264      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43265      */
43266     execCmd : function(cmd, value){
43267         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43268         this.syncValue();
43269     },
43270  
43271  
43272    
43273     /**
43274      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43275      * to insert tRoo.
43276      * @param {String} text | dom node.. 
43277      */
43278     insertAtCursor : function(text)
43279     {
43280         
43281         if(!this.activated){
43282             return;
43283         }
43284         /*
43285         if(Roo.isIE){
43286             this.win.focus();
43287             var r = this.doc.selection.createRange();
43288             if(r){
43289                 r.collapse(true);
43290                 r.pasteHTML(text);
43291                 this.syncValue();
43292                 this.deferFocus();
43293             
43294             }
43295             return;
43296         }
43297         */
43298         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43299             this.win.focus();
43300             
43301             
43302             // from jquery ui (MIT licenced)
43303             var range, node;
43304             var win = this.win;
43305             
43306             if (win.getSelection && win.getSelection().getRangeAt) {
43307                 range = win.getSelection().getRangeAt(0);
43308                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43309                 range.insertNode(node);
43310             } else if (win.document.selection && win.document.selection.createRange) {
43311                 // no firefox support
43312                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43313                 win.document.selection.createRange().pasteHTML(txt);
43314             } else {
43315                 // no firefox support
43316                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43317                 this.execCmd('InsertHTML', txt);
43318             } 
43319             
43320             this.syncValue();
43321             
43322             this.deferFocus();
43323         }
43324     },
43325  // private
43326     mozKeyPress : function(e){
43327         if(e.ctrlKey){
43328             var c = e.getCharCode(), cmd;
43329           
43330             if(c > 0){
43331                 c = String.fromCharCode(c).toLowerCase();
43332                 switch(c){
43333                     case 'b':
43334                         cmd = 'bold';
43335                         break;
43336                     case 'i':
43337                         cmd = 'italic';
43338                         break;
43339                     
43340                     case 'u':
43341                         cmd = 'underline';
43342                         break;
43343                     
43344                     case 'v':
43345                         this.cleanUpPaste.defer(100, this);
43346                         return;
43347                         
43348                 }
43349                 if(cmd){
43350                     this.win.focus();
43351                     this.execCmd(cmd);
43352                     this.deferFocus();
43353                     e.preventDefault();
43354                 }
43355                 
43356             }
43357         }
43358     },
43359
43360     // private
43361     fixKeys : function(){ // load time branching for fastest keydown performance
43362         if(Roo.isIE){
43363             return function(e){
43364                 var k = e.getKey(), r;
43365                 if(k == e.TAB){
43366                     e.stopEvent();
43367                     r = this.doc.selection.createRange();
43368                     if(r){
43369                         r.collapse(true);
43370                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43371                         this.deferFocus();
43372                     }
43373                     return;
43374                 }
43375                 
43376                 if(k == e.ENTER){
43377                     r = this.doc.selection.createRange();
43378                     if(r){
43379                         var target = r.parentElement();
43380                         if(!target || target.tagName.toLowerCase() != 'li'){
43381                             e.stopEvent();
43382                             r.pasteHTML('<br />');
43383                             r.collapse(false);
43384                             r.select();
43385                         }
43386                     }
43387                 }
43388                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43389                     this.cleanUpPaste.defer(100, this);
43390                     return;
43391                 }
43392                 
43393                 
43394             };
43395         }else if(Roo.isOpera){
43396             return function(e){
43397                 var k = e.getKey();
43398                 if(k == e.TAB){
43399                     e.stopEvent();
43400                     this.win.focus();
43401                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43402                     this.deferFocus();
43403                 }
43404                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43405                     this.cleanUpPaste.defer(100, this);
43406                     return;
43407                 }
43408                 
43409             };
43410         }else if(Roo.isSafari){
43411             return function(e){
43412                 var k = e.getKey();
43413                 
43414                 if(k == e.TAB){
43415                     e.stopEvent();
43416                     this.execCmd('InsertText','\t');
43417                     this.deferFocus();
43418                     return;
43419                 }
43420                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43421                     this.cleanUpPaste.defer(100, this);
43422                     return;
43423                 }
43424                 
43425              };
43426         }
43427     }(),
43428     
43429     getAllAncestors: function()
43430     {
43431         var p = this.getSelectedNode();
43432         var a = [];
43433         if (!p) {
43434             a.push(p); // push blank onto stack..
43435             p = this.getParentElement();
43436         }
43437         
43438         
43439         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43440             a.push(p);
43441             p = p.parentNode;
43442         }
43443         a.push(this.doc.body);
43444         return a;
43445     },
43446     lastSel : false,
43447     lastSelNode : false,
43448     
43449     
43450     getSelection : function() 
43451     {
43452         this.assignDocWin();
43453         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43454     },
43455     
43456     getSelectedNode: function() 
43457     {
43458         // this may only work on Gecko!!!
43459         
43460         // should we cache this!!!!
43461         
43462         
43463         
43464          
43465         var range = this.createRange(this.getSelection()).cloneRange();
43466         
43467         if (Roo.isIE) {
43468             var parent = range.parentElement();
43469             while (true) {
43470                 var testRange = range.duplicate();
43471                 testRange.moveToElementText(parent);
43472                 if (testRange.inRange(range)) {
43473                     break;
43474                 }
43475                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43476                     break;
43477                 }
43478                 parent = parent.parentElement;
43479             }
43480             return parent;
43481         }
43482         
43483         // is ancestor a text element.
43484         var ac =  range.commonAncestorContainer;
43485         if (ac.nodeType == 3) {
43486             ac = ac.parentNode;
43487         }
43488         
43489         var ar = ac.childNodes;
43490          
43491         var nodes = [];
43492         var other_nodes = [];
43493         var has_other_nodes = false;
43494         for (var i=0;i<ar.length;i++) {
43495             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43496                 continue;
43497             }
43498             // fullly contained node.
43499             
43500             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43501                 nodes.push(ar[i]);
43502                 continue;
43503             }
43504             
43505             // probably selected..
43506             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43507                 other_nodes.push(ar[i]);
43508                 continue;
43509             }
43510             // outer..
43511             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43512                 continue;
43513             }
43514             
43515             
43516             has_other_nodes = true;
43517         }
43518         if (!nodes.length && other_nodes.length) {
43519             nodes= other_nodes;
43520         }
43521         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43522             return false;
43523         }
43524         
43525         return nodes[0];
43526     },
43527     createRange: function(sel)
43528     {
43529         // this has strange effects when using with 
43530         // top toolbar - not sure if it's a great idea.
43531         //this.editor.contentWindow.focus();
43532         if (typeof sel != "undefined") {
43533             try {
43534                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43535             } catch(e) {
43536                 return this.doc.createRange();
43537             }
43538         } else {
43539             return this.doc.createRange();
43540         }
43541     },
43542     getParentElement: function()
43543     {
43544         
43545         this.assignDocWin();
43546         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43547         
43548         var range = this.createRange(sel);
43549          
43550         try {
43551             var p = range.commonAncestorContainer;
43552             while (p.nodeType == 3) { // text node
43553                 p = p.parentNode;
43554             }
43555             return p;
43556         } catch (e) {
43557             return null;
43558         }
43559     
43560     },
43561     /***
43562      *
43563      * Range intersection.. the hard stuff...
43564      *  '-1' = before
43565      *  '0' = hits..
43566      *  '1' = after.
43567      *         [ -- selected range --- ]
43568      *   [fail]                        [fail]
43569      *
43570      *    basically..
43571      *      if end is before start or  hits it. fail.
43572      *      if start is after end or hits it fail.
43573      *
43574      *   if either hits (but other is outside. - then it's not 
43575      *   
43576      *    
43577      **/
43578     
43579     
43580     // @see http://www.thismuchiknow.co.uk/?p=64.
43581     rangeIntersectsNode : function(range, node)
43582     {
43583         var nodeRange = node.ownerDocument.createRange();
43584         try {
43585             nodeRange.selectNode(node);
43586         } catch (e) {
43587             nodeRange.selectNodeContents(node);
43588         }
43589     
43590         var rangeStartRange = range.cloneRange();
43591         rangeStartRange.collapse(true);
43592     
43593         var rangeEndRange = range.cloneRange();
43594         rangeEndRange.collapse(false);
43595     
43596         var nodeStartRange = nodeRange.cloneRange();
43597         nodeStartRange.collapse(true);
43598     
43599         var nodeEndRange = nodeRange.cloneRange();
43600         nodeEndRange.collapse(false);
43601     
43602         return rangeStartRange.compareBoundaryPoints(
43603                  Range.START_TO_START, nodeEndRange) == -1 &&
43604                rangeEndRange.compareBoundaryPoints(
43605                  Range.START_TO_START, nodeStartRange) == 1;
43606         
43607          
43608     },
43609     rangeCompareNode : function(range, node)
43610     {
43611         var nodeRange = node.ownerDocument.createRange();
43612         try {
43613             nodeRange.selectNode(node);
43614         } catch (e) {
43615             nodeRange.selectNodeContents(node);
43616         }
43617         
43618         
43619         range.collapse(true);
43620     
43621         nodeRange.collapse(true);
43622      
43623         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43624         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43625          
43626         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43627         
43628         var nodeIsBefore   =  ss == 1;
43629         var nodeIsAfter    = ee == -1;
43630         
43631         if (nodeIsBefore && nodeIsAfter) {
43632             return 0; // outer
43633         }
43634         if (!nodeIsBefore && nodeIsAfter) {
43635             return 1; //right trailed.
43636         }
43637         
43638         if (nodeIsBefore && !nodeIsAfter) {
43639             return 2;  // left trailed.
43640         }
43641         // fully contined.
43642         return 3;
43643     },
43644
43645     // private? - in a new class?
43646     cleanUpPaste :  function()
43647     {
43648         // cleans up the whole document..
43649         Roo.log('cleanuppaste');
43650         
43651         this.cleanUpChildren(this.doc.body);
43652         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43653         if (clean != this.doc.body.innerHTML) {
43654             this.doc.body.innerHTML = clean;
43655         }
43656         
43657     },
43658     
43659     cleanWordChars : function(input) {// change the chars to hex code
43660         var he = Roo.HtmlEditorCore;
43661         
43662         var output = input;
43663         Roo.each(he.swapCodes, function(sw) { 
43664             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43665             
43666             output = output.replace(swapper, sw[1]);
43667         });
43668         
43669         return output;
43670     },
43671     
43672     
43673     cleanUpChildren : function (n)
43674     {
43675         if (!n.childNodes.length) {
43676             return;
43677         }
43678         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43679            this.cleanUpChild(n.childNodes[i]);
43680         }
43681     },
43682     
43683     
43684         
43685     
43686     cleanUpChild : function (node)
43687     {
43688         var ed = this;
43689         //console.log(node);
43690         if (node.nodeName == "#text") {
43691             // clean up silly Windows -- stuff?
43692             return; 
43693         }
43694         if (node.nodeName == "#comment") {
43695             node.parentNode.removeChild(node);
43696             // clean up silly Windows -- stuff?
43697             return; 
43698         }
43699         var lcname = node.tagName.toLowerCase();
43700         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43701         // whitelist of tags..
43702         
43703         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43704             // remove node.
43705             node.parentNode.removeChild(node);
43706             return;
43707             
43708         }
43709         
43710         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43711         
43712         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43713         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43714         
43715         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43716         //    remove_keep_children = true;
43717         //}
43718         
43719         if (remove_keep_children) {
43720             this.cleanUpChildren(node);
43721             // inserts everything just before this node...
43722             while (node.childNodes.length) {
43723                 var cn = node.childNodes[0];
43724                 node.removeChild(cn);
43725                 node.parentNode.insertBefore(cn, node);
43726             }
43727             node.parentNode.removeChild(node);
43728             return;
43729         }
43730         
43731         if (!node.attributes || !node.attributes.length) {
43732             this.cleanUpChildren(node);
43733             return;
43734         }
43735         
43736         function cleanAttr(n,v)
43737         {
43738             
43739             if (v.match(/^\./) || v.match(/^\//)) {
43740                 return;
43741             }
43742             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43743                 return;
43744             }
43745             if (v.match(/^#/)) {
43746                 return;
43747             }
43748 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43749             node.removeAttribute(n);
43750             
43751         }
43752         
43753         var cwhite = this.cwhite;
43754         var cblack = this.cblack;
43755             
43756         function cleanStyle(n,v)
43757         {
43758             if (v.match(/expression/)) { //XSS?? should we even bother..
43759                 node.removeAttribute(n);
43760                 return;
43761             }
43762             
43763             var parts = v.split(/;/);
43764             var clean = [];
43765             
43766             Roo.each(parts, function(p) {
43767                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43768                 if (!p.length) {
43769                     return true;
43770                 }
43771                 var l = p.split(':').shift().replace(/\s+/g,'');
43772                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43773                 
43774                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43775 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43776                     //node.removeAttribute(n);
43777                     return true;
43778                 }
43779                 //Roo.log()
43780                 // only allow 'c whitelisted system attributes'
43781                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43782 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43783                     //node.removeAttribute(n);
43784                     return true;
43785                 }
43786                 
43787                 
43788                  
43789                 
43790                 clean.push(p);
43791                 return true;
43792             });
43793             if (clean.length) { 
43794                 node.setAttribute(n, clean.join(';'));
43795             } else {
43796                 node.removeAttribute(n);
43797             }
43798             
43799         }
43800         
43801         
43802         for (var i = node.attributes.length-1; i > -1 ; i--) {
43803             var a = node.attributes[i];
43804             //console.log(a);
43805             
43806             if (a.name.toLowerCase().substr(0,2)=='on')  {
43807                 node.removeAttribute(a.name);
43808                 continue;
43809             }
43810             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43811                 node.removeAttribute(a.name);
43812                 continue;
43813             }
43814             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43815                 cleanAttr(a.name,a.value); // fixme..
43816                 continue;
43817             }
43818             if (a.name == 'style') {
43819                 cleanStyle(a.name,a.value);
43820                 continue;
43821             }
43822             /// clean up MS crap..
43823             // tecnically this should be a list of valid class'es..
43824             
43825             
43826             if (a.name == 'class') {
43827                 if (a.value.match(/^Mso/)) {
43828                     node.className = '';
43829                 }
43830                 
43831                 if (a.value.match(/^body$/)) {
43832                     node.className = '';
43833                 }
43834                 continue;
43835             }
43836             
43837             // style cleanup!?
43838             // class cleanup?
43839             
43840         }
43841         
43842         
43843         this.cleanUpChildren(node);
43844         
43845         
43846     },
43847     
43848     /**
43849      * Clean up MS wordisms...
43850      */
43851     cleanWord : function(node)
43852     {
43853         
43854         
43855         if (!node) {
43856             this.cleanWord(this.doc.body);
43857             return;
43858         }
43859         if (node.nodeName == "#text") {
43860             // clean up silly Windows -- stuff?
43861             return; 
43862         }
43863         if (node.nodeName == "#comment") {
43864             node.parentNode.removeChild(node);
43865             // clean up silly Windows -- stuff?
43866             return; 
43867         }
43868         
43869         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43870             node.parentNode.removeChild(node);
43871             return;
43872         }
43873         
43874         // remove - but keep children..
43875         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43876             while (node.childNodes.length) {
43877                 var cn = node.childNodes[0];
43878                 node.removeChild(cn);
43879                 node.parentNode.insertBefore(cn, node);
43880             }
43881             node.parentNode.removeChild(node);
43882             this.iterateChildren(node, this.cleanWord);
43883             return;
43884         }
43885         // clean styles
43886         if (node.className.length) {
43887             
43888             var cn = node.className.split(/\W+/);
43889             var cna = [];
43890             Roo.each(cn, function(cls) {
43891                 if (cls.match(/Mso[a-zA-Z]+/)) {
43892                     return;
43893                 }
43894                 cna.push(cls);
43895             });
43896             node.className = cna.length ? cna.join(' ') : '';
43897             if (!cna.length) {
43898                 node.removeAttribute("class");
43899             }
43900         }
43901         
43902         if (node.hasAttribute("lang")) {
43903             node.removeAttribute("lang");
43904         }
43905         
43906         if (node.hasAttribute("style")) {
43907             
43908             var styles = node.getAttribute("style").split(";");
43909             var nstyle = [];
43910             Roo.each(styles, function(s) {
43911                 if (!s.match(/:/)) {
43912                     return;
43913                 }
43914                 var kv = s.split(":");
43915                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43916                     return;
43917                 }
43918                 // what ever is left... we allow.
43919                 nstyle.push(s);
43920             });
43921             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43922             if (!nstyle.length) {
43923                 node.removeAttribute('style');
43924             }
43925         }
43926         this.iterateChildren(node, this.cleanWord);
43927         
43928         
43929         
43930     },
43931     /**
43932      * iterateChildren of a Node, calling fn each time, using this as the scole..
43933      * @param {DomNode} node node to iterate children of.
43934      * @param {Function} fn method of this class to call on each item.
43935      */
43936     iterateChildren : function(node, fn)
43937     {
43938         if (!node.childNodes.length) {
43939                 return;
43940         }
43941         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43942            fn.call(this, node.childNodes[i])
43943         }
43944     },
43945     
43946     
43947     /**
43948      * cleanTableWidths.
43949      *
43950      * Quite often pasting from word etc.. results in tables with column and widths.
43951      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43952      *
43953      */
43954     cleanTableWidths : function(node)
43955     {
43956          
43957          
43958         if (!node) {
43959             this.cleanTableWidths(this.doc.body);
43960             return;
43961         }
43962         
43963         // ignore list...
43964         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43965             return; 
43966         }
43967         Roo.log(node.tagName);
43968         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43969             this.iterateChildren(node, this.cleanTableWidths);
43970             return;
43971         }
43972         if (node.hasAttribute('width')) {
43973             node.removeAttribute('width');
43974         }
43975         
43976          
43977         if (node.hasAttribute("style")) {
43978             // pretty basic...
43979             
43980             var styles = node.getAttribute("style").split(";");
43981             var nstyle = [];
43982             Roo.each(styles, function(s) {
43983                 if (!s.match(/:/)) {
43984                     return;
43985                 }
43986                 var kv = s.split(":");
43987                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43988                     return;
43989                 }
43990                 // what ever is left... we allow.
43991                 nstyle.push(s);
43992             });
43993             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43994             if (!nstyle.length) {
43995                 node.removeAttribute('style');
43996             }
43997         }
43998         
43999         this.iterateChildren(node, this.cleanTableWidths);
44000         
44001         
44002     },
44003     
44004     
44005     
44006     
44007     domToHTML : function(currentElement, depth, nopadtext) {
44008         
44009         depth = depth || 0;
44010         nopadtext = nopadtext || false;
44011     
44012         if (!currentElement) {
44013             return this.domToHTML(this.doc.body);
44014         }
44015         
44016         //Roo.log(currentElement);
44017         var j;
44018         var allText = false;
44019         var nodeName = currentElement.nodeName;
44020         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44021         
44022         if  (nodeName == '#text') {
44023             
44024             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44025         }
44026         
44027         
44028         var ret = '';
44029         if (nodeName != 'BODY') {
44030              
44031             var i = 0;
44032             // Prints the node tagName, such as <A>, <IMG>, etc
44033             if (tagName) {
44034                 var attr = [];
44035                 for(i = 0; i < currentElement.attributes.length;i++) {
44036                     // quoting?
44037                     var aname = currentElement.attributes.item(i).name;
44038                     if (!currentElement.attributes.item(i).value.length) {
44039                         continue;
44040                     }
44041                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44042                 }
44043                 
44044                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44045             } 
44046             else {
44047                 
44048                 // eack
44049             }
44050         } else {
44051             tagName = false;
44052         }
44053         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44054             return ret;
44055         }
44056         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44057             nopadtext = true;
44058         }
44059         
44060         
44061         // Traverse the tree
44062         i = 0;
44063         var currentElementChild = currentElement.childNodes.item(i);
44064         var allText = true;
44065         var innerHTML  = '';
44066         lastnode = '';
44067         while (currentElementChild) {
44068             // Formatting code (indent the tree so it looks nice on the screen)
44069             var nopad = nopadtext;
44070             if (lastnode == 'SPAN') {
44071                 nopad  = true;
44072             }
44073             // text
44074             if  (currentElementChild.nodeName == '#text') {
44075                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44076                 toadd = nopadtext ? toadd : toadd.trim();
44077                 if (!nopad && toadd.length > 80) {
44078                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44079                 }
44080                 innerHTML  += toadd;
44081                 
44082                 i++;
44083                 currentElementChild = currentElement.childNodes.item(i);
44084                 lastNode = '';
44085                 continue;
44086             }
44087             allText = false;
44088             
44089             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44090                 
44091             // Recursively traverse the tree structure of the child node
44092             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44093             lastnode = currentElementChild.nodeName;
44094             i++;
44095             currentElementChild=currentElement.childNodes.item(i);
44096         }
44097         
44098         ret += innerHTML;
44099         
44100         if (!allText) {
44101                 // The remaining code is mostly for formatting the tree
44102             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44103         }
44104         
44105         
44106         if (tagName) {
44107             ret+= "</"+tagName+">";
44108         }
44109         return ret;
44110         
44111     },
44112         
44113     applyBlacklists : function()
44114     {
44115         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44116         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44117         
44118         this.white = [];
44119         this.black = [];
44120         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44121             if (b.indexOf(tag) > -1) {
44122                 return;
44123             }
44124             this.white.push(tag);
44125             
44126         }, this);
44127         
44128         Roo.each(w, function(tag) {
44129             if (b.indexOf(tag) > -1) {
44130                 return;
44131             }
44132             if (this.white.indexOf(tag) > -1) {
44133                 return;
44134             }
44135             this.white.push(tag);
44136             
44137         }, this);
44138         
44139         
44140         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44141             if (w.indexOf(tag) > -1) {
44142                 return;
44143             }
44144             this.black.push(tag);
44145             
44146         }, this);
44147         
44148         Roo.each(b, function(tag) {
44149             if (w.indexOf(tag) > -1) {
44150                 return;
44151             }
44152             if (this.black.indexOf(tag) > -1) {
44153                 return;
44154             }
44155             this.black.push(tag);
44156             
44157         }, this);
44158         
44159         
44160         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44161         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44162         
44163         this.cwhite = [];
44164         this.cblack = [];
44165         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44166             if (b.indexOf(tag) > -1) {
44167                 return;
44168             }
44169             this.cwhite.push(tag);
44170             
44171         }, this);
44172         
44173         Roo.each(w, function(tag) {
44174             if (b.indexOf(tag) > -1) {
44175                 return;
44176             }
44177             if (this.cwhite.indexOf(tag) > -1) {
44178                 return;
44179             }
44180             this.cwhite.push(tag);
44181             
44182         }, this);
44183         
44184         
44185         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44186             if (w.indexOf(tag) > -1) {
44187                 return;
44188             }
44189             this.cblack.push(tag);
44190             
44191         }, this);
44192         
44193         Roo.each(b, function(tag) {
44194             if (w.indexOf(tag) > -1) {
44195                 return;
44196             }
44197             if (this.cblack.indexOf(tag) > -1) {
44198                 return;
44199             }
44200             this.cblack.push(tag);
44201             
44202         }, this);
44203     },
44204     
44205     setStylesheets : function(stylesheets)
44206     {
44207         if(typeof(stylesheets) == 'string'){
44208             Roo.get(this.iframe.contentDocument.head).createChild({
44209                 tag : 'link',
44210                 rel : 'stylesheet',
44211                 type : 'text/css',
44212                 href : stylesheets
44213             });
44214             
44215             return;
44216         }
44217         var _this = this;
44218      
44219         Roo.each(stylesheets, function(s) {
44220             if(!s.length){
44221                 return;
44222             }
44223             
44224             Roo.get(_this.iframe.contentDocument.head).createChild({
44225                 tag : 'link',
44226                 rel : 'stylesheet',
44227                 type : 'text/css',
44228                 href : s
44229             });
44230         });
44231
44232         
44233     },
44234     
44235     removeStylesheets : function()
44236     {
44237         var _this = this;
44238         
44239         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44240             s.remove();
44241         });
44242     }
44243     
44244     // hide stuff that is not compatible
44245     /**
44246      * @event blur
44247      * @hide
44248      */
44249     /**
44250      * @event change
44251      * @hide
44252      */
44253     /**
44254      * @event focus
44255      * @hide
44256      */
44257     /**
44258      * @event specialkey
44259      * @hide
44260      */
44261     /**
44262      * @cfg {String} fieldClass @hide
44263      */
44264     /**
44265      * @cfg {String} focusClass @hide
44266      */
44267     /**
44268      * @cfg {String} autoCreate @hide
44269      */
44270     /**
44271      * @cfg {String} inputType @hide
44272      */
44273     /**
44274      * @cfg {String} invalidClass @hide
44275      */
44276     /**
44277      * @cfg {String} invalidText @hide
44278      */
44279     /**
44280      * @cfg {String} msgFx @hide
44281      */
44282     /**
44283      * @cfg {String} validateOnBlur @hide
44284      */
44285 });
44286
44287 Roo.HtmlEditorCore.white = [
44288         'area', 'br', 'img', 'input', 'hr', 'wbr',
44289         
44290        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44291        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44292        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44293        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44294        'table',   'ul',         'xmp', 
44295        
44296        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44297       'thead',   'tr', 
44298      
44299       'dir', 'menu', 'ol', 'ul', 'dl',
44300        
44301       'embed',  'object'
44302 ];
44303
44304
44305 Roo.HtmlEditorCore.black = [
44306     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44307         'applet', // 
44308         'base',   'basefont', 'bgsound', 'blink',  'body', 
44309         'frame',  'frameset', 'head',    'html',   'ilayer', 
44310         'iframe', 'layer',  'link',     'meta',    'object',   
44311         'script', 'style' ,'title',  'xml' // clean later..
44312 ];
44313 Roo.HtmlEditorCore.clean = [
44314     'script', 'style', 'title', 'xml'
44315 ];
44316 Roo.HtmlEditorCore.remove = [
44317     'font'
44318 ];
44319 // attributes..
44320
44321 Roo.HtmlEditorCore.ablack = [
44322     'on'
44323 ];
44324     
44325 Roo.HtmlEditorCore.aclean = [ 
44326     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44327 ];
44328
44329 // protocols..
44330 Roo.HtmlEditorCore.pwhite= [
44331         'http',  'https',  'mailto'
44332 ];
44333
44334 // white listed style attributes.
44335 Roo.HtmlEditorCore.cwhite= [
44336       //  'text-align', /// default is to allow most things..
44337       
44338          
44339 //        'font-size'//??
44340 ];
44341
44342 // black listed style attributes.
44343 Roo.HtmlEditorCore.cblack= [
44344       //  'font-size' -- this can be set by the project 
44345 ];
44346
44347
44348 Roo.HtmlEditorCore.swapCodes   =[ 
44349     [    8211, "--" ], 
44350     [    8212, "--" ], 
44351     [    8216,  "'" ],  
44352     [    8217, "'" ],  
44353     [    8220, '"' ],  
44354     [    8221, '"' ],  
44355     [    8226, "*" ],  
44356     [    8230, "..." ]
44357 ]; 
44358
44359     //<script type="text/javascript">
44360
44361 /*
44362  * Ext JS Library 1.1.1
44363  * Copyright(c) 2006-2007, Ext JS, LLC.
44364  * Licence LGPL
44365  * 
44366  */
44367  
44368  
44369 Roo.form.HtmlEditor = function(config){
44370     
44371     
44372     
44373     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44374     
44375     if (!this.toolbars) {
44376         this.toolbars = [];
44377     }
44378     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44379     
44380     
44381 };
44382
44383 /**
44384  * @class Roo.form.HtmlEditor
44385  * @extends Roo.form.Field
44386  * Provides a lightweight HTML Editor component.
44387  *
44388  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44389  * 
44390  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44391  * supported by this editor.</b><br/><br/>
44392  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44393  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44394  */
44395 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44396     /**
44397      * @cfg {Boolean} clearUp
44398      */
44399     clearUp : true,
44400       /**
44401      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44402      */
44403     toolbars : false,
44404    
44405      /**
44406      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44407      *                        Roo.resizable.
44408      */
44409     resizable : false,
44410      /**
44411      * @cfg {Number} height (in pixels)
44412      */   
44413     height: 300,
44414    /**
44415      * @cfg {Number} width (in pixels)
44416      */   
44417     width: 500,
44418     
44419     /**
44420      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44421      * 
44422      */
44423     stylesheets: false,
44424     
44425     
44426      /**
44427      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44428      * 
44429      */
44430     cblack: false,
44431     /**
44432      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44433      * 
44434      */
44435     cwhite: false,
44436     
44437      /**
44438      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44439      * 
44440      */
44441     black: false,
44442     /**
44443      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44444      * 
44445      */
44446     white: false,
44447     
44448     // id of frame..
44449     frameId: false,
44450     
44451     // private properties
44452     validationEvent : false,
44453     deferHeight: true,
44454     initialized : false,
44455     activated : false,
44456     
44457     onFocus : Roo.emptyFn,
44458     iframePad:3,
44459     hideMode:'offsets',
44460     
44461     actionMode : 'container', // defaults to hiding it...
44462     
44463     defaultAutoCreate : { // modified by initCompnoent..
44464         tag: "textarea",
44465         style:"width:500px;height:300px;",
44466         autocomplete: "new-password"
44467     },
44468
44469     // private
44470     initComponent : function(){
44471         this.addEvents({
44472             /**
44473              * @event initialize
44474              * Fires when the editor is fully initialized (including the iframe)
44475              * @param {HtmlEditor} this
44476              */
44477             initialize: true,
44478             /**
44479              * @event activate
44480              * Fires when the editor is first receives the focus. Any insertion must wait
44481              * until after this event.
44482              * @param {HtmlEditor} this
44483              */
44484             activate: true,
44485              /**
44486              * @event beforesync
44487              * Fires before the textarea is updated with content from the editor iframe. Return false
44488              * to cancel the sync.
44489              * @param {HtmlEditor} this
44490              * @param {String} html
44491              */
44492             beforesync: true,
44493              /**
44494              * @event beforepush
44495              * Fires before the iframe editor is updated with content from the textarea. Return false
44496              * to cancel the push.
44497              * @param {HtmlEditor} this
44498              * @param {String} html
44499              */
44500             beforepush: true,
44501              /**
44502              * @event sync
44503              * Fires when the textarea is updated with content from the editor iframe.
44504              * @param {HtmlEditor} this
44505              * @param {String} html
44506              */
44507             sync: true,
44508              /**
44509              * @event push
44510              * Fires when the iframe editor is updated with content from the textarea.
44511              * @param {HtmlEditor} this
44512              * @param {String} html
44513              */
44514             push: true,
44515              /**
44516              * @event editmodechange
44517              * Fires when the editor switches edit modes
44518              * @param {HtmlEditor} this
44519              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44520              */
44521             editmodechange: true,
44522             /**
44523              * @event editorevent
44524              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44525              * @param {HtmlEditor} this
44526              */
44527             editorevent: true,
44528             /**
44529              * @event firstfocus
44530              * Fires when on first focus - needed by toolbars..
44531              * @param {HtmlEditor} this
44532              */
44533             firstfocus: true,
44534             /**
44535              * @event autosave
44536              * Auto save the htmlEditor value as a file into Events
44537              * @param {HtmlEditor} this
44538              */
44539             autosave: true,
44540             /**
44541              * @event savedpreview
44542              * preview the saved version of htmlEditor
44543              * @param {HtmlEditor} this
44544              */
44545             savedpreview: true,
44546             
44547             /**
44548             * @event stylesheetsclick
44549             * Fires when press the Sytlesheets button
44550             * @param {Roo.HtmlEditorCore} this
44551             */
44552             stylesheetsclick: true
44553         });
44554         this.defaultAutoCreate =  {
44555             tag: "textarea",
44556             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44557             autocomplete: "new-password"
44558         };
44559     },
44560
44561     /**
44562      * Protected method that will not generally be called directly. It
44563      * is called when the editor creates its toolbar. Override this method if you need to
44564      * add custom toolbar buttons.
44565      * @param {HtmlEditor} editor
44566      */
44567     createToolbar : function(editor){
44568         Roo.log("create toolbars");
44569         if (!editor.toolbars || !editor.toolbars.length) {
44570             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44571         }
44572         
44573         for (var i =0 ; i < editor.toolbars.length;i++) {
44574             editor.toolbars[i] = Roo.factory(
44575                     typeof(editor.toolbars[i]) == 'string' ?
44576                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44577                 Roo.form.HtmlEditor);
44578             editor.toolbars[i].init(editor);
44579         }
44580          
44581         
44582     },
44583
44584      
44585     // private
44586     onRender : function(ct, position)
44587     {
44588         var _t = this;
44589         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44590         
44591         this.wrap = this.el.wrap({
44592             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44593         });
44594         
44595         this.editorcore.onRender(ct, position);
44596          
44597         if (this.resizable) {
44598             this.resizeEl = new Roo.Resizable(this.wrap, {
44599                 pinned : true,
44600                 wrap: true,
44601                 dynamic : true,
44602                 minHeight : this.height,
44603                 height: this.height,
44604                 handles : this.resizable,
44605                 width: this.width,
44606                 listeners : {
44607                     resize : function(r, w, h) {
44608                         _t.onResize(w,h); // -something
44609                     }
44610                 }
44611             });
44612             
44613         }
44614         this.createToolbar(this);
44615        
44616         
44617         if(!this.width){
44618             this.setSize(this.wrap.getSize());
44619         }
44620         if (this.resizeEl) {
44621             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44622             // should trigger onReize..
44623         }
44624         
44625         this.keyNav = new Roo.KeyNav(this.el, {
44626             
44627             "tab" : function(e){
44628                 e.preventDefault();
44629                 
44630                 var value = this.getValue();
44631                 
44632                 var start = this.el.dom.selectionStart;
44633                 var end = this.el.dom.selectionEnd;
44634                 
44635                 if(!e.shiftKey){
44636                     
44637                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44638                     this.el.dom.setSelectionRange(end + 1, end + 1);
44639                     return;
44640                 }
44641                 
44642                 var f = value.substring(0, start).split("\t");
44643                 
44644                 if(f.pop().length != 0){
44645                     return;
44646                 }
44647                 
44648                 this.setValue(f.join("\t") + value.substring(end));
44649                 this.el.dom.setSelectionRange(start - 1, start - 1);
44650                 
44651             },
44652             
44653             "home" : function(e){
44654                 e.preventDefault();
44655                 
44656                 var curr = this.el.dom.selectionStart;
44657                 var lines = this.getValue().split("\n");
44658                 
44659                 if(!lines.length){
44660                     return;
44661                 }
44662                 
44663                 if(e.ctrlKey){
44664                     this.el.dom.setSelectionRange(0, 0);
44665                     return;
44666                 }
44667                 
44668                 var pos = 0;
44669                 
44670                 for (var i = 0; i < lines.length;i++) {
44671                     pos += lines[i].length;
44672                     
44673                     if(i != 0){
44674                         pos += 1;
44675                     }
44676                     
44677                     if(pos < curr){
44678                         continue;
44679                     }
44680                     
44681                     pos -= lines[i].length;
44682                     
44683                     break;
44684                 }
44685                 
44686                 if(!e.shiftKey){
44687                     this.el.dom.setSelectionRange(pos, pos);
44688                     return;
44689                 }
44690                 
44691                 this.el.dom.selectionStart = pos;
44692                 this.el.dom.selectionEnd = curr;
44693             },
44694             
44695             "end" : function(e){
44696                 e.preventDefault();
44697                 
44698                 var curr = this.el.dom.selectionStart;
44699                 var lines = this.getValue().split("\n");
44700                 
44701                 if(!lines.length){
44702                     return;
44703                 }
44704                 
44705                 if(e.ctrlKey){
44706                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44707                     return;
44708                 }
44709                 
44710                 var pos = 0;
44711                 
44712                 for (var i = 0; i < lines.length;i++) {
44713                     
44714                     pos += lines[i].length;
44715                     
44716                     if(i != 0){
44717                         pos += 1;
44718                     }
44719                     
44720                     if(pos < curr){
44721                         continue;
44722                     }
44723                     
44724                     break;
44725                 }
44726                 
44727                 if(!e.shiftKey){
44728                     this.el.dom.setSelectionRange(pos, pos);
44729                     return;
44730                 }
44731                 
44732                 this.el.dom.selectionStart = curr;
44733                 this.el.dom.selectionEnd = pos;
44734             },
44735
44736             scope : this,
44737
44738             doRelay : function(foo, bar, hname){
44739                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44740             },
44741
44742             forceKeyDown: true
44743         });
44744         
44745 //        if(this.autosave && this.w){
44746 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44747 //        }
44748     },
44749
44750     // private
44751     onResize : function(w, h)
44752     {
44753         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44754         var ew = false;
44755         var eh = false;
44756         
44757         if(this.el ){
44758             if(typeof w == 'number'){
44759                 var aw = w - this.wrap.getFrameWidth('lr');
44760                 this.el.setWidth(this.adjustWidth('textarea', aw));
44761                 ew = aw;
44762             }
44763             if(typeof h == 'number'){
44764                 var tbh = 0;
44765                 for (var i =0; i < this.toolbars.length;i++) {
44766                     // fixme - ask toolbars for heights?
44767                     tbh += this.toolbars[i].tb.el.getHeight();
44768                     if (this.toolbars[i].footer) {
44769                         tbh += this.toolbars[i].footer.el.getHeight();
44770                     }
44771                 }
44772                 
44773                 
44774                 
44775                 
44776                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44777                 ah -= 5; // knock a few pixes off for look..
44778 //                Roo.log(ah);
44779                 this.el.setHeight(this.adjustWidth('textarea', ah));
44780                 var eh = ah;
44781             }
44782         }
44783         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44784         this.editorcore.onResize(ew,eh);
44785         
44786     },
44787
44788     /**
44789      * Toggles the editor between standard and source edit mode.
44790      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44791      */
44792     toggleSourceEdit : function(sourceEditMode)
44793     {
44794         this.editorcore.toggleSourceEdit(sourceEditMode);
44795         
44796         if(this.editorcore.sourceEditMode){
44797             Roo.log('editor - showing textarea');
44798             
44799 //            Roo.log('in');
44800 //            Roo.log(this.syncValue());
44801             this.editorcore.syncValue();
44802             this.el.removeClass('x-hidden');
44803             this.el.dom.removeAttribute('tabIndex');
44804             this.el.focus();
44805             
44806             for (var i = 0; i < this.toolbars.length; i++) {
44807                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44808                     this.toolbars[i].tb.hide();
44809                     this.toolbars[i].footer.hide();
44810                 }
44811             }
44812             
44813         }else{
44814             Roo.log('editor - hiding textarea');
44815 //            Roo.log('out')
44816 //            Roo.log(this.pushValue()); 
44817             this.editorcore.pushValue();
44818             
44819             this.el.addClass('x-hidden');
44820             this.el.dom.setAttribute('tabIndex', -1);
44821             
44822             for (var i = 0; i < this.toolbars.length; i++) {
44823                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44824                     this.toolbars[i].tb.show();
44825                     this.toolbars[i].footer.show();
44826                 }
44827             }
44828             
44829             //this.deferFocus();
44830         }
44831         
44832         this.setSize(this.wrap.getSize());
44833         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44834         
44835         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44836     },
44837  
44838     // private (for BoxComponent)
44839     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44840
44841     // private (for BoxComponent)
44842     getResizeEl : function(){
44843         return this.wrap;
44844     },
44845
44846     // private (for BoxComponent)
44847     getPositionEl : function(){
44848         return this.wrap;
44849     },
44850
44851     // private
44852     initEvents : function(){
44853         this.originalValue = this.getValue();
44854     },
44855
44856     /**
44857      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44858      * @method
44859      */
44860     markInvalid : Roo.emptyFn,
44861     /**
44862      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44863      * @method
44864      */
44865     clearInvalid : Roo.emptyFn,
44866
44867     setValue : function(v){
44868         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44869         this.editorcore.pushValue();
44870     },
44871
44872      
44873     // private
44874     deferFocus : function(){
44875         this.focus.defer(10, this);
44876     },
44877
44878     // doc'ed in Field
44879     focus : function(){
44880         this.editorcore.focus();
44881         
44882     },
44883       
44884
44885     // private
44886     onDestroy : function(){
44887         
44888         
44889         
44890         if(this.rendered){
44891             
44892             for (var i =0; i < this.toolbars.length;i++) {
44893                 // fixme - ask toolbars for heights?
44894                 this.toolbars[i].onDestroy();
44895             }
44896             
44897             this.wrap.dom.innerHTML = '';
44898             this.wrap.remove();
44899         }
44900     },
44901
44902     // private
44903     onFirstFocus : function(){
44904         //Roo.log("onFirstFocus");
44905         this.editorcore.onFirstFocus();
44906          for (var i =0; i < this.toolbars.length;i++) {
44907             this.toolbars[i].onFirstFocus();
44908         }
44909         
44910     },
44911     
44912     // private
44913     syncValue : function()
44914     {
44915         this.editorcore.syncValue();
44916     },
44917     
44918     pushValue : function()
44919     {
44920         this.editorcore.pushValue();
44921     },
44922     
44923     setStylesheets : function(stylesheets)
44924     {
44925         this.editorcore.setStylesheets(stylesheets);
44926     },
44927     
44928     removeStylesheets : function()
44929     {
44930         this.editorcore.removeStylesheets();
44931     }
44932      
44933     
44934     // hide stuff that is not compatible
44935     /**
44936      * @event blur
44937      * @hide
44938      */
44939     /**
44940      * @event change
44941      * @hide
44942      */
44943     /**
44944      * @event focus
44945      * @hide
44946      */
44947     /**
44948      * @event specialkey
44949      * @hide
44950      */
44951     /**
44952      * @cfg {String} fieldClass @hide
44953      */
44954     /**
44955      * @cfg {String} focusClass @hide
44956      */
44957     /**
44958      * @cfg {String} autoCreate @hide
44959      */
44960     /**
44961      * @cfg {String} inputType @hide
44962      */
44963     /**
44964      * @cfg {String} invalidClass @hide
44965      */
44966     /**
44967      * @cfg {String} invalidText @hide
44968      */
44969     /**
44970      * @cfg {String} msgFx @hide
44971      */
44972     /**
44973      * @cfg {String} validateOnBlur @hide
44974      */
44975 });
44976  
44977     // <script type="text/javascript">
44978 /*
44979  * Based on
44980  * Ext JS Library 1.1.1
44981  * Copyright(c) 2006-2007, Ext JS, LLC.
44982  *  
44983  
44984  */
44985
44986 /**
44987  * @class Roo.form.HtmlEditorToolbar1
44988  * Basic Toolbar
44989  * 
44990  * Usage:
44991  *
44992  new Roo.form.HtmlEditor({
44993     ....
44994     toolbars : [
44995         new Roo.form.HtmlEditorToolbar1({
44996             disable : { fonts: 1 , format: 1, ..., ... , ...],
44997             btns : [ .... ]
44998         })
44999     }
45000      
45001  * 
45002  * @cfg {Object} disable List of elements to disable..
45003  * @cfg {Array} btns List of additional buttons.
45004  * 
45005  * 
45006  * NEEDS Extra CSS? 
45007  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45008  */
45009  
45010 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45011 {
45012     
45013     Roo.apply(this, config);
45014     
45015     // default disabled, based on 'good practice'..
45016     this.disable = this.disable || {};
45017     Roo.applyIf(this.disable, {
45018         fontSize : true,
45019         colors : true,
45020         specialElements : true
45021     });
45022     
45023     
45024     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45025     // dont call parent... till later.
45026 }
45027
45028 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45029     
45030     tb: false,
45031     
45032     rendered: false,
45033     
45034     editor : false,
45035     editorcore : false,
45036     /**
45037      * @cfg {Object} disable  List of toolbar elements to disable
45038          
45039      */
45040     disable : false,
45041     
45042     
45043      /**
45044      * @cfg {String} createLinkText The default text for the create link prompt
45045      */
45046     createLinkText : 'Please enter the URL for the link:',
45047     /**
45048      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45049      */
45050     defaultLinkValue : 'http:/'+'/',
45051    
45052     
45053       /**
45054      * @cfg {Array} fontFamilies An array of available font families
45055      */
45056     fontFamilies : [
45057         'Arial',
45058         'Courier New',
45059         'Tahoma',
45060         'Times New Roman',
45061         'Verdana'
45062     ],
45063     
45064     specialChars : [
45065            "&#169;",
45066           "&#174;",     
45067           "&#8482;",    
45068           "&#163;" ,    
45069          // "&#8212;",    
45070           "&#8230;",    
45071           "&#247;" ,    
45072         //  "&#225;" ,     ?? a acute?
45073            "&#8364;"    , //Euro
45074        //   "&#8220;"    ,
45075         //  "&#8221;"    ,
45076         //  "&#8226;"    ,
45077           "&#176;"  //   , // degrees
45078
45079          // "&#233;"     , // e ecute
45080          // "&#250;"     , // u ecute?
45081     ],
45082     
45083     specialElements : [
45084         {
45085             text: "Insert Table",
45086             xtype: 'MenuItem',
45087             xns : Roo.Menu,
45088             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45089                 
45090         },
45091         {    
45092             text: "Insert Image",
45093             xtype: 'MenuItem',
45094             xns : Roo.Menu,
45095             ihtml : '<img src="about:blank"/>'
45096             
45097         }
45098         
45099          
45100     ],
45101     
45102     
45103     inputElements : [ 
45104             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45105             "input:submit", "input:button", "select", "textarea", "label" ],
45106     formats : [
45107         ["p"] ,  
45108         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45109         ["pre"],[ "code"], 
45110         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45111         ['div'],['span']
45112     ],
45113     
45114     cleanStyles : [
45115         "font-size"
45116     ],
45117      /**
45118      * @cfg {String} defaultFont default font to use.
45119      */
45120     defaultFont: 'tahoma',
45121    
45122     fontSelect : false,
45123     
45124     
45125     formatCombo : false,
45126     
45127     init : function(editor)
45128     {
45129         this.editor = editor;
45130         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45131         var editorcore = this.editorcore;
45132         
45133         var _t = this;
45134         
45135         var fid = editorcore.frameId;
45136         var etb = this;
45137         function btn(id, toggle, handler){
45138             var xid = fid + '-'+ id ;
45139             return {
45140                 id : xid,
45141                 cmd : id,
45142                 cls : 'x-btn-icon x-edit-'+id,
45143                 enableToggle:toggle !== false,
45144                 scope: _t, // was editor...
45145                 handler:handler||_t.relayBtnCmd,
45146                 clickEvent:'mousedown',
45147                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45148                 tabIndex:-1
45149             };
45150         }
45151         
45152         
45153         
45154         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45155         this.tb = tb;
45156          // stop form submits
45157         tb.el.on('click', function(e){
45158             e.preventDefault(); // what does this do?
45159         });
45160
45161         if(!this.disable.font) { // && !Roo.isSafari){
45162             /* why no safari for fonts 
45163             editor.fontSelect = tb.el.createChild({
45164                 tag:'select',
45165                 tabIndex: -1,
45166                 cls:'x-font-select',
45167                 html: this.createFontOptions()
45168             });
45169             
45170             editor.fontSelect.on('change', function(){
45171                 var font = editor.fontSelect.dom.value;
45172                 editor.relayCmd('fontname', font);
45173                 editor.deferFocus();
45174             }, editor);
45175             
45176             tb.add(
45177                 editor.fontSelect.dom,
45178                 '-'
45179             );
45180             */
45181             
45182         };
45183         if(!this.disable.formats){
45184             this.formatCombo = new Roo.form.ComboBox({
45185                 store: new Roo.data.SimpleStore({
45186                     id : 'tag',
45187                     fields: ['tag'],
45188                     data : this.formats // from states.js
45189                 }),
45190                 blockFocus : true,
45191                 name : '',
45192                 //autoCreate : {tag: "div",  size: "20"},
45193                 displayField:'tag',
45194                 typeAhead: false,
45195                 mode: 'local',
45196                 editable : false,
45197                 triggerAction: 'all',
45198                 emptyText:'Add tag',
45199                 selectOnFocus:true,
45200                 width:135,
45201                 listeners : {
45202                     'select': function(c, r, i) {
45203                         editorcore.insertTag(r.get('tag'));
45204                         editor.focus();
45205                     }
45206                 }
45207
45208             });
45209             tb.addField(this.formatCombo);
45210             
45211         }
45212         
45213         if(!this.disable.format){
45214             tb.add(
45215                 btn('bold'),
45216                 btn('italic'),
45217                 btn('underline'),
45218                 btn('strikethrough')
45219             );
45220         };
45221         if(!this.disable.fontSize){
45222             tb.add(
45223                 '-',
45224                 
45225                 
45226                 btn('increasefontsize', false, editorcore.adjustFont),
45227                 btn('decreasefontsize', false, editorcore.adjustFont)
45228             );
45229         };
45230         
45231         
45232         if(!this.disable.colors){
45233             tb.add(
45234                 '-', {
45235                     id:editorcore.frameId +'-forecolor',
45236                     cls:'x-btn-icon x-edit-forecolor',
45237                     clickEvent:'mousedown',
45238                     tooltip: this.buttonTips['forecolor'] || undefined,
45239                     tabIndex:-1,
45240                     menu : new Roo.menu.ColorMenu({
45241                         allowReselect: true,
45242                         focus: Roo.emptyFn,
45243                         value:'000000',
45244                         plain:true,
45245                         selectHandler: function(cp, color){
45246                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45247                             editor.deferFocus();
45248                         },
45249                         scope: editorcore,
45250                         clickEvent:'mousedown'
45251                     })
45252                 }, {
45253                     id:editorcore.frameId +'backcolor',
45254                     cls:'x-btn-icon x-edit-backcolor',
45255                     clickEvent:'mousedown',
45256                     tooltip: this.buttonTips['backcolor'] || undefined,
45257                     tabIndex:-1,
45258                     menu : new Roo.menu.ColorMenu({
45259                         focus: Roo.emptyFn,
45260                         value:'FFFFFF',
45261                         plain:true,
45262                         allowReselect: true,
45263                         selectHandler: function(cp, color){
45264                             if(Roo.isGecko){
45265                                 editorcore.execCmd('useCSS', false);
45266                                 editorcore.execCmd('hilitecolor', color);
45267                                 editorcore.execCmd('useCSS', true);
45268                                 editor.deferFocus();
45269                             }else{
45270                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45271                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45272                                 editor.deferFocus();
45273                             }
45274                         },
45275                         scope:editorcore,
45276                         clickEvent:'mousedown'
45277                     })
45278                 }
45279             );
45280         };
45281         // now add all the items...
45282         
45283
45284         if(!this.disable.alignments){
45285             tb.add(
45286                 '-',
45287                 btn('justifyleft'),
45288                 btn('justifycenter'),
45289                 btn('justifyright')
45290             );
45291         };
45292
45293         //if(!Roo.isSafari){
45294             if(!this.disable.links){
45295                 tb.add(
45296                     '-',
45297                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45298                 );
45299             };
45300
45301             if(!this.disable.lists){
45302                 tb.add(
45303                     '-',
45304                     btn('insertorderedlist'),
45305                     btn('insertunorderedlist')
45306                 );
45307             }
45308             if(!this.disable.sourceEdit){
45309                 tb.add(
45310                     '-',
45311                     btn('sourceedit', true, function(btn){
45312                         this.toggleSourceEdit(btn.pressed);
45313                     })
45314                 );
45315             }
45316         //}
45317         
45318         var smenu = { };
45319         // special menu.. - needs to be tidied up..
45320         if (!this.disable.special) {
45321             smenu = {
45322                 text: "&#169;",
45323                 cls: 'x-edit-none',
45324                 
45325                 menu : {
45326                     items : []
45327                 }
45328             };
45329             for (var i =0; i < this.specialChars.length; i++) {
45330                 smenu.menu.items.push({
45331                     
45332                     html: this.specialChars[i],
45333                     handler: function(a,b) {
45334                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45335                         //editor.insertAtCursor(a.html);
45336                         
45337                     },
45338                     tabIndex:-1
45339                 });
45340             }
45341             
45342             
45343             tb.add(smenu);
45344             
45345             
45346         }
45347         
45348         var cmenu = { };
45349         if (!this.disable.cleanStyles) {
45350             cmenu = {
45351                 cls: 'x-btn-icon x-btn-clear',
45352                 
45353                 menu : {
45354                     items : []
45355                 }
45356             };
45357             for (var i =0; i < this.cleanStyles.length; i++) {
45358                 cmenu.menu.items.push({
45359                     actiontype : this.cleanStyles[i],
45360                     html: 'Remove ' + this.cleanStyles[i],
45361                     handler: function(a,b) {
45362 //                        Roo.log(a);
45363 //                        Roo.log(b);
45364                         var c = Roo.get(editorcore.doc.body);
45365                         c.select('[style]').each(function(s) {
45366                             s.dom.style.removeProperty(a.actiontype);
45367                         });
45368                         editorcore.syncValue();
45369                     },
45370                     tabIndex:-1
45371                 });
45372             }
45373              cmenu.menu.items.push({
45374                 actiontype : 'tablewidths',
45375                 html: 'Remove Table Widths',
45376                 handler: function(a,b) {
45377                     editorcore.cleanTableWidths();
45378                     editorcore.syncValue();
45379                 },
45380                 tabIndex:-1
45381             });
45382             cmenu.menu.items.push({
45383                 actiontype : 'word',
45384                 html: 'Remove MS Word Formating',
45385                 handler: function(a,b) {
45386                     editorcore.cleanWord();
45387                     editorcore.syncValue();
45388                 },
45389                 tabIndex:-1
45390             });
45391             
45392             cmenu.menu.items.push({
45393                 actiontype : 'all',
45394                 html: 'Remove All Styles',
45395                 handler: function(a,b) {
45396                     
45397                     var c = Roo.get(editorcore.doc.body);
45398                     c.select('[style]').each(function(s) {
45399                         s.dom.removeAttribute('style');
45400                     });
45401                     editorcore.syncValue();
45402                 },
45403                 tabIndex:-1
45404             });
45405             
45406             cmenu.menu.items.push({
45407                 actiontype : 'all',
45408                 html: 'Remove All CSS Classes',
45409                 handler: function(a,b) {
45410                     
45411                     var c = Roo.get(editorcore.doc.body);
45412                     c.select('[class]').each(function(s) {
45413                         s.dom.className = '';
45414                     });
45415                     editorcore.syncValue();
45416                 },
45417                 tabIndex:-1
45418             });
45419             
45420              cmenu.menu.items.push({
45421                 actiontype : 'tidy',
45422                 html: 'Tidy HTML Source',
45423                 handler: function(a,b) {
45424                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45425                     editorcore.syncValue();
45426                 },
45427                 tabIndex:-1
45428             });
45429             
45430             
45431             tb.add(cmenu);
45432         }
45433          
45434         if (!this.disable.specialElements) {
45435             var semenu = {
45436                 text: "Other;",
45437                 cls: 'x-edit-none',
45438                 menu : {
45439                     items : []
45440                 }
45441             };
45442             for (var i =0; i < this.specialElements.length; i++) {
45443                 semenu.menu.items.push(
45444                     Roo.apply({ 
45445                         handler: function(a,b) {
45446                             editor.insertAtCursor(this.ihtml);
45447                         }
45448                     }, this.specialElements[i])
45449                 );
45450                     
45451             }
45452             
45453             tb.add(semenu);
45454             
45455             
45456         }
45457          
45458         
45459         if (this.btns) {
45460             for(var i =0; i< this.btns.length;i++) {
45461                 var b = Roo.factory(this.btns[i],Roo.form);
45462                 b.cls =  'x-edit-none';
45463                 
45464                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45465                     b.cls += ' x-init-enable';
45466                 }
45467                 
45468                 b.scope = editorcore;
45469                 tb.add(b);
45470             }
45471         
45472         }
45473         
45474         
45475         
45476         // disable everything...
45477         
45478         this.tb.items.each(function(item){
45479             
45480            if(
45481                 item.id != editorcore.frameId+ '-sourceedit' && 
45482                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45483             ){
45484                 
45485                 item.disable();
45486             }
45487         });
45488         this.rendered = true;
45489         
45490         // the all the btns;
45491         editor.on('editorevent', this.updateToolbar, this);
45492         // other toolbars need to implement this..
45493         //editor.on('editmodechange', this.updateToolbar, this);
45494     },
45495     
45496     
45497     relayBtnCmd : function(btn) {
45498         this.editorcore.relayCmd(btn.cmd);
45499     },
45500     // private used internally
45501     createLink : function(){
45502         Roo.log("create link?");
45503         var url = prompt(this.createLinkText, this.defaultLinkValue);
45504         if(url && url != 'http:/'+'/'){
45505             this.editorcore.relayCmd('createlink', url);
45506         }
45507     },
45508
45509     
45510     /**
45511      * Protected method that will not generally be called directly. It triggers
45512      * a toolbar update by reading the markup state of the current selection in the editor.
45513      */
45514     updateToolbar: function(){
45515
45516         if(!this.editorcore.activated){
45517             this.editor.onFirstFocus();
45518             return;
45519         }
45520
45521         var btns = this.tb.items.map, 
45522             doc = this.editorcore.doc,
45523             frameId = this.editorcore.frameId;
45524
45525         if(!this.disable.font && !Roo.isSafari){
45526             /*
45527             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45528             if(name != this.fontSelect.dom.value){
45529                 this.fontSelect.dom.value = name;
45530             }
45531             */
45532         }
45533         if(!this.disable.format){
45534             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45535             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45536             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45537             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45538         }
45539         if(!this.disable.alignments){
45540             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45541             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45542             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45543         }
45544         if(!Roo.isSafari && !this.disable.lists){
45545             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45546             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45547         }
45548         
45549         var ans = this.editorcore.getAllAncestors();
45550         if (this.formatCombo) {
45551             
45552             
45553             var store = this.formatCombo.store;
45554             this.formatCombo.setValue("");
45555             for (var i =0; i < ans.length;i++) {
45556                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45557                     // select it..
45558                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45559                     break;
45560                 }
45561             }
45562         }
45563         
45564         
45565         
45566         // hides menus... - so this cant be on a menu...
45567         Roo.menu.MenuMgr.hideAll();
45568
45569         //this.editorsyncValue();
45570     },
45571    
45572     
45573     createFontOptions : function(){
45574         var buf = [], fs = this.fontFamilies, ff, lc;
45575         
45576         
45577         
45578         for(var i = 0, len = fs.length; i< len; i++){
45579             ff = fs[i];
45580             lc = ff.toLowerCase();
45581             buf.push(
45582                 '<option value="',lc,'" style="font-family:',ff,';"',
45583                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45584                     ff,
45585                 '</option>'
45586             );
45587         }
45588         return buf.join('');
45589     },
45590     
45591     toggleSourceEdit : function(sourceEditMode){
45592         
45593         Roo.log("toolbar toogle");
45594         if(sourceEditMode === undefined){
45595             sourceEditMode = !this.sourceEditMode;
45596         }
45597         this.sourceEditMode = sourceEditMode === true;
45598         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45599         // just toggle the button?
45600         if(btn.pressed !== this.sourceEditMode){
45601             btn.toggle(this.sourceEditMode);
45602             return;
45603         }
45604         
45605         if(sourceEditMode){
45606             Roo.log("disabling buttons");
45607             this.tb.items.each(function(item){
45608                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45609                     item.disable();
45610                 }
45611             });
45612           
45613         }else{
45614             Roo.log("enabling buttons");
45615             if(this.editorcore.initialized){
45616                 this.tb.items.each(function(item){
45617                     item.enable();
45618                 });
45619             }
45620             
45621         }
45622         Roo.log("calling toggole on editor");
45623         // tell the editor that it's been pressed..
45624         this.editor.toggleSourceEdit(sourceEditMode);
45625        
45626     },
45627      /**
45628      * Object collection of toolbar tooltips for the buttons in the editor. The key
45629      * is the command id associated with that button and the value is a valid QuickTips object.
45630      * For example:
45631 <pre><code>
45632 {
45633     bold : {
45634         title: 'Bold (Ctrl+B)',
45635         text: 'Make the selected text bold.',
45636         cls: 'x-html-editor-tip'
45637     },
45638     italic : {
45639         title: 'Italic (Ctrl+I)',
45640         text: 'Make the selected text italic.',
45641         cls: 'x-html-editor-tip'
45642     },
45643     ...
45644 </code></pre>
45645     * @type Object
45646      */
45647     buttonTips : {
45648         bold : {
45649             title: 'Bold (Ctrl+B)',
45650             text: 'Make the selected text bold.',
45651             cls: 'x-html-editor-tip'
45652         },
45653         italic : {
45654             title: 'Italic (Ctrl+I)',
45655             text: 'Make the selected text italic.',
45656             cls: 'x-html-editor-tip'
45657         },
45658         underline : {
45659             title: 'Underline (Ctrl+U)',
45660             text: 'Underline the selected text.',
45661             cls: 'x-html-editor-tip'
45662         },
45663         strikethrough : {
45664             title: 'Strikethrough',
45665             text: 'Strikethrough the selected text.',
45666             cls: 'x-html-editor-tip'
45667         },
45668         increasefontsize : {
45669             title: 'Grow Text',
45670             text: 'Increase the font size.',
45671             cls: 'x-html-editor-tip'
45672         },
45673         decreasefontsize : {
45674             title: 'Shrink Text',
45675             text: 'Decrease the font size.',
45676             cls: 'x-html-editor-tip'
45677         },
45678         backcolor : {
45679             title: 'Text Highlight Color',
45680             text: 'Change the background color of the selected text.',
45681             cls: 'x-html-editor-tip'
45682         },
45683         forecolor : {
45684             title: 'Font Color',
45685             text: 'Change the color of the selected text.',
45686             cls: 'x-html-editor-tip'
45687         },
45688         justifyleft : {
45689             title: 'Align Text Left',
45690             text: 'Align text to the left.',
45691             cls: 'x-html-editor-tip'
45692         },
45693         justifycenter : {
45694             title: 'Center Text',
45695             text: 'Center text in the editor.',
45696             cls: 'x-html-editor-tip'
45697         },
45698         justifyright : {
45699             title: 'Align Text Right',
45700             text: 'Align text to the right.',
45701             cls: 'x-html-editor-tip'
45702         },
45703         insertunorderedlist : {
45704             title: 'Bullet List',
45705             text: 'Start a bulleted list.',
45706             cls: 'x-html-editor-tip'
45707         },
45708         insertorderedlist : {
45709             title: 'Numbered List',
45710             text: 'Start a numbered list.',
45711             cls: 'x-html-editor-tip'
45712         },
45713         createlink : {
45714             title: 'Hyperlink',
45715             text: 'Make the selected text a hyperlink.',
45716             cls: 'x-html-editor-tip'
45717         },
45718         sourceedit : {
45719             title: 'Source Edit',
45720             text: 'Switch to source editing mode.',
45721             cls: 'x-html-editor-tip'
45722         }
45723     },
45724     // private
45725     onDestroy : function(){
45726         if(this.rendered){
45727             
45728             this.tb.items.each(function(item){
45729                 if(item.menu){
45730                     item.menu.removeAll();
45731                     if(item.menu.el){
45732                         item.menu.el.destroy();
45733                     }
45734                 }
45735                 item.destroy();
45736             });
45737              
45738         }
45739     },
45740     onFirstFocus: function() {
45741         this.tb.items.each(function(item){
45742            item.enable();
45743         });
45744     }
45745 });
45746
45747
45748
45749
45750 // <script type="text/javascript">
45751 /*
45752  * Based on
45753  * Ext JS Library 1.1.1
45754  * Copyright(c) 2006-2007, Ext JS, LLC.
45755  *  
45756  
45757  */
45758
45759  
45760 /**
45761  * @class Roo.form.HtmlEditor.ToolbarContext
45762  * Context Toolbar
45763  * 
45764  * Usage:
45765  *
45766  new Roo.form.HtmlEditor({
45767     ....
45768     toolbars : [
45769         { xtype: 'ToolbarStandard', styles : {} }
45770         { xtype: 'ToolbarContext', disable : {} }
45771     ]
45772 })
45773
45774      
45775  * 
45776  * @config : {Object} disable List of elements to disable.. (not done yet.)
45777  * @config : {Object} styles  Map of styles available.
45778  * 
45779  */
45780
45781 Roo.form.HtmlEditor.ToolbarContext = function(config)
45782 {
45783     
45784     Roo.apply(this, config);
45785     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45786     // dont call parent... till later.
45787     this.styles = this.styles || {};
45788 }
45789
45790  
45791
45792 Roo.form.HtmlEditor.ToolbarContext.types = {
45793     'IMG' : {
45794         width : {
45795             title: "Width",
45796             width: 40
45797         },
45798         height:  {
45799             title: "Height",
45800             width: 40
45801         },
45802         align: {
45803             title: "Align",
45804             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45805             width : 80
45806             
45807         },
45808         border: {
45809             title: "Border",
45810             width: 40
45811         },
45812         alt: {
45813             title: "Alt",
45814             width: 120
45815         },
45816         src : {
45817             title: "Src",
45818             width: 220
45819         }
45820         
45821     },
45822     'A' : {
45823         name : {
45824             title: "Name",
45825             width: 50
45826         },
45827         target:  {
45828             title: "Target",
45829             width: 120
45830         },
45831         href:  {
45832             title: "Href",
45833             width: 220
45834         } // border?
45835         
45836     },
45837     'TABLE' : {
45838         rows : {
45839             title: "Rows",
45840             width: 20
45841         },
45842         cols : {
45843             title: "Cols",
45844             width: 20
45845         },
45846         width : {
45847             title: "Width",
45848             width: 40
45849         },
45850         height : {
45851             title: "Height",
45852             width: 40
45853         },
45854         border : {
45855             title: "Border",
45856             width: 20
45857         }
45858     },
45859     'TD' : {
45860         width : {
45861             title: "Width",
45862             width: 40
45863         },
45864         height : {
45865             title: "Height",
45866             width: 40
45867         },   
45868         align: {
45869             title: "Align",
45870             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45871             width: 80
45872         },
45873         valign: {
45874             title: "Valign",
45875             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45876             width: 80
45877         },
45878         colspan: {
45879             title: "Colspan",
45880             width: 20
45881             
45882         },
45883          'font-family'  : {
45884             title : "Font",
45885             style : 'fontFamily',
45886             displayField: 'display',
45887             optname : 'font-family',
45888             width: 140
45889         }
45890     },
45891     'INPUT' : {
45892         name : {
45893             title: "name",
45894             width: 120
45895         },
45896         value : {
45897             title: "Value",
45898             width: 120
45899         },
45900         width : {
45901             title: "Width",
45902             width: 40
45903         }
45904     },
45905     'LABEL' : {
45906         'for' : {
45907             title: "For",
45908             width: 120
45909         }
45910     },
45911     'TEXTAREA' : {
45912           name : {
45913             title: "name",
45914             width: 120
45915         },
45916         rows : {
45917             title: "Rows",
45918             width: 20
45919         },
45920         cols : {
45921             title: "Cols",
45922             width: 20
45923         }
45924     },
45925     'SELECT' : {
45926         name : {
45927             title: "name",
45928             width: 120
45929         },
45930         selectoptions : {
45931             title: "Options",
45932             width: 200
45933         }
45934     },
45935     
45936     // should we really allow this??
45937     // should this just be 
45938     'BODY' : {
45939         title : {
45940             title: "Title",
45941             width: 200,
45942             disabled : true
45943         }
45944     },
45945     'SPAN' : {
45946         'font-family'  : {
45947             title : "Font",
45948             style : 'fontFamily',
45949             displayField: 'display',
45950             optname : 'font-family',
45951             width: 140
45952         }
45953     },
45954     'DIV' : {
45955         'font-family'  : {
45956             title : "Font",
45957             style : 'fontFamily',
45958             displayField: 'display',
45959             optname : 'font-family',
45960             width: 140
45961         }
45962     },
45963      'P' : {
45964         'font-family'  : {
45965             title : "Font",
45966             style : 'fontFamily',
45967             displayField: 'display',
45968             optname : 'font-family',
45969             width: 140
45970         }
45971     },
45972     
45973     '*' : {
45974         // empty..
45975     }
45976
45977 };
45978
45979 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45980 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45981
45982 Roo.form.HtmlEditor.ToolbarContext.options = {
45983         'font-family'  : [ 
45984                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45985                 [ 'Courier New', 'Courier New'],
45986                 [ 'Tahoma', 'Tahoma'],
45987                 [ 'Times New Roman,serif', 'Times'],
45988                 [ 'Verdana','Verdana' ]
45989         ]
45990 };
45991
45992 // fixme - these need to be configurable..
45993  
45994
45995 //Roo.form.HtmlEditor.ToolbarContext.types
45996
45997
45998 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45999     
46000     tb: false,
46001     
46002     rendered: false,
46003     
46004     editor : false,
46005     editorcore : false,
46006     /**
46007      * @cfg {Object} disable  List of toolbar elements to disable
46008          
46009      */
46010     disable : false,
46011     /**
46012      * @cfg {Object} styles List of styles 
46013      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46014      *
46015      * These must be defined in the page, so they get rendered correctly..
46016      * .headline { }
46017      * TD.underline { }
46018      * 
46019      */
46020     styles : false,
46021     
46022     options: false,
46023     
46024     toolbars : false,
46025     
46026     init : function(editor)
46027     {
46028         this.editor = editor;
46029         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46030         var editorcore = this.editorcore;
46031         
46032         var fid = editorcore.frameId;
46033         var etb = this;
46034         function btn(id, toggle, handler){
46035             var xid = fid + '-'+ id ;
46036             return {
46037                 id : xid,
46038                 cmd : id,
46039                 cls : 'x-btn-icon x-edit-'+id,
46040                 enableToggle:toggle !== false,
46041                 scope: editorcore, // was editor...
46042                 handler:handler||editorcore.relayBtnCmd,
46043                 clickEvent:'mousedown',
46044                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46045                 tabIndex:-1
46046             };
46047         }
46048         // create a new element.
46049         var wdiv = editor.wrap.createChild({
46050                 tag: 'div'
46051             }, editor.wrap.dom.firstChild.nextSibling, true);
46052         
46053         // can we do this more than once??
46054         
46055          // stop form submits
46056       
46057  
46058         // disable everything...
46059         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46060         this.toolbars = {};
46061            
46062         for (var i in  ty) {
46063           
46064             this.toolbars[i] = this.buildToolbar(ty[i],i);
46065         }
46066         this.tb = this.toolbars.BODY;
46067         this.tb.el.show();
46068         this.buildFooter();
46069         this.footer.show();
46070         editor.on('hide', function( ) { this.footer.hide() }, this);
46071         editor.on('show', function( ) { this.footer.show() }, this);
46072         
46073          
46074         this.rendered = true;
46075         
46076         // the all the btns;
46077         editor.on('editorevent', this.updateToolbar, this);
46078         // other toolbars need to implement this..
46079         //editor.on('editmodechange', this.updateToolbar, this);
46080     },
46081     
46082     
46083     
46084     /**
46085      * Protected method that will not generally be called directly. It triggers
46086      * a toolbar update by reading the markup state of the current selection in the editor.
46087      *
46088      * Note you can force an update by calling on('editorevent', scope, false)
46089      */
46090     updateToolbar: function(editor,ev,sel){
46091
46092         //Roo.log(ev);
46093         // capture mouse up - this is handy for selecting images..
46094         // perhaps should go somewhere else...
46095         if(!this.editorcore.activated){
46096              this.editor.onFirstFocus();
46097             return;
46098         }
46099         
46100         
46101         
46102         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46103         // selectNode - might want to handle IE?
46104         if (ev &&
46105             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46106             ev.target && ev.target.tagName == 'IMG') {
46107             // they have click on an image...
46108             // let's see if we can change the selection...
46109             sel = ev.target;
46110          
46111               var nodeRange = sel.ownerDocument.createRange();
46112             try {
46113                 nodeRange.selectNode(sel);
46114             } catch (e) {
46115                 nodeRange.selectNodeContents(sel);
46116             }
46117             //nodeRange.collapse(true);
46118             var s = this.editorcore.win.getSelection();
46119             s.removeAllRanges();
46120             s.addRange(nodeRange);
46121         }  
46122         
46123       
46124         var updateFooter = sel ? false : true;
46125         
46126         
46127         var ans = this.editorcore.getAllAncestors();
46128         
46129         // pick
46130         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46131         
46132         if (!sel) { 
46133             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46134             sel = sel ? sel : this.editorcore.doc.body;
46135             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46136             
46137         }
46138         // pick a menu that exists..
46139         var tn = sel.tagName.toUpperCase();
46140         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46141         
46142         tn = sel.tagName.toUpperCase();
46143         
46144         var lastSel = this.tb.selectedNode;
46145         
46146         this.tb.selectedNode = sel;
46147         
46148         // if current menu does not match..
46149         
46150         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46151                 
46152             this.tb.el.hide();
46153             ///console.log("show: " + tn);
46154             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46155             this.tb.el.show();
46156             // update name
46157             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46158             
46159             
46160             // update attributes
46161             if (this.tb.fields) {
46162                 this.tb.fields.each(function(e) {
46163                     if (e.stylename) {
46164                         e.setValue(sel.style[e.stylename]);
46165                         return;
46166                     } 
46167                    e.setValue(sel.getAttribute(e.attrname));
46168                 });
46169             }
46170             
46171             var hasStyles = false;
46172             for(var i in this.styles) {
46173                 hasStyles = true;
46174                 break;
46175             }
46176             
46177             // update styles
46178             if (hasStyles) { 
46179                 var st = this.tb.fields.item(0);
46180                 
46181                 st.store.removeAll();
46182                
46183                 
46184                 var cn = sel.className.split(/\s+/);
46185                 
46186                 var avs = [];
46187                 if (this.styles['*']) {
46188                     
46189                     Roo.each(this.styles['*'], function(v) {
46190                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46191                     });
46192                 }
46193                 if (this.styles[tn]) { 
46194                     Roo.each(this.styles[tn], function(v) {
46195                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46196                     });
46197                 }
46198                 
46199                 st.store.loadData(avs);
46200                 st.collapse();
46201                 st.setValue(cn);
46202             }
46203             // flag our selected Node.
46204             this.tb.selectedNode = sel;
46205            
46206            
46207             Roo.menu.MenuMgr.hideAll();
46208
46209         }
46210         
46211         if (!updateFooter) {
46212             //this.footDisp.dom.innerHTML = ''; 
46213             return;
46214         }
46215         // update the footer
46216         //
46217         var html = '';
46218         
46219         this.footerEls = ans.reverse();
46220         Roo.each(this.footerEls, function(a,i) {
46221             if (!a) { return; }
46222             html += html.length ? ' &gt; '  :  '';
46223             
46224             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46225             
46226         });
46227        
46228         // 
46229         var sz = this.footDisp.up('td').getSize();
46230         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46231         this.footDisp.dom.style.marginLeft = '5px';
46232         
46233         this.footDisp.dom.style.overflow = 'hidden';
46234         
46235         this.footDisp.dom.innerHTML = html;
46236             
46237         //this.editorsyncValue();
46238     },
46239      
46240     
46241    
46242        
46243     // private
46244     onDestroy : function(){
46245         if(this.rendered){
46246             
46247             this.tb.items.each(function(item){
46248                 if(item.menu){
46249                     item.menu.removeAll();
46250                     if(item.menu.el){
46251                         item.menu.el.destroy();
46252                     }
46253                 }
46254                 item.destroy();
46255             });
46256              
46257         }
46258     },
46259     onFirstFocus: function() {
46260         // need to do this for all the toolbars..
46261         this.tb.items.each(function(item){
46262            item.enable();
46263         });
46264     },
46265     buildToolbar: function(tlist, nm)
46266     {
46267         var editor = this.editor;
46268         var editorcore = this.editorcore;
46269          // create a new element.
46270         var wdiv = editor.wrap.createChild({
46271                 tag: 'div'
46272             }, editor.wrap.dom.firstChild.nextSibling, true);
46273         
46274        
46275         var tb = new Roo.Toolbar(wdiv);
46276         // add the name..
46277         
46278         tb.add(nm+ ":&nbsp;");
46279         
46280         var styles = [];
46281         for(var i in this.styles) {
46282             styles.push(i);
46283         }
46284         
46285         // styles...
46286         if (styles && styles.length) {
46287             
46288             // this needs a multi-select checkbox...
46289             tb.addField( new Roo.form.ComboBox({
46290                 store: new Roo.data.SimpleStore({
46291                     id : 'val',
46292                     fields: ['val', 'selected'],
46293                     data : [] 
46294                 }),
46295                 name : '-roo-edit-className',
46296                 attrname : 'className',
46297                 displayField: 'val',
46298                 typeAhead: false,
46299                 mode: 'local',
46300                 editable : false,
46301                 triggerAction: 'all',
46302                 emptyText:'Select Style',
46303                 selectOnFocus:true,
46304                 width: 130,
46305                 listeners : {
46306                     'select': function(c, r, i) {
46307                         // initial support only for on class per el..
46308                         tb.selectedNode.className =  r ? r.get('val') : '';
46309                         editorcore.syncValue();
46310                     }
46311                 }
46312     
46313             }));
46314         }
46315         
46316         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46317         var tbops = tbc.options;
46318         
46319         for (var i in tlist) {
46320             
46321             var item = tlist[i];
46322             tb.add(item.title + ":&nbsp;");
46323             
46324             
46325             //optname == used so you can configure the options available..
46326             var opts = item.opts ? item.opts : false;
46327             if (item.optname) {
46328                 opts = tbops[item.optname];
46329            
46330             }
46331             
46332             if (opts) {
46333                 // opts == pulldown..
46334                 tb.addField( new Roo.form.ComboBox({
46335                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46336                         id : 'val',
46337                         fields: ['val', 'display'],
46338                         data : opts  
46339                     }),
46340                     name : '-roo-edit-' + i,
46341                     attrname : i,
46342                     stylename : item.style ? item.style : false,
46343                     displayField: item.displayField ? item.displayField : 'val',
46344                     valueField :  'val',
46345                     typeAhead: false,
46346                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46347                     editable : false,
46348                     triggerAction: 'all',
46349                     emptyText:'Select',
46350                     selectOnFocus:true,
46351                     width: item.width ? item.width  : 130,
46352                     listeners : {
46353                         'select': function(c, r, i) {
46354                             if (c.stylename) {
46355                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46356                                 return;
46357                             }
46358                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46359                         }
46360                     }
46361
46362                 }));
46363                 continue;
46364                     
46365                  
46366                 
46367                 tb.addField( new Roo.form.TextField({
46368                     name: i,
46369                     width: 100,
46370                     //allowBlank:false,
46371                     value: ''
46372                 }));
46373                 continue;
46374             }
46375             tb.addField( new Roo.form.TextField({
46376                 name: '-roo-edit-' + i,
46377                 attrname : i,
46378                 
46379                 width: item.width,
46380                 //allowBlank:true,
46381                 value: '',
46382                 listeners: {
46383                     'change' : function(f, nv, ov) {
46384                         tb.selectedNode.setAttribute(f.attrname, nv);
46385                         editorcore.syncValue();
46386                     }
46387                 }
46388             }));
46389              
46390         }
46391         
46392         var _this = this;
46393         
46394         if(nm == 'BODY'){
46395             tb.addSeparator();
46396         
46397             tb.addButton( {
46398                 text: 'Stylesheets',
46399
46400                 listeners : {
46401                     click : function ()
46402                     {
46403                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46404                     }
46405                 }
46406             });
46407         }
46408         
46409         tb.addFill();
46410         tb.addButton( {
46411             text: 'Remove Tag',
46412     
46413             listeners : {
46414                 click : function ()
46415                 {
46416                     // remove
46417                     // undo does not work.
46418                      
46419                     var sn = tb.selectedNode;
46420                     
46421                     var pn = sn.parentNode;
46422                     
46423                     var stn =  sn.childNodes[0];
46424                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46425                     while (sn.childNodes.length) {
46426                         var node = sn.childNodes[0];
46427                         sn.removeChild(node);
46428                         //Roo.log(node);
46429                         pn.insertBefore(node, sn);
46430                         
46431                     }
46432                     pn.removeChild(sn);
46433                     var range = editorcore.createRange();
46434         
46435                     range.setStart(stn,0);
46436                     range.setEnd(en,0); //????
46437                     //range.selectNode(sel);
46438                     
46439                     
46440                     var selection = editorcore.getSelection();
46441                     selection.removeAllRanges();
46442                     selection.addRange(range);
46443                     
46444                     
46445                     
46446                     //_this.updateToolbar(null, null, pn);
46447                     _this.updateToolbar(null, null, null);
46448                     _this.footDisp.dom.innerHTML = ''; 
46449                 }
46450             }
46451             
46452                     
46453                 
46454             
46455         });
46456         
46457         
46458         tb.el.on('click', function(e){
46459             e.preventDefault(); // what does this do?
46460         });
46461         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46462         tb.el.hide();
46463         tb.name = nm;
46464         // dont need to disable them... as they will get hidden
46465         return tb;
46466          
46467         
46468     },
46469     buildFooter : function()
46470     {
46471         
46472         var fel = this.editor.wrap.createChild();
46473         this.footer = new Roo.Toolbar(fel);
46474         // toolbar has scrolly on left / right?
46475         var footDisp= new Roo.Toolbar.Fill();
46476         var _t = this;
46477         this.footer.add(
46478             {
46479                 text : '&lt;',
46480                 xtype: 'Button',
46481                 handler : function() {
46482                     _t.footDisp.scrollTo('left',0,true)
46483                 }
46484             }
46485         );
46486         this.footer.add( footDisp );
46487         this.footer.add( 
46488             {
46489                 text : '&gt;',
46490                 xtype: 'Button',
46491                 handler : function() {
46492                     // no animation..
46493                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46494                 }
46495             }
46496         );
46497         var fel = Roo.get(footDisp.el);
46498         fel.addClass('x-editor-context');
46499         this.footDispWrap = fel; 
46500         this.footDispWrap.overflow  = 'hidden';
46501         
46502         this.footDisp = fel.createChild();
46503         this.footDispWrap.on('click', this.onContextClick, this)
46504         
46505         
46506     },
46507     onContextClick : function (ev,dom)
46508     {
46509         ev.preventDefault();
46510         var  cn = dom.className;
46511         //Roo.log(cn);
46512         if (!cn.match(/x-ed-loc-/)) {
46513             return;
46514         }
46515         var n = cn.split('-').pop();
46516         var ans = this.footerEls;
46517         var sel = ans[n];
46518         
46519          // pick
46520         var range = this.editorcore.createRange();
46521         
46522         range.selectNodeContents(sel);
46523         //range.selectNode(sel);
46524         
46525         
46526         var selection = this.editorcore.getSelection();
46527         selection.removeAllRanges();
46528         selection.addRange(range);
46529         
46530         
46531         
46532         this.updateToolbar(null, null, sel);
46533         
46534         
46535     }
46536     
46537     
46538     
46539     
46540     
46541 });
46542
46543
46544
46545
46546
46547 /*
46548  * Based on:
46549  * Ext JS Library 1.1.1
46550  * Copyright(c) 2006-2007, Ext JS, LLC.
46551  *
46552  * Originally Released Under LGPL - original licence link has changed is not relivant.
46553  *
46554  * Fork - LGPL
46555  * <script type="text/javascript">
46556  */
46557  
46558 /**
46559  * @class Roo.form.BasicForm
46560  * @extends Roo.util.Observable
46561  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46562  * @constructor
46563  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46564  * @param {Object} config Configuration options
46565  */
46566 Roo.form.BasicForm = function(el, config){
46567     this.allItems = [];
46568     this.childForms = [];
46569     Roo.apply(this, config);
46570     /*
46571      * The Roo.form.Field items in this form.
46572      * @type MixedCollection
46573      */
46574      
46575      
46576     this.items = new Roo.util.MixedCollection(false, function(o){
46577         return o.id || (o.id = Roo.id());
46578     });
46579     this.addEvents({
46580         /**
46581          * @event beforeaction
46582          * Fires before any action is performed. Return false to cancel the action.
46583          * @param {Form} this
46584          * @param {Action} action The action to be performed
46585          */
46586         beforeaction: true,
46587         /**
46588          * @event actionfailed
46589          * Fires when an action fails.
46590          * @param {Form} this
46591          * @param {Action} action The action that failed
46592          */
46593         actionfailed : true,
46594         /**
46595          * @event actioncomplete
46596          * Fires when an action is completed.
46597          * @param {Form} this
46598          * @param {Action} action The action that completed
46599          */
46600         actioncomplete : true
46601     });
46602     if(el){
46603         this.initEl(el);
46604     }
46605     Roo.form.BasicForm.superclass.constructor.call(this);
46606 };
46607
46608 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46609     /**
46610      * @cfg {String} method
46611      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46612      */
46613     /**
46614      * @cfg {DataReader} reader
46615      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46616      * This is optional as there is built-in support for processing JSON.
46617      */
46618     /**
46619      * @cfg {DataReader} errorReader
46620      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46621      * This is completely optional as there is built-in support for processing JSON.
46622      */
46623     /**
46624      * @cfg {String} url
46625      * The URL to use for form actions if one isn't supplied in the action options.
46626      */
46627     /**
46628      * @cfg {Boolean} fileUpload
46629      * Set to true if this form is a file upload.
46630      */
46631      
46632     /**
46633      * @cfg {Object} baseParams
46634      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46635      */
46636      /**
46637      
46638     /**
46639      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46640      */
46641     timeout: 30,
46642
46643     // private
46644     activeAction : null,
46645
46646     /**
46647      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46648      * or setValues() data instead of when the form was first created.
46649      */
46650     trackResetOnLoad : false,
46651     
46652     
46653     /**
46654      * childForms - used for multi-tab forms
46655      * @type {Array}
46656      */
46657     childForms : false,
46658     
46659     /**
46660      * allItems - full list of fields.
46661      * @type {Array}
46662      */
46663     allItems : false,
46664     
46665     /**
46666      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46667      * element by passing it or its id or mask the form itself by passing in true.
46668      * @type Mixed
46669      */
46670     waitMsgTarget : false,
46671
46672     // private
46673     initEl : function(el){
46674         this.el = Roo.get(el);
46675         this.id = this.el.id || Roo.id();
46676         this.el.on('submit', this.onSubmit, this);
46677         this.el.addClass('x-form');
46678     },
46679
46680     // private
46681     onSubmit : function(e){
46682         e.stopEvent();
46683     },
46684
46685     /**
46686      * Returns true if client-side validation on the form is successful.
46687      * @return Boolean
46688      */
46689     isValid : function(){
46690         var valid = true;
46691         this.items.each(function(f){
46692            if(!f.validate()){
46693                valid = false;
46694            }
46695         });
46696         return valid;
46697     },
46698
46699     /**
46700      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46701      * @return Boolean
46702      */
46703     isDirty : function(){
46704         var dirty = false;
46705         this.items.each(function(f){
46706            if(f.isDirty()){
46707                dirty = true;
46708                return false;
46709            }
46710         });
46711         return dirty;
46712     },
46713     
46714     /**
46715      * Returns true if any fields in this form have changed since their original load. (New version)
46716      * @return Boolean
46717      */
46718     
46719     hasChanged : function()
46720     {
46721         var dirty = false;
46722         this.items.each(function(f){
46723            if(f.hasChanged()){
46724                dirty = true;
46725                return false;
46726            }
46727         });
46728         return dirty;
46729         
46730     },
46731     /**
46732      * Resets all hasChanged to 'false' -
46733      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46734      * So hasChanged storage is only to be used for this purpose
46735      * @return Boolean
46736      */
46737     resetHasChanged : function()
46738     {
46739         this.items.each(function(f){
46740            f.resetHasChanged();
46741         });
46742         
46743     },
46744     
46745     
46746     /**
46747      * Performs a predefined action (submit or load) or custom actions you define on this form.
46748      * @param {String} actionName The name of the action type
46749      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46750      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46751      * accept other config options):
46752      * <pre>
46753 Property          Type             Description
46754 ----------------  ---------------  ----------------------------------------------------------------------------------
46755 url               String           The url for the action (defaults to the form's url)
46756 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46757 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46758 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46759                                    validate the form on the client (defaults to false)
46760      * </pre>
46761      * @return {BasicForm} this
46762      */
46763     doAction : function(action, options){
46764         if(typeof action == 'string'){
46765             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46766         }
46767         if(this.fireEvent('beforeaction', this, action) !== false){
46768             this.beforeAction(action);
46769             action.run.defer(100, action);
46770         }
46771         return this;
46772     },
46773
46774     /**
46775      * Shortcut to do a submit action.
46776      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46777      * @return {BasicForm} this
46778      */
46779     submit : function(options){
46780         this.doAction('submit', options);
46781         return this;
46782     },
46783
46784     /**
46785      * Shortcut to do a load action.
46786      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46787      * @return {BasicForm} this
46788      */
46789     load : function(options){
46790         this.doAction('load', options);
46791         return this;
46792     },
46793
46794     /**
46795      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46796      * @param {Record} record The record to edit
46797      * @return {BasicForm} this
46798      */
46799     updateRecord : function(record){
46800         record.beginEdit();
46801         var fs = record.fields;
46802         fs.each(function(f){
46803             var field = this.findField(f.name);
46804             if(field){
46805                 record.set(f.name, field.getValue());
46806             }
46807         }, this);
46808         record.endEdit();
46809         return this;
46810     },
46811
46812     /**
46813      * Loads an Roo.data.Record into this form.
46814      * @param {Record} record The record to load
46815      * @return {BasicForm} this
46816      */
46817     loadRecord : function(record){
46818         this.setValues(record.data);
46819         return this;
46820     },
46821
46822     // private
46823     beforeAction : function(action){
46824         var o = action.options;
46825         
46826        
46827         if(this.waitMsgTarget === true){
46828             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46829         }else if(this.waitMsgTarget){
46830             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46831             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46832         }else {
46833             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46834         }
46835          
46836     },
46837
46838     // private
46839     afterAction : function(action, success){
46840         this.activeAction = null;
46841         var o = action.options;
46842         
46843         if(this.waitMsgTarget === true){
46844             this.el.unmask();
46845         }else if(this.waitMsgTarget){
46846             this.waitMsgTarget.unmask();
46847         }else{
46848             Roo.MessageBox.updateProgress(1);
46849             Roo.MessageBox.hide();
46850         }
46851          
46852         if(success){
46853             if(o.reset){
46854                 this.reset();
46855             }
46856             Roo.callback(o.success, o.scope, [this, action]);
46857             this.fireEvent('actioncomplete', this, action);
46858             
46859         }else{
46860             
46861             // failure condition..
46862             // we have a scenario where updates need confirming.
46863             // eg. if a locking scenario exists..
46864             // we look for { errors : { needs_confirm : true }} in the response.
46865             if (
46866                 (typeof(action.result) != 'undefined')  &&
46867                 (typeof(action.result.errors) != 'undefined')  &&
46868                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46869            ){
46870                 var _t = this;
46871                 Roo.MessageBox.confirm(
46872                     "Change requires confirmation",
46873                     action.result.errorMsg,
46874                     function(r) {
46875                         if (r != 'yes') {
46876                             return;
46877                         }
46878                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46879                     }
46880                     
46881                 );
46882                 
46883                 
46884                 
46885                 return;
46886             }
46887             
46888             Roo.callback(o.failure, o.scope, [this, action]);
46889             // show an error message if no failed handler is set..
46890             if (!this.hasListener('actionfailed')) {
46891                 Roo.MessageBox.alert("Error",
46892                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46893                         action.result.errorMsg :
46894                         "Saving Failed, please check your entries or try again"
46895                 );
46896             }
46897             
46898             this.fireEvent('actionfailed', this, action);
46899         }
46900         
46901     },
46902
46903     /**
46904      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46905      * @param {String} id The value to search for
46906      * @return Field
46907      */
46908     findField : function(id){
46909         var field = this.items.get(id);
46910         if(!field){
46911             this.items.each(function(f){
46912                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46913                     field = f;
46914                     return false;
46915                 }
46916             });
46917         }
46918         return field || null;
46919     },
46920
46921     /**
46922      * Add a secondary form to this one, 
46923      * Used to provide tabbed forms. One form is primary, with hidden values 
46924      * which mirror the elements from the other forms.
46925      * 
46926      * @param {Roo.form.Form} form to add.
46927      * 
46928      */
46929     addForm : function(form)
46930     {
46931        
46932         if (this.childForms.indexOf(form) > -1) {
46933             // already added..
46934             return;
46935         }
46936         this.childForms.push(form);
46937         var n = '';
46938         Roo.each(form.allItems, function (fe) {
46939             
46940             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46941             if (this.findField(n)) { // already added..
46942                 return;
46943             }
46944             var add = new Roo.form.Hidden({
46945                 name : n
46946             });
46947             add.render(this.el);
46948             
46949             this.add( add );
46950         }, this);
46951         
46952     },
46953     /**
46954      * Mark fields in this form invalid in bulk.
46955      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46956      * @return {BasicForm} this
46957      */
46958     markInvalid : function(errors){
46959         if(errors instanceof Array){
46960             for(var i = 0, len = errors.length; i < len; i++){
46961                 var fieldError = errors[i];
46962                 var f = this.findField(fieldError.id);
46963                 if(f){
46964                     f.markInvalid(fieldError.msg);
46965                 }
46966             }
46967         }else{
46968             var field, id;
46969             for(id in errors){
46970                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46971                     field.markInvalid(errors[id]);
46972                 }
46973             }
46974         }
46975         Roo.each(this.childForms || [], function (f) {
46976             f.markInvalid(errors);
46977         });
46978         
46979         return this;
46980     },
46981
46982     /**
46983      * Set values for fields in this form in bulk.
46984      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46985      * @return {BasicForm} this
46986      */
46987     setValues : function(values){
46988         if(values instanceof Array){ // array of objects
46989             for(var i = 0, len = values.length; i < len; i++){
46990                 var v = values[i];
46991                 var f = this.findField(v.id);
46992                 if(f){
46993                     f.setValue(v.value);
46994                     if(this.trackResetOnLoad){
46995                         f.originalValue = f.getValue();
46996                     }
46997                 }
46998             }
46999         }else{ // object hash
47000             var field, id;
47001             for(id in values){
47002                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47003                     
47004                     if (field.setFromData && 
47005                         field.valueField && 
47006                         field.displayField &&
47007                         // combos' with local stores can 
47008                         // be queried via setValue()
47009                         // to set their value..
47010                         (field.store && !field.store.isLocal)
47011                         ) {
47012                         // it's a combo
47013                         var sd = { };
47014                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47015                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47016                         field.setFromData(sd);
47017                         
47018                     } else {
47019                         field.setValue(values[id]);
47020                     }
47021                     
47022                     
47023                     if(this.trackResetOnLoad){
47024                         field.originalValue = field.getValue();
47025                     }
47026                 }
47027             }
47028         }
47029         this.resetHasChanged();
47030         
47031         
47032         Roo.each(this.childForms || [], function (f) {
47033             f.setValues(values);
47034             f.resetHasChanged();
47035         });
47036                 
47037         return this;
47038     },
47039
47040     /**
47041      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47042      * they are returned as an array.
47043      * @param {Boolean} asString
47044      * @return {Object}
47045      */
47046     getValues : function(asString){
47047         if (this.childForms) {
47048             // copy values from the child forms
47049             Roo.each(this.childForms, function (f) {
47050                 this.setValues(f.getValues());
47051             }, this);
47052         }
47053         
47054         
47055         
47056         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47057         if(asString === true){
47058             return fs;
47059         }
47060         return Roo.urlDecode(fs);
47061     },
47062     
47063     /**
47064      * Returns the fields in this form as an object with key/value pairs. 
47065      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47066      * @return {Object}
47067      */
47068     getFieldValues : function(with_hidden)
47069     {
47070         if (this.childForms) {
47071             // copy values from the child forms
47072             // should this call getFieldValues - probably not as we do not currently copy
47073             // hidden fields when we generate..
47074             Roo.each(this.childForms, function (f) {
47075                 this.setValues(f.getValues());
47076             }, this);
47077         }
47078         
47079         var ret = {};
47080         this.items.each(function(f){
47081             if (!f.getName()) {
47082                 return;
47083             }
47084             var v = f.getValue();
47085             if (f.inputType =='radio') {
47086                 if (typeof(ret[f.getName()]) == 'undefined') {
47087                     ret[f.getName()] = ''; // empty..
47088                 }
47089                 
47090                 if (!f.el.dom.checked) {
47091                     return;
47092                     
47093                 }
47094                 v = f.el.dom.value;
47095                 
47096             }
47097             
47098             // not sure if this supported any more..
47099             if ((typeof(v) == 'object') && f.getRawValue) {
47100                 v = f.getRawValue() ; // dates..
47101             }
47102             // combo boxes where name != hiddenName...
47103             if (f.name != f.getName()) {
47104                 ret[f.name] = f.getRawValue();
47105             }
47106             ret[f.getName()] = v;
47107         });
47108         
47109         return ret;
47110     },
47111
47112     /**
47113      * Clears all invalid messages in this form.
47114      * @return {BasicForm} this
47115      */
47116     clearInvalid : function(){
47117         this.items.each(function(f){
47118            f.clearInvalid();
47119         });
47120         
47121         Roo.each(this.childForms || [], function (f) {
47122             f.clearInvalid();
47123         });
47124         
47125         
47126         return this;
47127     },
47128
47129     /**
47130      * Resets this form.
47131      * @return {BasicForm} this
47132      */
47133     reset : function(){
47134         this.items.each(function(f){
47135             f.reset();
47136         });
47137         
47138         Roo.each(this.childForms || [], function (f) {
47139             f.reset();
47140         });
47141         this.resetHasChanged();
47142         
47143         return this;
47144     },
47145
47146     /**
47147      * Add Roo.form components to this form.
47148      * @param {Field} field1
47149      * @param {Field} field2 (optional)
47150      * @param {Field} etc (optional)
47151      * @return {BasicForm} this
47152      */
47153     add : function(){
47154         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47155         return this;
47156     },
47157
47158
47159     /**
47160      * Removes a field from the items collection (does NOT remove its markup).
47161      * @param {Field} field
47162      * @return {BasicForm} this
47163      */
47164     remove : function(field){
47165         this.items.remove(field);
47166         return this;
47167     },
47168
47169     /**
47170      * Looks at the fields in this form, checks them for an id attribute,
47171      * and calls applyTo on the existing dom element with that id.
47172      * @return {BasicForm} this
47173      */
47174     render : function(){
47175         this.items.each(function(f){
47176             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47177                 f.applyTo(f.id);
47178             }
47179         });
47180         return this;
47181     },
47182
47183     /**
47184      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47185      * @param {Object} values
47186      * @return {BasicForm} this
47187      */
47188     applyToFields : function(o){
47189         this.items.each(function(f){
47190            Roo.apply(f, o);
47191         });
47192         return this;
47193     },
47194
47195     /**
47196      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47197      * @param {Object} values
47198      * @return {BasicForm} this
47199      */
47200     applyIfToFields : function(o){
47201         this.items.each(function(f){
47202            Roo.applyIf(f, o);
47203         });
47204         return this;
47205     }
47206 });
47207
47208 // back compat
47209 Roo.BasicForm = Roo.form.BasicForm;/*
47210  * Based on:
47211  * Ext JS Library 1.1.1
47212  * Copyright(c) 2006-2007, Ext JS, LLC.
47213  *
47214  * Originally Released Under LGPL - original licence link has changed is not relivant.
47215  *
47216  * Fork - LGPL
47217  * <script type="text/javascript">
47218  */
47219
47220 /**
47221  * @class Roo.form.Form
47222  * @extends Roo.form.BasicForm
47223  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47224  * @constructor
47225  * @param {Object} config Configuration options
47226  */
47227 Roo.form.Form = function(config){
47228     var xitems =  [];
47229     if (config.items) {
47230         xitems = config.items;
47231         delete config.items;
47232     }
47233    
47234     
47235     Roo.form.Form.superclass.constructor.call(this, null, config);
47236     this.url = this.url || this.action;
47237     if(!this.root){
47238         this.root = new Roo.form.Layout(Roo.applyIf({
47239             id: Roo.id()
47240         }, config));
47241     }
47242     this.active = this.root;
47243     /**
47244      * Array of all the buttons that have been added to this form via {@link addButton}
47245      * @type Array
47246      */
47247     this.buttons = [];
47248     this.allItems = [];
47249     this.addEvents({
47250         /**
47251          * @event clientvalidation
47252          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47253          * @param {Form} this
47254          * @param {Boolean} valid true if the form has passed client-side validation
47255          */
47256         clientvalidation: true,
47257         /**
47258          * @event rendered
47259          * Fires when the form is rendered
47260          * @param {Roo.form.Form} form
47261          */
47262         rendered : true
47263     });
47264     
47265     if (this.progressUrl) {
47266             // push a hidden field onto the list of fields..
47267             this.addxtype( {
47268                     xns: Roo.form, 
47269                     xtype : 'Hidden', 
47270                     name : 'UPLOAD_IDENTIFIER' 
47271             });
47272         }
47273         
47274     
47275     Roo.each(xitems, this.addxtype, this);
47276     
47277     
47278     
47279 };
47280
47281 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47282     /**
47283      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47284      */
47285     /**
47286      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47287      */
47288     /**
47289      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47290      */
47291     buttonAlign:'center',
47292
47293     /**
47294      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47295      */
47296     minButtonWidth:75,
47297
47298     /**
47299      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47300      * This property cascades to child containers if not set.
47301      */
47302     labelAlign:'left',
47303
47304     /**
47305      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47306      * fires a looping event with that state. This is required to bind buttons to the valid
47307      * state using the config value formBind:true on the button.
47308      */
47309     monitorValid : false,
47310
47311     /**
47312      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47313      */
47314     monitorPoll : 200,
47315     
47316     /**
47317      * @cfg {String} progressUrl - Url to return progress data 
47318      */
47319     
47320     progressUrl : false,
47321   
47322     /**
47323      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47324      * fields are added and the column is closed. If no fields are passed the column remains open
47325      * until end() is called.
47326      * @param {Object} config The config to pass to the column
47327      * @param {Field} field1 (optional)
47328      * @param {Field} field2 (optional)
47329      * @param {Field} etc (optional)
47330      * @return Column The column container object
47331      */
47332     column : function(c){
47333         var col = new Roo.form.Column(c);
47334         this.start(col);
47335         if(arguments.length > 1){ // duplicate code required because of Opera
47336             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47337             this.end();
47338         }
47339         return col;
47340     },
47341
47342     /**
47343      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47344      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47345      * until end() is called.
47346      * @param {Object} config The config to pass to the fieldset
47347      * @param {Field} field1 (optional)
47348      * @param {Field} field2 (optional)
47349      * @param {Field} etc (optional)
47350      * @return FieldSet The fieldset container object
47351      */
47352     fieldset : function(c){
47353         var fs = new Roo.form.FieldSet(c);
47354         this.start(fs);
47355         if(arguments.length > 1){ // duplicate code required because of Opera
47356             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47357             this.end();
47358         }
47359         return fs;
47360     },
47361
47362     /**
47363      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47364      * fields are added and the container is closed. If no fields are passed the container remains open
47365      * until end() is called.
47366      * @param {Object} config The config to pass to the Layout
47367      * @param {Field} field1 (optional)
47368      * @param {Field} field2 (optional)
47369      * @param {Field} etc (optional)
47370      * @return Layout The container object
47371      */
47372     container : function(c){
47373         var l = new Roo.form.Layout(c);
47374         this.start(l);
47375         if(arguments.length > 1){ // duplicate code required because of Opera
47376             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47377             this.end();
47378         }
47379         return l;
47380     },
47381
47382     /**
47383      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47384      * @param {Object} container A Roo.form.Layout or subclass of Layout
47385      * @return {Form} this
47386      */
47387     start : function(c){
47388         // cascade label info
47389         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47390         this.active.stack.push(c);
47391         c.ownerCt = this.active;
47392         this.active = c;
47393         return this;
47394     },
47395
47396     /**
47397      * Closes the current open container
47398      * @return {Form} this
47399      */
47400     end : function(){
47401         if(this.active == this.root){
47402             return this;
47403         }
47404         this.active = this.active.ownerCt;
47405         return this;
47406     },
47407
47408     /**
47409      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47410      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47411      * as the label of the field.
47412      * @param {Field} field1
47413      * @param {Field} field2 (optional)
47414      * @param {Field} etc. (optional)
47415      * @return {Form} this
47416      */
47417     add : function(){
47418         this.active.stack.push.apply(this.active.stack, arguments);
47419         this.allItems.push.apply(this.allItems,arguments);
47420         var r = [];
47421         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47422             if(a[i].isFormField){
47423                 r.push(a[i]);
47424             }
47425         }
47426         if(r.length > 0){
47427             Roo.form.Form.superclass.add.apply(this, r);
47428         }
47429         return this;
47430     },
47431     
47432
47433     
47434     
47435     
47436      /**
47437      * Find any element that has been added to a form, using it's ID or name
47438      * This can include framesets, columns etc. along with regular fields..
47439      * @param {String} id - id or name to find.
47440      
47441      * @return {Element} e - or false if nothing found.
47442      */
47443     findbyId : function(id)
47444     {
47445         var ret = false;
47446         if (!id) {
47447             return ret;
47448         }
47449         Roo.each(this.allItems, function(f){
47450             if (f.id == id || f.name == id ){
47451                 ret = f;
47452                 return false;
47453             }
47454         });
47455         return ret;
47456     },
47457
47458     
47459     
47460     /**
47461      * Render this form into the passed container. This should only be called once!
47462      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47463      * @return {Form} this
47464      */
47465     render : function(ct)
47466     {
47467         
47468         
47469         
47470         ct = Roo.get(ct);
47471         var o = this.autoCreate || {
47472             tag: 'form',
47473             method : this.method || 'POST',
47474             id : this.id || Roo.id()
47475         };
47476         this.initEl(ct.createChild(o));
47477
47478         this.root.render(this.el);
47479         
47480        
47481              
47482         this.items.each(function(f){
47483             f.render('x-form-el-'+f.id);
47484         });
47485
47486         if(this.buttons.length > 0){
47487             // tables are required to maintain order and for correct IE layout
47488             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47489                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47490                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47491             }}, null, true);
47492             var tr = tb.getElementsByTagName('tr')[0];
47493             for(var i = 0, len = this.buttons.length; i < len; i++) {
47494                 var b = this.buttons[i];
47495                 var td = document.createElement('td');
47496                 td.className = 'x-form-btn-td';
47497                 b.render(tr.appendChild(td));
47498             }
47499         }
47500         if(this.monitorValid){ // initialize after render
47501             this.startMonitoring();
47502         }
47503         this.fireEvent('rendered', this);
47504         return this;
47505     },
47506
47507     /**
47508      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47509      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47510      * object or a valid Roo.DomHelper element config
47511      * @param {Function} handler The function called when the button is clicked
47512      * @param {Object} scope (optional) The scope of the handler function
47513      * @return {Roo.Button}
47514      */
47515     addButton : function(config, handler, scope){
47516         var bc = {
47517             handler: handler,
47518             scope: scope,
47519             minWidth: this.minButtonWidth,
47520             hideParent:true
47521         };
47522         if(typeof config == "string"){
47523             bc.text = config;
47524         }else{
47525             Roo.apply(bc, config);
47526         }
47527         var btn = new Roo.Button(null, bc);
47528         this.buttons.push(btn);
47529         return btn;
47530     },
47531
47532      /**
47533      * Adds a series of form elements (using the xtype property as the factory method.
47534      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47535      * @param {Object} config 
47536      */
47537     
47538     addxtype : function()
47539     {
47540         var ar = Array.prototype.slice.call(arguments, 0);
47541         var ret = false;
47542         for(var i = 0; i < ar.length; i++) {
47543             if (!ar[i]) {
47544                 continue; // skip -- if this happends something invalid got sent, we 
47545                 // should ignore it, as basically that interface element will not show up
47546                 // and that should be pretty obvious!!
47547             }
47548             
47549             if (Roo.form[ar[i].xtype]) {
47550                 ar[i].form = this;
47551                 var fe = Roo.factory(ar[i], Roo.form);
47552                 if (!ret) {
47553                     ret = fe;
47554                 }
47555                 fe.form = this;
47556                 if (fe.store) {
47557                     fe.store.form = this;
47558                 }
47559                 if (fe.isLayout) {  
47560                          
47561                     this.start(fe);
47562                     this.allItems.push(fe);
47563                     if (fe.items && fe.addxtype) {
47564                         fe.addxtype.apply(fe, fe.items);
47565                         delete fe.items;
47566                     }
47567                      this.end();
47568                     continue;
47569                 }
47570                 
47571                 
47572                  
47573                 this.add(fe);
47574               //  console.log('adding ' + ar[i].xtype);
47575             }
47576             if (ar[i].xtype == 'Button') {  
47577                 //console.log('adding button');
47578                 //console.log(ar[i]);
47579                 this.addButton(ar[i]);
47580                 this.allItems.push(fe);
47581                 continue;
47582             }
47583             
47584             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47585                 alert('end is not supported on xtype any more, use items');
47586             //    this.end();
47587             //    //console.log('adding end');
47588             }
47589             
47590         }
47591         return ret;
47592     },
47593     
47594     /**
47595      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47596      * option "monitorValid"
47597      */
47598     startMonitoring : function(){
47599         if(!this.bound){
47600             this.bound = true;
47601             Roo.TaskMgr.start({
47602                 run : this.bindHandler,
47603                 interval : this.monitorPoll || 200,
47604                 scope: this
47605             });
47606         }
47607     },
47608
47609     /**
47610      * Stops monitoring of the valid state of this form
47611      */
47612     stopMonitoring : function(){
47613         this.bound = false;
47614     },
47615
47616     // private
47617     bindHandler : function(){
47618         if(!this.bound){
47619             return false; // stops binding
47620         }
47621         var valid = true;
47622         this.items.each(function(f){
47623             if(!f.isValid(true)){
47624                 valid = false;
47625                 return false;
47626             }
47627         });
47628         for(var i = 0, len = this.buttons.length; i < len; i++){
47629             var btn = this.buttons[i];
47630             if(btn.formBind === true && btn.disabled === valid){
47631                 btn.setDisabled(!valid);
47632             }
47633         }
47634         this.fireEvent('clientvalidation', this, valid);
47635     }
47636     
47637     
47638     
47639     
47640     
47641     
47642     
47643     
47644 });
47645
47646
47647 // back compat
47648 Roo.Form = Roo.form.Form;
47649 /*
47650  * Based on:
47651  * Ext JS Library 1.1.1
47652  * Copyright(c) 2006-2007, Ext JS, LLC.
47653  *
47654  * Originally Released Under LGPL - original licence link has changed is not relivant.
47655  *
47656  * Fork - LGPL
47657  * <script type="text/javascript">
47658  */
47659
47660 // as we use this in bootstrap.
47661 Roo.namespace('Roo.form');
47662  /**
47663  * @class Roo.form.Action
47664  * Internal Class used to handle form actions
47665  * @constructor
47666  * @param {Roo.form.BasicForm} el The form element or its id
47667  * @param {Object} config Configuration options
47668  */
47669
47670  
47671  
47672 // define the action interface
47673 Roo.form.Action = function(form, options){
47674     this.form = form;
47675     this.options = options || {};
47676 };
47677 /**
47678  * Client Validation Failed
47679  * @const 
47680  */
47681 Roo.form.Action.CLIENT_INVALID = 'client';
47682 /**
47683  * Server Validation Failed
47684  * @const 
47685  */
47686 Roo.form.Action.SERVER_INVALID = 'server';
47687  /**
47688  * Connect to Server Failed
47689  * @const 
47690  */
47691 Roo.form.Action.CONNECT_FAILURE = 'connect';
47692 /**
47693  * Reading Data from Server Failed
47694  * @const 
47695  */
47696 Roo.form.Action.LOAD_FAILURE = 'load';
47697
47698 Roo.form.Action.prototype = {
47699     type : 'default',
47700     failureType : undefined,
47701     response : undefined,
47702     result : undefined,
47703
47704     // interface method
47705     run : function(options){
47706
47707     },
47708
47709     // interface method
47710     success : function(response){
47711
47712     },
47713
47714     // interface method
47715     handleResponse : function(response){
47716
47717     },
47718
47719     // default connection failure
47720     failure : function(response){
47721         
47722         this.response = response;
47723         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47724         this.form.afterAction(this, false);
47725     },
47726
47727     processResponse : function(response){
47728         this.response = response;
47729         if(!response.responseText){
47730             return true;
47731         }
47732         this.result = this.handleResponse(response);
47733         return this.result;
47734     },
47735
47736     // utility functions used internally
47737     getUrl : function(appendParams){
47738         var url = this.options.url || this.form.url || this.form.el.dom.action;
47739         if(appendParams){
47740             var p = this.getParams();
47741             if(p){
47742                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47743             }
47744         }
47745         return url;
47746     },
47747
47748     getMethod : function(){
47749         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47750     },
47751
47752     getParams : function(){
47753         var bp = this.form.baseParams;
47754         var p = this.options.params;
47755         if(p){
47756             if(typeof p == "object"){
47757                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47758             }else if(typeof p == 'string' && bp){
47759                 p += '&' + Roo.urlEncode(bp);
47760             }
47761         }else if(bp){
47762             p = Roo.urlEncode(bp);
47763         }
47764         return p;
47765     },
47766
47767     createCallback : function(){
47768         return {
47769             success: this.success,
47770             failure: this.failure,
47771             scope: this,
47772             timeout: (this.form.timeout*1000),
47773             upload: this.form.fileUpload ? this.success : undefined
47774         };
47775     }
47776 };
47777
47778 Roo.form.Action.Submit = function(form, options){
47779     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47780 };
47781
47782 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47783     type : 'submit',
47784
47785     haveProgress : false,
47786     uploadComplete : false,
47787     
47788     // uploadProgress indicator.
47789     uploadProgress : function()
47790     {
47791         if (!this.form.progressUrl) {
47792             return;
47793         }
47794         
47795         if (!this.haveProgress) {
47796             Roo.MessageBox.progress("Uploading", "Uploading");
47797         }
47798         if (this.uploadComplete) {
47799            Roo.MessageBox.hide();
47800            return;
47801         }
47802         
47803         this.haveProgress = true;
47804    
47805         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47806         
47807         var c = new Roo.data.Connection();
47808         c.request({
47809             url : this.form.progressUrl,
47810             params: {
47811                 id : uid
47812             },
47813             method: 'GET',
47814             success : function(req){
47815                //console.log(data);
47816                 var rdata = false;
47817                 var edata;
47818                 try  {
47819                    rdata = Roo.decode(req.responseText)
47820                 } catch (e) {
47821                     Roo.log("Invalid data from server..");
47822                     Roo.log(edata);
47823                     return;
47824                 }
47825                 if (!rdata || !rdata.success) {
47826                     Roo.log(rdata);
47827                     Roo.MessageBox.alert(Roo.encode(rdata));
47828                     return;
47829                 }
47830                 var data = rdata.data;
47831                 
47832                 if (this.uploadComplete) {
47833                    Roo.MessageBox.hide();
47834                    return;
47835                 }
47836                    
47837                 if (data){
47838                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47839                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47840                     );
47841                 }
47842                 this.uploadProgress.defer(2000,this);
47843             },
47844        
47845             failure: function(data) {
47846                 Roo.log('progress url failed ');
47847                 Roo.log(data);
47848             },
47849             scope : this
47850         });
47851            
47852     },
47853     
47854     
47855     run : function()
47856     {
47857         // run get Values on the form, so it syncs any secondary forms.
47858         this.form.getValues();
47859         
47860         var o = this.options;
47861         var method = this.getMethod();
47862         var isPost = method == 'POST';
47863         if(o.clientValidation === false || this.form.isValid()){
47864             
47865             if (this.form.progressUrl) {
47866                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47867                     (new Date() * 1) + '' + Math.random());
47868                     
47869             } 
47870             
47871             
47872             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47873                 form:this.form.el.dom,
47874                 url:this.getUrl(!isPost),
47875                 method: method,
47876                 params:isPost ? this.getParams() : null,
47877                 isUpload: this.form.fileUpload
47878             }));
47879             
47880             this.uploadProgress();
47881
47882         }else if (o.clientValidation !== false){ // client validation failed
47883             this.failureType = Roo.form.Action.CLIENT_INVALID;
47884             this.form.afterAction(this, false);
47885         }
47886     },
47887
47888     success : function(response)
47889     {
47890         this.uploadComplete= true;
47891         if (this.haveProgress) {
47892             Roo.MessageBox.hide();
47893         }
47894         
47895         
47896         var result = this.processResponse(response);
47897         if(result === true || result.success){
47898             this.form.afterAction(this, true);
47899             return;
47900         }
47901         if(result.errors){
47902             this.form.markInvalid(result.errors);
47903             this.failureType = Roo.form.Action.SERVER_INVALID;
47904         }
47905         this.form.afterAction(this, false);
47906     },
47907     failure : function(response)
47908     {
47909         this.uploadComplete= true;
47910         if (this.haveProgress) {
47911             Roo.MessageBox.hide();
47912         }
47913         
47914         this.response = response;
47915         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47916         this.form.afterAction(this, false);
47917     },
47918     
47919     handleResponse : function(response){
47920         if(this.form.errorReader){
47921             var rs = this.form.errorReader.read(response);
47922             var errors = [];
47923             if(rs.records){
47924                 for(var i = 0, len = rs.records.length; i < len; i++) {
47925                     var r = rs.records[i];
47926                     errors[i] = r.data;
47927                 }
47928             }
47929             if(errors.length < 1){
47930                 errors = null;
47931             }
47932             return {
47933                 success : rs.success,
47934                 errors : errors
47935             };
47936         }
47937         var ret = false;
47938         try {
47939             ret = Roo.decode(response.responseText);
47940         } catch (e) {
47941             ret = {
47942                 success: false,
47943                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47944                 errors : []
47945             };
47946         }
47947         return ret;
47948         
47949     }
47950 });
47951
47952
47953 Roo.form.Action.Load = function(form, options){
47954     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47955     this.reader = this.form.reader;
47956 };
47957
47958 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47959     type : 'load',
47960
47961     run : function(){
47962         
47963         Roo.Ajax.request(Roo.apply(
47964                 this.createCallback(), {
47965                     method:this.getMethod(),
47966                     url:this.getUrl(false),
47967                     params:this.getParams()
47968         }));
47969     },
47970
47971     success : function(response){
47972         
47973         var result = this.processResponse(response);
47974         if(result === true || !result.success || !result.data){
47975             this.failureType = Roo.form.Action.LOAD_FAILURE;
47976             this.form.afterAction(this, false);
47977             return;
47978         }
47979         this.form.clearInvalid();
47980         this.form.setValues(result.data);
47981         this.form.afterAction(this, true);
47982     },
47983
47984     handleResponse : function(response){
47985         if(this.form.reader){
47986             var rs = this.form.reader.read(response);
47987             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47988             return {
47989                 success : rs.success,
47990                 data : data
47991             };
47992         }
47993         return Roo.decode(response.responseText);
47994     }
47995 });
47996
47997 Roo.form.Action.ACTION_TYPES = {
47998     'load' : Roo.form.Action.Load,
47999     'submit' : Roo.form.Action.Submit
48000 };/*
48001  * Based on:
48002  * Ext JS Library 1.1.1
48003  * Copyright(c) 2006-2007, Ext JS, LLC.
48004  *
48005  * Originally Released Under LGPL - original licence link has changed is not relivant.
48006  *
48007  * Fork - LGPL
48008  * <script type="text/javascript">
48009  */
48010  
48011 /**
48012  * @class Roo.form.Layout
48013  * @extends Roo.Component
48014  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48015  * @constructor
48016  * @param {Object} config Configuration options
48017  */
48018 Roo.form.Layout = function(config){
48019     var xitems = [];
48020     if (config.items) {
48021         xitems = config.items;
48022         delete config.items;
48023     }
48024     Roo.form.Layout.superclass.constructor.call(this, config);
48025     this.stack = [];
48026     Roo.each(xitems, this.addxtype, this);
48027      
48028 };
48029
48030 Roo.extend(Roo.form.Layout, Roo.Component, {
48031     /**
48032      * @cfg {String/Object} autoCreate
48033      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48034      */
48035     /**
48036      * @cfg {String/Object/Function} style
48037      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48038      * a function which returns such a specification.
48039      */
48040     /**
48041      * @cfg {String} labelAlign
48042      * Valid values are "left," "top" and "right" (defaults to "left")
48043      */
48044     /**
48045      * @cfg {Number} labelWidth
48046      * Fixed width in pixels of all field labels (defaults to undefined)
48047      */
48048     /**
48049      * @cfg {Boolean} clear
48050      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48051      */
48052     clear : true,
48053     /**
48054      * @cfg {String} labelSeparator
48055      * The separator to use after field labels (defaults to ':')
48056      */
48057     labelSeparator : ':',
48058     /**
48059      * @cfg {Boolean} hideLabels
48060      * True to suppress the display of field labels in this layout (defaults to false)
48061      */
48062     hideLabels : false,
48063
48064     // private
48065     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48066     
48067     isLayout : true,
48068     
48069     // private
48070     onRender : function(ct, position){
48071         if(this.el){ // from markup
48072             this.el = Roo.get(this.el);
48073         }else {  // generate
48074             var cfg = this.getAutoCreate();
48075             this.el = ct.createChild(cfg, position);
48076         }
48077         if(this.style){
48078             this.el.applyStyles(this.style);
48079         }
48080         if(this.labelAlign){
48081             this.el.addClass('x-form-label-'+this.labelAlign);
48082         }
48083         if(this.hideLabels){
48084             this.labelStyle = "display:none";
48085             this.elementStyle = "padding-left:0;";
48086         }else{
48087             if(typeof this.labelWidth == 'number'){
48088                 this.labelStyle = "width:"+this.labelWidth+"px;";
48089                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48090             }
48091             if(this.labelAlign == 'top'){
48092                 this.labelStyle = "width:auto;";
48093                 this.elementStyle = "padding-left:0;";
48094             }
48095         }
48096         var stack = this.stack;
48097         var slen = stack.length;
48098         if(slen > 0){
48099             if(!this.fieldTpl){
48100                 var t = new Roo.Template(
48101                     '<div class="x-form-item {5}">',
48102                         '<label for="{0}" style="{2}">{1}{4}</label>',
48103                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48104                         '</div>',
48105                     '</div><div class="x-form-clear-left"></div>'
48106                 );
48107                 t.disableFormats = true;
48108                 t.compile();
48109                 Roo.form.Layout.prototype.fieldTpl = t;
48110             }
48111             for(var i = 0; i < slen; i++) {
48112                 if(stack[i].isFormField){
48113                     this.renderField(stack[i]);
48114                 }else{
48115                     this.renderComponent(stack[i]);
48116                 }
48117             }
48118         }
48119         if(this.clear){
48120             this.el.createChild({cls:'x-form-clear'});
48121         }
48122     },
48123
48124     // private
48125     renderField : function(f){
48126         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48127                f.id, //0
48128                f.fieldLabel, //1
48129                f.labelStyle||this.labelStyle||'', //2
48130                this.elementStyle||'', //3
48131                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48132                f.itemCls||this.itemCls||''  //5
48133        ], true).getPrevSibling());
48134     },
48135
48136     // private
48137     renderComponent : function(c){
48138         c.render(c.isLayout ? this.el : this.el.createChild());    
48139     },
48140     /**
48141      * Adds a object form elements (using the xtype property as the factory method.)
48142      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48143      * @param {Object} config 
48144      */
48145     addxtype : function(o)
48146     {
48147         // create the lement.
48148         o.form = this.form;
48149         var fe = Roo.factory(o, Roo.form);
48150         this.form.allItems.push(fe);
48151         this.stack.push(fe);
48152         
48153         if (fe.isFormField) {
48154             this.form.items.add(fe);
48155         }
48156          
48157         return fe;
48158     }
48159 });
48160
48161 /**
48162  * @class Roo.form.Column
48163  * @extends Roo.form.Layout
48164  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48165  * @constructor
48166  * @param {Object} config Configuration options
48167  */
48168 Roo.form.Column = function(config){
48169     Roo.form.Column.superclass.constructor.call(this, config);
48170 };
48171
48172 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48173     /**
48174      * @cfg {Number/String} width
48175      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48176      */
48177     /**
48178      * @cfg {String/Object} autoCreate
48179      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48180      */
48181
48182     // private
48183     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48184
48185     // private
48186     onRender : function(ct, position){
48187         Roo.form.Column.superclass.onRender.call(this, ct, position);
48188         if(this.width){
48189             this.el.setWidth(this.width);
48190         }
48191     }
48192 });
48193
48194
48195 /**
48196  * @class Roo.form.Row
48197  * @extends Roo.form.Layout
48198  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48199  * @constructor
48200  * @param {Object} config Configuration options
48201  */
48202
48203  
48204 Roo.form.Row = function(config){
48205     Roo.form.Row.superclass.constructor.call(this, config);
48206 };
48207  
48208 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48209       /**
48210      * @cfg {Number/String} width
48211      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48212      */
48213     /**
48214      * @cfg {Number/String} height
48215      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48216      */
48217     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48218     
48219     padWidth : 20,
48220     // private
48221     onRender : function(ct, position){
48222         //console.log('row render');
48223         if(!this.rowTpl){
48224             var t = new Roo.Template(
48225                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48226                     '<label for="{0}" style="{2}">{1}{4}</label>',
48227                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48228                     '</div>',
48229                 '</div>'
48230             );
48231             t.disableFormats = true;
48232             t.compile();
48233             Roo.form.Layout.prototype.rowTpl = t;
48234         }
48235         this.fieldTpl = this.rowTpl;
48236         
48237         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48238         var labelWidth = 100;
48239         
48240         if ((this.labelAlign != 'top')) {
48241             if (typeof this.labelWidth == 'number') {
48242                 labelWidth = this.labelWidth
48243             }
48244             this.padWidth =  20 + labelWidth;
48245             
48246         }
48247         
48248         Roo.form.Column.superclass.onRender.call(this, ct, position);
48249         if(this.width){
48250             this.el.setWidth(this.width);
48251         }
48252         if(this.height){
48253             this.el.setHeight(this.height);
48254         }
48255     },
48256     
48257     // private
48258     renderField : function(f){
48259         f.fieldEl = this.fieldTpl.append(this.el, [
48260                f.id, f.fieldLabel,
48261                f.labelStyle||this.labelStyle||'',
48262                this.elementStyle||'',
48263                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48264                f.itemCls||this.itemCls||'',
48265                f.width ? f.width + this.padWidth : 160 + this.padWidth
48266        ],true);
48267     }
48268 });
48269  
48270
48271 /**
48272  * @class Roo.form.FieldSet
48273  * @extends Roo.form.Layout
48274  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48275  * @constructor
48276  * @param {Object} config Configuration options
48277  */
48278 Roo.form.FieldSet = function(config){
48279     Roo.form.FieldSet.superclass.constructor.call(this, config);
48280 };
48281
48282 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48283     /**
48284      * @cfg {String} legend
48285      * The text to display as the legend for the FieldSet (defaults to '')
48286      */
48287     /**
48288      * @cfg {String/Object} autoCreate
48289      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48290      */
48291
48292     // private
48293     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48294
48295     // private
48296     onRender : function(ct, position){
48297         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48298         if(this.legend){
48299             this.setLegend(this.legend);
48300         }
48301     },
48302
48303     // private
48304     setLegend : function(text){
48305         if(this.rendered){
48306             this.el.child('legend').update(text);
48307         }
48308     }
48309 });/*
48310  * Based on:
48311  * Ext JS Library 1.1.1
48312  * Copyright(c) 2006-2007, Ext JS, LLC.
48313  *
48314  * Originally Released Under LGPL - original licence link has changed is not relivant.
48315  *
48316  * Fork - LGPL
48317  * <script type="text/javascript">
48318  */
48319 /**
48320  * @class Roo.form.VTypes
48321  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48322  * @singleton
48323  */
48324 Roo.form.VTypes = function(){
48325     // closure these in so they are only created once.
48326     var alpha = /^[a-zA-Z_]+$/;
48327     var alphanum = /^[a-zA-Z0-9_]+$/;
48328     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48329     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48330
48331     // All these messages and functions are configurable
48332     return {
48333         /**
48334          * The function used to validate email addresses
48335          * @param {String} value The email address
48336          */
48337         'email' : function(v){
48338             return email.test(v);
48339         },
48340         /**
48341          * The error text to display when the email validation function returns false
48342          * @type String
48343          */
48344         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48345         /**
48346          * The keystroke filter mask to be applied on email input
48347          * @type RegExp
48348          */
48349         'emailMask' : /[a-z0-9_\.\-@]/i,
48350
48351         /**
48352          * The function used to validate URLs
48353          * @param {String} value The URL
48354          */
48355         'url' : function(v){
48356             return url.test(v);
48357         },
48358         /**
48359          * The error text to display when the url validation function returns false
48360          * @type String
48361          */
48362         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48363         
48364         /**
48365          * The function used to validate alpha values
48366          * @param {String} value The value
48367          */
48368         'alpha' : function(v){
48369             return alpha.test(v);
48370         },
48371         /**
48372          * The error text to display when the alpha validation function returns false
48373          * @type String
48374          */
48375         'alphaText' : 'This field should only contain letters and _',
48376         /**
48377          * The keystroke filter mask to be applied on alpha input
48378          * @type RegExp
48379          */
48380         'alphaMask' : /[a-z_]/i,
48381
48382         /**
48383          * The function used to validate alphanumeric values
48384          * @param {String} value The value
48385          */
48386         'alphanum' : function(v){
48387             return alphanum.test(v);
48388         },
48389         /**
48390          * The error text to display when the alphanumeric validation function returns false
48391          * @type String
48392          */
48393         'alphanumText' : 'This field should only contain letters, numbers and _',
48394         /**
48395          * The keystroke filter mask to be applied on alphanumeric input
48396          * @type RegExp
48397          */
48398         'alphanumMask' : /[a-z0-9_]/i
48399     };
48400 }();//<script type="text/javascript">
48401
48402 /**
48403  * @class Roo.form.FCKeditor
48404  * @extends Roo.form.TextArea
48405  * Wrapper around the FCKEditor http://www.fckeditor.net
48406  * @constructor
48407  * Creates a new FCKeditor
48408  * @param {Object} config Configuration options
48409  */
48410 Roo.form.FCKeditor = function(config){
48411     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48412     this.addEvents({
48413          /**
48414          * @event editorinit
48415          * Fired when the editor is initialized - you can add extra handlers here..
48416          * @param {FCKeditor} this
48417          * @param {Object} the FCK object.
48418          */
48419         editorinit : true
48420     });
48421     
48422     
48423 };
48424 Roo.form.FCKeditor.editors = { };
48425 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48426 {
48427     //defaultAutoCreate : {
48428     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48429     //},
48430     // private
48431     /**
48432      * @cfg {Object} fck options - see fck manual for details.
48433      */
48434     fckconfig : false,
48435     
48436     /**
48437      * @cfg {Object} fck toolbar set (Basic or Default)
48438      */
48439     toolbarSet : 'Basic',
48440     /**
48441      * @cfg {Object} fck BasePath
48442      */ 
48443     basePath : '/fckeditor/',
48444     
48445     
48446     frame : false,
48447     
48448     value : '',
48449     
48450    
48451     onRender : function(ct, position)
48452     {
48453         if(!this.el){
48454             this.defaultAutoCreate = {
48455                 tag: "textarea",
48456                 style:"width:300px;height:60px;",
48457                 autocomplete: "new-password"
48458             };
48459         }
48460         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48461         /*
48462         if(this.grow){
48463             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48464             if(this.preventScrollbars){
48465                 this.el.setStyle("overflow", "hidden");
48466             }
48467             this.el.setHeight(this.growMin);
48468         }
48469         */
48470         //console.log('onrender' + this.getId() );
48471         Roo.form.FCKeditor.editors[this.getId()] = this;
48472          
48473
48474         this.replaceTextarea() ;
48475         
48476     },
48477     
48478     getEditor : function() {
48479         return this.fckEditor;
48480     },
48481     /**
48482      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48483      * @param {Mixed} value The value to set
48484      */
48485     
48486     
48487     setValue : function(value)
48488     {
48489         //console.log('setValue: ' + value);
48490         
48491         if(typeof(value) == 'undefined') { // not sure why this is happending...
48492             return;
48493         }
48494         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48495         
48496         //if(!this.el || !this.getEditor()) {
48497         //    this.value = value;
48498             //this.setValue.defer(100,this,[value]);    
48499         //    return;
48500         //} 
48501         
48502         if(!this.getEditor()) {
48503             return;
48504         }
48505         
48506         this.getEditor().SetData(value);
48507         
48508         //
48509
48510     },
48511
48512     /**
48513      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48514      * @return {Mixed} value The field value
48515      */
48516     getValue : function()
48517     {
48518         
48519         if (this.frame && this.frame.dom.style.display == 'none') {
48520             return Roo.form.FCKeditor.superclass.getValue.call(this);
48521         }
48522         
48523         if(!this.el || !this.getEditor()) {
48524            
48525            // this.getValue.defer(100,this); 
48526             return this.value;
48527         }
48528        
48529         
48530         var value=this.getEditor().GetData();
48531         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48532         return Roo.form.FCKeditor.superclass.getValue.call(this);
48533         
48534
48535     },
48536
48537     /**
48538      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48539      * @return {Mixed} value The field value
48540      */
48541     getRawValue : function()
48542     {
48543         if (this.frame && this.frame.dom.style.display == 'none') {
48544             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48545         }
48546         
48547         if(!this.el || !this.getEditor()) {
48548             //this.getRawValue.defer(100,this); 
48549             return this.value;
48550             return;
48551         }
48552         
48553         
48554         
48555         var value=this.getEditor().GetData();
48556         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48557         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48558          
48559     },
48560     
48561     setSize : function(w,h) {
48562         
48563         
48564         
48565         //if (this.frame && this.frame.dom.style.display == 'none') {
48566         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48567         //    return;
48568         //}
48569         //if(!this.el || !this.getEditor()) {
48570         //    this.setSize.defer(100,this, [w,h]); 
48571         //    return;
48572         //}
48573         
48574         
48575         
48576         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48577         
48578         this.frame.dom.setAttribute('width', w);
48579         this.frame.dom.setAttribute('height', h);
48580         this.frame.setSize(w,h);
48581         
48582     },
48583     
48584     toggleSourceEdit : function(value) {
48585         
48586       
48587          
48588         this.el.dom.style.display = value ? '' : 'none';
48589         this.frame.dom.style.display = value ?  'none' : '';
48590         
48591     },
48592     
48593     
48594     focus: function(tag)
48595     {
48596         if (this.frame.dom.style.display == 'none') {
48597             return Roo.form.FCKeditor.superclass.focus.call(this);
48598         }
48599         if(!this.el || !this.getEditor()) {
48600             this.focus.defer(100,this, [tag]); 
48601             return;
48602         }
48603         
48604         
48605         
48606         
48607         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48608         this.getEditor().Focus();
48609         if (tgs.length) {
48610             if (!this.getEditor().Selection.GetSelection()) {
48611                 this.focus.defer(100,this, [tag]); 
48612                 return;
48613             }
48614             
48615             
48616             var r = this.getEditor().EditorDocument.createRange();
48617             r.setStart(tgs[0],0);
48618             r.setEnd(tgs[0],0);
48619             this.getEditor().Selection.GetSelection().removeAllRanges();
48620             this.getEditor().Selection.GetSelection().addRange(r);
48621             this.getEditor().Focus();
48622         }
48623         
48624     },
48625     
48626     
48627     
48628     replaceTextarea : function()
48629     {
48630         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48631             return ;
48632         }
48633         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48634         //{
48635             // We must check the elements firstly using the Id and then the name.
48636         var oTextarea = document.getElementById( this.getId() );
48637         
48638         var colElementsByName = document.getElementsByName( this.getId() ) ;
48639          
48640         oTextarea.style.display = 'none' ;
48641
48642         if ( oTextarea.tabIndex ) {            
48643             this.TabIndex = oTextarea.tabIndex ;
48644         }
48645         
48646         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48647         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48648         this.frame = Roo.get(this.getId() + '___Frame')
48649     },
48650     
48651     _getConfigHtml : function()
48652     {
48653         var sConfig = '' ;
48654
48655         for ( var o in this.fckconfig ) {
48656             sConfig += sConfig.length > 0  ? '&amp;' : '';
48657             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48658         }
48659
48660         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48661     },
48662     
48663     
48664     _getIFrameHtml : function()
48665     {
48666         var sFile = 'fckeditor.html' ;
48667         /* no idea what this is about..
48668         try
48669         {
48670             if ( (/fcksource=true/i).test( window.top.location.search ) )
48671                 sFile = 'fckeditor.original.html' ;
48672         }
48673         catch (e) { 
48674         */
48675
48676         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48677         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48678         
48679         
48680         var html = '<iframe id="' + this.getId() +
48681             '___Frame" src="' + sLink +
48682             '" width="' + this.width +
48683             '" height="' + this.height + '"' +
48684             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48685             ' frameborder="0" scrolling="no"></iframe>' ;
48686
48687         return html ;
48688     },
48689     
48690     _insertHtmlBefore : function( html, element )
48691     {
48692         if ( element.insertAdjacentHTML )       {
48693             // IE
48694             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48695         } else { // Gecko
48696             var oRange = document.createRange() ;
48697             oRange.setStartBefore( element ) ;
48698             var oFragment = oRange.createContextualFragment( html );
48699             element.parentNode.insertBefore( oFragment, element ) ;
48700         }
48701     }
48702     
48703     
48704   
48705     
48706     
48707     
48708     
48709
48710 });
48711
48712 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48713
48714 function FCKeditor_OnComplete(editorInstance){
48715     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48716     f.fckEditor = editorInstance;
48717     //console.log("loaded");
48718     f.fireEvent('editorinit', f, editorInstance);
48719
48720   
48721
48722  
48723
48724
48725
48726
48727
48728
48729
48730
48731
48732
48733
48734
48735
48736
48737
48738 //<script type="text/javascript">
48739 /**
48740  * @class Roo.form.GridField
48741  * @extends Roo.form.Field
48742  * Embed a grid (or editable grid into a form)
48743  * STATUS ALPHA
48744  * 
48745  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48746  * it needs 
48747  * xgrid.store = Roo.data.Store
48748  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48749  * xgrid.store.reader = Roo.data.JsonReader 
48750  * 
48751  * 
48752  * @constructor
48753  * Creates a new GridField
48754  * @param {Object} config Configuration options
48755  */
48756 Roo.form.GridField = function(config){
48757     Roo.form.GridField.superclass.constructor.call(this, config);
48758      
48759 };
48760
48761 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48762     /**
48763      * @cfg {Number} width  - used to restrict width of grid..
48764      */
48765     width : 100,
48766     /**
48767      * @cfg {Number} height - used to restrict height of grid..
48768      */
48769     height : 50,
48770      /**
48771      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48772          * 
48773          *}
48774      */
48775     xgrid : false, 
48776     /**
48777      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48778      * {tag: "input", type: "checkbox", autocomplete: "off"})
48779      */
48780    // defaultAutoCreate : { tag: 'div' },
48781     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48782     /**
48783      * @cfg {String} addTitle Text to include for adding a title.
48784      */
48785     addTitle : false,
48786     //
48787     onResize : function(){
48788         Roo.form.Field.superclass.onResize.apply(this, arguments);
48789     },
48790
48791     initEvents : function(){
48792         // Roo.form.Checkbox.superclass.initEvents.call(this);
48793         // has no events...
48794        
48795     },
48796
48797
48798     getResizeEl : function(){
48799         return this.wrap;
48800     },
48801
48802     getPositionEl : function(){
48803         return this.wrap;
48804     },
48805
48806     // private
48807     onRender : function(ct, position){
48808         
48809         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48810         var style = this.style;
48811         delete this.style;
48812         
48813         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48814         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48815         this.viewEl = this.wrap.createChild({ tag: 'div' });
48816         if (style) {
48817             this.viewEl.applyStyles(style);
48818         }
48819         if (this.width) {
48820             this.viewEl.setWidth(this.width);
48821         }
48822         if (this.height) {
48823             this.viewEl.setHeight(this.height);
48824         }
48825         //if(this.inputValue !== undefined){
48826         //this.setValue(this.value);
48827         
48828         
48829         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48830         
48831         
48832         this.grid.render();
48833         this.grid.getDataSource().on('remove', this.refreshValue, this);
48834         this.grid.getDataSource().on('update', this.refreshValue, this);
48835         this.grid.on('afteredit', this.refreshValue, this);
48836  
48837     },
48838      
48839     
48840     /**
48841      * Sets the value of the item. 
48842      * @param {String} either an object  or a string..
48843      */
48844     setValue : function(v){
48845         //this.value = v;
48846         v = v || []; // empty set..
48847         // this does not seem smart - it really only affects memoryproxy grids..
48848         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48849             var ds = this.grid.getDataSource();
48850             // assumes a json reader..
48851             var data = {}
48852             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48853             ds.loadData( data);
48854         }
48855         // clear selection so it does not get stale.
48856         if (this.grid.sm) { 
48857             this.grid.sm.clearSelections();
48858         }
48859         
48860         Roo.form.GridField.superclass.setValue.call(this, v);
48861         this.refreshValue();
48862         // should load data in the grid really....
48863     },
48864     
48865     // private
48866     refreshValue: function() {
48867          var val = [];
48868         this.grid.getDataSource().each(function(r) {
48869             val.push(r.data);
48870         });
48871         this.el.dom.value = Roo.encode(val);
48872     }
48873     
48874      
48875     
48876     
48877 });/*
48878  * Based on:
48879  * Ext JS Library 1.1.1
48880  * Copyright(c) 2006-2007, Ext JS, LLC.
48881  *
48882  * Originally Released Under LGPL - original licence link has changed is not relivant.
48883  *
48884  * Fork - LGPL
48885  * <script type="text/javascript">
48886  */
48887 /**
48888  * @class Roo.form.DisplayField
48889  * @extends Roo.form.Field
48890  * A generic Field to display non-editable data.
48891  * @cfg {Boolean} closable (true|false) default false
48892  * @constructor
48893  * Creates a new Display Field item.
48894  * @param {Object} config Configuration options
48895  */
48896 Roo.form.DisplayField = function(config){
48897     Roo.form.DisplayField.superclass.constructor.call(this, config);
48898     
48899     this.addEvents({
48900         /**
48901          * @event close
48902          * Fires after the click the close btn
48903              * @param {Roo.form.DisplayField} this
48904              */
48905         close : true
48906     });
48907 };
48908
48909 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48910     inputType:      'hidden',
48911     allowBlank:     true,
48912     readOnly:         true,
48913     
48914  
48915     /**
48916      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48917      */
48918     focusClass : undefined,
48919     /**
48920      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48921      */
48922     fieldClass: 'x-form-field',
48923     
48924      /**
48925      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48926      */
48927     valueRenderer: undefined,
48928     
48929     width: 100,
48930     /**
48931      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48932      * {tag: "input", type: "checkbox", autocomplete: "off"})
48933      */
48934      
48935  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48936  
48937     closable : false,
48938     
48939     onResize : function(){
48940         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48941         
48942     },
48943
48944     initEvents : function(){
48945         // Roo.form.Checkbox.superclass.initEvents.call(this);
48946         // has no events...
48947         
48948         if(this.closable){
48949             this.closeEl.on('click', this.onClose, this);
48950         }
48951        
48952     },
48953
48954
48955     getResizeEl : function(){
48956         return this.wrap;
48957     },
48958
48959     getPositionEl : function(){
48960         return this.wrap;
48961     },
48962
48963     // private
48964     onRender : function(ct, position){
48965         
48966         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48967         //if(this.inputValue !== undefined){
48968         this.wrap = this.el.wrap();
48969         
48970         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48971         
48972         if(this.closable){
48973             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48974         }
48975         
48976         if (this.bodyStyle) {
48977             this.viewEl.applyStyles(this.bodyStyle);
48978         }
48979         //this.viewEl.setStyle('padding', '2px');
48980         
48981         this.setValue(this.value);
48982         
48983     },
48984 /*
48985     // private
48986     initValue : Roo.emptyFn,
48987
48988   */
48989
48990         // private
48991     onClick : function(){
48992         
48993     },
48994
48995     /**
48996      * Sets the checked state of the checkbox.
48997      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48998      */
48999     setValue : function(v){
49000         this.value = v;
49001         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49002         // this might be called before we have a dom element..
49003         if (!this.viewEl) {
49004             return;
49005         }
49006         this.viewEl.dom.innerHTML = html;
49007         Roo.form.DisplayField.superclass.setValue.call(this, v);
49008
49009     },
49010     
49011     onClose : function(e)
49012     {
49013         e.preventDefault();
49014         
49015         this.fireEvent('close', this);
49016     }
49017 });/*
49018  * 
49019  * Licence- LGPL
49020  * 
49021  */
49022
49023 /**
49024  * @class Roo.form.DayPicker
49025  * @extends Roo.form.Field
49026  * A Day picker show [M] [T] [W] ....
49027  * @constructor
49028  * Creates a new Day Picker
49029  * @param {Object} config Configuration options
49030  */
49031 Roo.form.DayPicker= function(config){
49032     Roo.form.DayPicker.superclass.constructor.call(this, config);
49033      
49034 };
49035
49036 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49037     /**
49038      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49039      */
49040     focusClass : undefined,
49041     /**
49042      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49043      */
49044     fieldClass: "x-form-field",
49045    
49046     /**
49047      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49048      * {tag: "input", type: "checkbox", autocomplete: "off"})
49049      */
49050     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49051     
49052    
49053     actionMode : 'viewEl', 
49054     //
49055     // private
49056  
49057     inputType : 'hidden',
49058     
49059      
49060     inputElement: false, // real input element?
49061     basedOn: false, // ????
49062     
49063     isFormField: true, // not sure where this is needed!!!!
49064
49065     onResize : function(){
49066         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49067         if(!this.boxLabel){
49068             this.el.alignTo(this.wrap, 'c-c');
49069         }
49070     },
49071
49072     initEvents : function(){
49073         Roo.form.Checkbox.superclass.initEvents.call(this);
49074         this.el.on("click", this.onClick,  this);
49075         this.el.on("change", this.onClick,  this);
49076     },
49077
49078
49079     getResizeEl : function(){
49080         return this.wrap;
49081     },
49082
49083     getPositionEl : function(){
49084         return this.wrap;
49085     },
49086
49087     
49088     // private
49089     onRender : function(ct, position){
49090         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49091        
49092         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49093         
49094         var r1 = '<table><tr>';
49095         var r2 = '<tr class="x-form-daypick-icons">';
49096         for (var i=0; i < 7; i++) {
49097             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49098             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49099         }
49100         
49101         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49102         viewEl.select('img').on('click', this.onClick, this);
49103         this.viewEl = viewEl;   
49104         
49105         
49106         // this will not work on Chrome!!!
49107         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49108         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49109         
49110         
49111           
49112
49113     },
49114
49115     // private
49116     initValue : Roo.emptyFn,
49117
49118     /**
49119      * Returns the checked state of the checkbox.
49120      * @return {Boolean} True if checked, else false
49121      */
49122     getValue : function(){
49123         return this.el.dom.value;
49124         
49125     },
49126
49127         // private
49128     onClick : function(e){ 
49129         //this.setChecked(!this.checked);
49130         Roo.get(e.target).toggleClass('x-menu-item-checked');
49131         this.refreshValue();
49132         //if(this.el.dom.checked != this.checked){
49133         //    this.setValue(this.el.dom.checked);
49134        // }
49135     },
49136     
49137     // private
49138     refreshValue : function()
49139     {
49140         var val = '';
49141         this.viewEl.select('img',true).each(function(e,i,n)  {
49142             val += e.is(".x-menu-item-checked") ? String(n) : '';
49143         });
49144         this.setValue(val, true);
49145     },
49146
49147     /**
49148      * Sets the checked state of the checkbox.
49149      * On is always based on a string comparison between inputValue and the param.
49150      * @param {Boolean/String} value - the value to set 
49151      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49152      */
49153     setValue : function(v,suppressEvent){
49154         if (!this.el.dom) {
49155             return;
49156         }
49157         var old = this.el.dom.value ;
49158         this.el.dom.value = v;
49159         if (suppressEvent) {
49160             return ;
49161         }
49162          
49163         // update display..
49164         this.viewEl.select('img',true).each(function(e,i,n)  {
49165             
49166             var on = e.is(".x-menu-item-checked");
49167             var newv = v.indexOf(String(n)) > -1;
49168             if (on != newv) {
49169                 e.toggleClass('x-menu-item-checked');
49170             }
49171             
49172         });
49173         
49174         
49175         this.fireEvent('change', this, v, old);
49176         
49177         
49178     },
49179    
49180     // handle setting of hidden value by some other method!!?!?
49181     setFromHidden: function()
49182     {
49183         if(!this.el){
49184             return;
49185         }
49186         //console.log("SET FROM HIDDEN");
49187         //alert('setFrom hidden');
49188         this.setValue(this.el.dom.value);
49189     },
49190     
49191     onDestroy : function()
49192     {
49193         if(this.viewEl){
49194             Roo.get(this.viewEl).remove();
49195         }
49196          
49197         Roo.form.DayPicker.superclass.onDestroy.call(this);
49198     }
49199
49200 });/*
49201  * RooJS Library 1.1.1
49202  * Copyright(c) 2008-2011  Alan Knowles
49203  *
49204  * License - LGPL
49205  */
49206  
49207
49208 /**
49209  * @class Roo.form.ComboCheck
49210  * @extends Roo.form.ComboBox
49211  * A combobox for multiple select items.
49212  *
49213  * FIXME - could do with a reset button..
49214  * 
49215  * @constructor
49216  * Create a new ComboCheck
49217  * @param {Object} config Configuration options
49218  */
49219 Roo.form.ComboCheck = function(config){
49220     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49221     // should verify some data...
49222     // like
49223     // hiddenName = required..
49224     // displayField = required
49225     // valudField == required
49226     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49227     var _t = this;
49228     Roo.each(req, function(e) {
49229         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49230             throw "Roo.form.ComboCheck : missing value for: " + e;
49231         }
49232     });
49233     
49234     
49235 };
49236
49237 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49238      
49239      
49240     editable : false,
49241      
49242     selectedClass: 'x-menu-item-checked', 
49243     
49244     // private
49245     onRender : function(ct, position){
49246         var _t = this;
49247         
49248         
49249         
49250         if(!this.tpl){
49251             var cls = 'x-combo-list';
49252
49253             
49254             this.tpl =  new Roo.Template({
49255                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49256                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49257                    '<span>{' + this.displayField + '}</span>' +
49258                     '</div>' 
49259                 
49260             });
49261         }
49262  
49263         
49264         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49265         this.view.singleSelect = false;
49266         this.view.multiSelect = true;
49267         this.view.toggleSelect = true;
49268         this.pageTb.add(new Roo.Toolbar.Fill(), {
49269             
49270             text: 'Done',
49271             handler: function()
49272             {
49273                 _t.collapse();
49274             }
49275         });
49276     },
49277     
49278     onViewOver : function(e, t){
49279         // do nothing...
49280         return;
49281         
49282     },
49283     
49284     onViewClick : function(doFocus,index){
49285         return;
49286         
49287     },
49288     select: function () {
49289         //Roo.log("SELECT CALLED");
49290     },
49291      
49292     selectByValue : function(xv, scrollIntoView){
49293         var ar = this.getValueArray();
49294         var sels = [];
49295         
49296         Roo.each(ar, function(v) {
49297             if(v === undefined || v === null){
49298                 return;
49299             }
49300             var r = this.findRecord(this.valueField, v);
49301             if(r){
49302                 sels.push(this.store.indexOf(r))
49303                 
49304             }
49305         },this);
49306         this.view.select(sels);
49307         return false;
49308     },
49309     
49310     
49311     
49312     onSelect : function(record, index){
49313        // Roo.log("onselect Called");
49314        // this is only called by the clear button now..
49315         this.view.clearSelections();
49316         this.setValue('[]');
49317         if (this.value != this.valueBefore) {
49318             this.fireEvent('change', this, this.value, this.valueBefore);
49319             this.valueBefore = this.value;
49320         }
49321     },
49322     getValueArray : function()
49323     {
49324         var ar = [] ;
49325         
49326         try {
49327             //Roo.log(this.value);
49328             if (typeof(this.value) == 'undefined') {
49329                 return [];
49330             }
49331             var ar = Roo.decode(this.value);
49332             return  ar instanceof Array ? ar : []; //?? valid?
49333             
49334         } catch(e) {
49335             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49336             return [];
49337         }
49338          
49339     },
49340     expand : function ()
49341     {
49342         
49343         Roo.form.ComboCheck.superclass.expand.call(this);
49344         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49345         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49346         
49347
49348     },
49349     
49350     collapse : function(){
49351         Roo.form.ComboCheck.superclass.collapse.call(this);
49352         var sl = this.view.getSelectedIndexes();
49353         var st = this.store;
49354         var nv = [];
49355         var tv = [];
49356         var r;
49357         Roo.each(sl, function(i) {
49358             r = st.getAt(i);
49359             nv.push(r.get(this.valueField));
49360         },this);
49361         this.setValue(Roo.encode(nv));
49362         if (this.value != this.valueBefore) {
49363
49364             this.fireEvent('change', this, this.value, this.valueBefore);
49365             this.valueBefore = this.value;
49366         }
49367         
49368     },
49369     
49370     setValue : function(v){
49371         // Roo.log(v);
49372         this.value = v;
49373         
49374         var vals = this.getValueArray();
49375         var tv = [];
49376         Roo.each(vals, function(k) {
49377             var r = this.findRecord(this.valueField, k);
49378             if(r){
49379                 tv.push(r.data[this.displayField]);
49380             }else if(this.valueNotFoundText !== undefined){
49381                 tv.push( this.valueNotFoundText );
49382             }
49383         },this);
49384        // Roo.log(tv);
49385         
49386         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49387         this.hiddenField.value = v;
49388         this.value = v;
49389     }
49390     
49391 });/*
49392  * Based on:
49393  * Ext JS Library 1.1.1
49394  * Copyright(c) 2006-2007, Ext JS, LLC.
49395  *
49396  * Originally Released Under LGPL - original licence link has changed is not relivant.
49397  *
49398  * Fork - LGPL
49399  * <script type="text/javascript">
49400  */
49401  
49402 /**
49403  * @class Roo.form.Signature
49404  * @extends Roo.form.Field
49405  * Signature field.  
49406  * @constructor
49407  * 
49408  * @param {Object} config Configuration options
49409  */
49410
49411 Roo.form.Signature = function(config){
49412     Roo.form.Signature.superclass.constructor.call(this, config);
49413     
49414     this.addEvents({// not in used??
49415          /**
49416          * @event confirm
49417          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49418              * @param {Roo.form.Signature} combo This combo box
49419              */
49420         'confirm' : true,
49421         /**
49422          * @event reset
49423          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49424              * @param {Roo.form.ComboBox} combo This combo box
49425              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49426              */
49427         'reset' : true
49428     });
49429 };
49430
49431 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49432     /**
49433      * @cfg {Object} labels Label to use when rendering a form.
49434      * defaults to 
49435      * labels : { 
49436      *      clear : "Clear",
49437      *      confirm : "Confirm"
49438      *  }
49439      */
49440     labels : { 
49441         clear : "Clear",
49442         confirm : "Confirm"
49443     },
49444     /**
49445      * @cfg {Number} width The signature panel width (defaults to 300)
49446      */
49447     width: 300,
49448     /**
49449      * @cfg {Number} height The signature panel height (defaults to 100)
49450      */
49451     height : 100,
49452     /**
49453      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49454      */
49455     allowBlank : false,
49456     
49457     //private
49458     // {Object} signPanel The signature SVG panel element (defaults to {})
49459     signPanel : {},
49460     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49461     isMouseDown : false,
49462     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49463     isConfirmed : false,
49464     // {String} signatureTmp SVG mapping string (defaults to empty string)
49465     signatureTmp : '',
49466     
49467     
49468     defaultAutoCreate : { // modified by initCompnoent..
49469         tag: "input",
49470         type:"hidden"
49471     },
49472
49473     // private
49474     onRender : function(ct, position){
49475         
49476         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49477         
49478         this.wrap = this.el.wrap({
49479             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49480         });
49481         
49482         this.createToolbar(this);
49483         this.signPanel = this.wrap.createChild({
49484                 tag: 'div',
49485                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49486             }, this.el
49487         );
49488             
49489         this.svgID = Roo.id();
49490         this.svgEl = this.signPanel.createChild({
49491               xmlns : 'http://www.w3.org/2000/svg',
49492               tag : 'svg',
49493               id : this.svgID + "-svg",
49494               width: this.width,
49495               height: this.height,
49496               viewBox: '0 0 '+this.width+' '+this.height,
49497               cn : [
49498                 {
49499                     tag: "rect",
49500                     id: this.svgID + "-svg-r",
49501                     width: this.width,
49502                     height: this.height,
49503                     fill: "#ffa"
49504                 },
49505                 {
49506                     tag: "line",
49507                     id: this.svgID + "-svg-l",
49508                     x1: "0", // start
49509                     y1: (this.height*0.8), // start set the line in 80% of height
49510                     x2: this.width, // end
49511                     y2: (this.height*0.8), // end set the line in 80% of height
49512                     'stroke': "#666",
49513                     'stroke-width': "1",
49514                     'stroke-dasharray': "3",
49515                     'shape-rendering': "crispEdges",
49516                     'pointer-events': "none"
49517                 },
49518                 {
49519                     tag: "path",
49520                     id: this.svgID + "-svg-p",
49521                     'stroke': "navy",
49522                     'stroke-width': "3",
49523                     'fill': "none",
49524                     'pointer-events': 'none'
49525                 }
49526               ]
49527         });
49528         this.createSVG();
49529         this.svgBox = this.svgEl.dom.getScreenCTM();
49530     },
49531     createSVG : function(){ 
49532         var svg = this.signPanel;
49533         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49534         var t = this;
49535
49536         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49537         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49538         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49539         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49540         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49541         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49542         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49543         
49544     },
49545     isTouchEvent : function(e){
49546         return e.type.match(/^touch/);
49547     },
49548     getCoords : function (e) {
49549         var pt    = this.svgEl.dom.createSVGPoint();
49550         pt.x = e.clientX; 
49551         pt.y = e.clientY;
49552         if (this.isTouchEvent(e)) {
49553             pt.x =  e.targetTouches[0].clientX;
49554             pt.y = e.targetTouches[0].clientY;
49555         }
49556         var a = this.svgEl.dom.getScreenCTM();
49557         var b = a.inverse();
49558         var mx = pt.matrixTransform(b);
49559         return mx.x + ',' + mx.y;
49560     },
49561     //mouse event headler 
49562     down : function (e) {
49563         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49564         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49565         
49566         this.isMouseDown = true;
49567         
49568         e.preventDefault();
49569     },
49570     move : function (e) {
49571         if (this.isMouseDown) {
49572             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49573             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49574         }
49575         
49576         e.preventDefault();
49577     },
49578     up : function (e) {
49579         this.isMouseDown = false;
49580         var sp = this.signatureTmp.split(' ');
49581         
49582         if(sp.length > 1){
49583             if(!sp[sp.length-2].match(/^L/)){
49584                 sp.pop();
49585                 sp.pop();
49586                 sp.push("");
49587                 this.signatureTmp = sp.join(" ");
49588             }
49589         }
49590         if(this.getValue() != this.signatureTmp){
49591             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49592             this.isConfirmed = false;
49593         }
49594         e.preventDefault();
49595     },
49596     
49597     /**
49598      * Protected method that will not generally be called directly. It
49599      * is called when the editor creates its toolbar. Override this method if you need to
49600      * add custom toolbar buttons.
49601      * @param {HtmlEditor} editor
49602      */
49603     createToolbar : function(editor){
49604          function btn(id, toggle, handler){
49605             var xid = fid + '-'+ id ;
49606             return {
49607                 id : xid,
49608                 cmd : id,
49609                 cls : 'x-btn-icon x-edit-'+id,
49610                 enableToggle:toggle !== false,
49611                 scope: editor, // was editor...
49612                 handler:handler||editor.relayBtnCmd,
49613                 clickEvent:'mousedown',
49614                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49615                 tabIndex:-1
49616             };
49617         }
49618         
49619         
49620         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49621         this.tb = tb;
49622         this.tb.add(
49623            {
49624                 cls : ' x-signature-btn x-signature-'+id,
49625                 scope: editor, // was editor...
49626                 handler: this.reset,
49627                 clickEvent:'mousedown',
49628                 text: this.labels.clear
49629             },
49630             {
49631                  xtype : 'Fill',
49632                  xns: Roo.Toolbar
49633             }, 
49634             {
49635                 cls : '  x-signature-btn x-signature-'+id,
49636                 scope: editor, // was editor...
49637                 handler: this.confirmHandler,
49638                 clickEvent:'mousedown',
49639                 text: this.labels.confirm
49640             }
49641         );
49642     
49643     },
49644     //public
49645     /**
49646      * when user is clicked confirm then show this image.....
49647      * 
49648      * @return {String} Image Data URI
49649      */
49650     getImageDataURI : function(){
49651         var svg = this.svgEl.dom.parentNode.innerHTML;
49652         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49653         return src; 
49654     },
49655     /**
49656      * 
49657      * @return {Boolean} this.isConfirmed
49658      */
49659     getConfirmed : function(){
49660         return this.isConfirmed;
49661     },
49662     /**
49663      * 
49664      * @return {Number} this.width
49665      */
49666     getWidth : function(){
49667         return this.width;
49668     },
49669     /**
49670      * 
49671      * @return {Number} this.height
49672      */
49673     getHeight : function(){
49674         return this.height;
49675     },
49676     // private
49677     getSignature : function(){
49678         return this.signatureTmp;
49679     },
49680     // private
49681     reset : function(){
49682         this.signatureTmp = '';
49683         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49684         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49685         this.isConfirmed = false;
49686         Roo.form.Signature.superclass.reset.call(this);
49687     },
49688     setSignature : function(s){
49689         this.signatureTmp = s;
49690         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49691         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49692         this.setValue(s);
49693         this.isConfirmed = false;
49694         Roo.form.Signature.superclass.reset.call(this);
49695     }, 
49696     test : function(){
49697 //        Roo.log(this.signPanel.dom.contentWindow.up())
49698     },
49699     //private
49700     setConfirmed : function(){
49701         
49702         
49703         
49704 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49705     },
49706     // private
49707     confirmHandler : function(){
49708         if(!this.getSignature()){
49709             return;
49710         }
49711         
49712         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49713         this.setValue(this.getSignature());
49714         this.isConfirmed = true;
49715         
49716         this.fireEvent('confirm', this);
49717     },
49718     // private
49719     // Subclasses should provide the validation implementation by overriding this
49720     validateValue : function(value){
49721         if(this.allowBlank){
49722             return true;
49723         }
49724         
49725         if(this.isConfirmed){
49726             return true;
49727         }
49728         return false;
49729     }
49730 });/*
49731  * Based on:
49732  * Ext JS Library 1.1.1
49733  * Copyright(c) 2006-2007, Ext JS, LLC.
49734  *
49735  * Originally Released Under LGPL - original licence link has changed is not relivant.
49736  *
49737  * Fork - LGPL
49738  * <script type="text/javascript">
49739  */
49740  
49741
49742 /**
49743  * @class Roo.form.ComboBox
49744  * @extends Roo.form.TriggerField
49745  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49746  * @constructor
49747  * Create a new ComboBox.
49748  * @param {Object} config Configuration options
49749  */
49750 Roo.form.Select = function(config){
49751     Roo.form.Select.superclass.constructor.call(this, config);
49752      
49753 };
49754
49755 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49756     /**
49757      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49758      */
49759     /**
49760      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49761      * rendering into an Roo.Editor, defaults to false)
49762      */
49763     /**
49764      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49765      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49766      */
49767     /**
49768      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49769      */
49770     /**
49771      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49772      * the dropdown list (defaults to undefined, with no header element)
49773      */
49774
49775      /**
49776      * @cfg {String/Roo.Template} tpl The template to use to render the output
49777      */
49778      
49779     // private
49780     defaultAutoCreate : {tag: "select"  },
49781     /**
49782      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49783      */
49784     listWidth: undefined,
49785     /**
49786      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49787      * mode = 'remote' or 'text' if mode = 'local')
49788      */
49789     displayField: undefined,
49790     /**
49791      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49792      * mode = 'remote' or 'value' if mode = 'local'). 
49793      * Note: use of a valueField requires the user make a selection
49794      * in order for a value to be mapped.
49795      */
49796     valueField: undefined,
49797     
49798     
49799     /**
49800      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49801      * field's data value (defaults to the underlying DOM element's name)
49802      */
49803     hiddenName: undefined,
49804     /**
49805      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49806      */
49807     listClass: '',
49808     /**
49809      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49810      */
49811     selectedClass: 'x-combo-selected',
49812     /**
49813      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49814      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49815      * which displays a downward arrow icon).
49816      */
49817     triggerClass : 'x-form-arrow-trigger',
49818     /**
49819      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49820      */
49821     shadow:'sides',
49822     /**
49823      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49824      * anchor positions (defaults to 'tl-bl')
49825      */
49826     listAlign: 'tl-bl?',
49827     /**
49828      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49829      */
49830     maxHeight: 300,
49831     /**
49832      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49833      * query specified by the allQuery config option (defaults to 'query')
49834      */
49835     triggerAction: 'query',
49836     /**
49837      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49838      * (defaults to 4, does not apply if editable = false)
49839      */
49840     minChars : 4,
49841     /**
49842      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49843      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49844      */
49845     typeAhead: false,
49846     /**
49847      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49848      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49849      */
49850     queryDelay: 500,
49851     /**
49852      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49853      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49854      */
49855     pageSize: 0,
49856     /**
49857      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49858      * when editable = true (defaults to false)
49859      */
49860     selectOnFocus:false,
49861     /**
49862      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49863      */
49864     queryParam: 'query',
49865     /**
49866      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49867      * when mode = 'remote' (defaults to 'Loading...')
49868      */
49869     loadingText: 'Loading...',
49870     /**
49871      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49872      */
49873     resizable: false,
49874     /**
49875      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49876      */
49877     handleHeight : 8,
49878     /**
49879      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49880      * traditional select (defaults to true)
49881      */
49882     editable: true,
49883     /**
49884      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49885      */
49886     allQuery: '',
49887     /**
49888      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49889      */
49890     mode: 'remote',
49891     /**
49892      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49893      * listWidth has a higher value)
49894      */
49895     minListWidth : 70,
49896     /**
49897      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49898      * allow the user to set arbitrary text into the field (defaults to false)
49899      */
49900     forceSelection:false,
49901     /**
49902      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49903      * if typeAhead = true (defaults to 250)
49904      */
49905     typeAheadDelay : 250,
49906     /**
49907      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49908      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49909      */
49910     valueNotFoundText : undefined,
49911     
49912     /**
49913      * @cfg {String} defaultValue The value displayed after loading the store.
49914      */
49915     defaultValue: '',
49916     
49917     /**
49918      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49919      */
49920     blockFocus : false,
49921     
49922     /**
49923      * @cfg {Boolean} disableClear Disable showing of clear button.
49924      */
49925     disableClear : false,
49926     /**
49927      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49928      */
49929     alwaysQuery : false,
49930     
49931     //private
49932     addicon : false,
49933     editicon: false,
49934     
49935     // element that contains real text value.. (when hidden is used..)
49936      
49937     // private
49938     onRender : function(ct, position){
49939         Roo.form.Field.prototype.onRender.call(this, ct, position);
49940         
49941         if(this.store){
49942             this.store.on('beforeload', this.onBeforeLoad, this);
49943             this.store.on('load', this.onLoad, this);
49944             this.store.on('loadexception', this.onLoadException, this);
49945             this.store.load({});
49946         }
49947         
49948         
49949         
49950     },
49951
49952     // private
49953     initEvents : function(){
49954         //Roo.form.ComboBox.superclass.initEvents.call(this);
49955  
49956     },
49957
49958     onDestroy : function(){
49959        
49960         if(this.store){
49961             this.store.un('beforeload', this.onBeforeLoad, this);
49962             this.store.un('load', this.onLoad, this);
49963             this.store.un('loadexception', this.onLoadException, this);
49964         }
49965         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49966     },
49967
49968     // private
49969     fireKey : function(e){
49970         if(e.isNavKeyPress() && !this.list.isVisible()){
49971             this.fireEvent("specialkey", this, e);
49972         }
49973     },
49974
49975     // private
49976     onResize: function(w, h){
49977         
49978         return; 
49979     
49980         
49981     },
49982
49983     /**
49984      * Allow or prevent the user from directly editing the field text.  If false is passed,
49985      * the user will only be able to select from the items defined in the dropdown list.  This method
49986      * is the runtime equivalent of setting the 'editable' config option at config time.
49987      * @param {Boolean} value True to allow the user to directly edit the field text
49988      */
49989     setEditable : function(value){
49990          
49991     },
49992
49993     // private
49994     onBeforeLoad : function(){
49995         
49996         Roo.log("Select before load");
49997         return;
49998     
49999         this.innerList.update(this.loadingText ?
50000                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50001         //this.restrictHeight();
50002         this.selectedIndex = -1;
50003     },
50004
50005     // private
50006     onLoad : function(){
50007
50008     
50009         var dom = this.el.dom;
50010         dom.innerHTML = '';
50011          var od = dom.ownerDocument;
50012          
50013         if (this.emptyText) {
50014             var op = od.createElement('option');
50015             op.setAttribute('value', '');
50016             op.innerHTML = String.format('{0}', this.emptyText);
50017             dom.appendChild(op);
50018         }
50019         if(this.store.getCount() > 0){
50020            
50021             var vf = this.valueField;
50022             var df = this.displayField;
50023             this.store.data.each(function(r) {
50024                 // which colmsn to use... testing - cdoe / title..
50025                 var op = od.createElement('option');
50026                 op.setAttribute('value', r.data[vf]);
50027                 op.innerHTML = String.format('{0}', r.data[df]);
50028                 dom.appendChild(op);
50029             });
50030             if (typeof(this.defaultValue != 'undefined')) {
50031                 this.setValue(this.defaultValue);
50032             }
50033             
50034              
50035         }else{
50036             //this.onEmptyResults();
50037         }
50038         //this.el.focus();
50039     },
50040     // private
50041     onLoadException : function()
50042     {
50043         dom.innerHTML = '';
50044             
50045         Roo.log("Select on load exception");
50046         return;
50047     
50048         this.collapse();
50049         Roo.log(this.store.reader.jsonData);
50050         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50051             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50052         }
50053         
50054         
50055     },
50056     // private
50057     onTypeAhead : function(){
50058          
50059     },
50060
50061     // private
50062     onSelect : function(record, index){
50063         Roo.log('on select?');
50064         return;
50065         if(this.fireEvent('beforeselect', this, record, index) !== false){
50066             this.setFromData(index > -1 ? record.data : false);
50067             this.collapse();
50068             this.fireEvent('select', this, record, index);
50069         }
50070     },
50071
50072     /**
50073      * Returns the currently selected field value or empty string if no value is set.
50074      * @return {String} value The selected value
50075      */
50076     getValue : function(){
50077         var dom = this.el.dom;
50078         this.value = dom.options[dom.selectedIndex].value;
50079         return this.value;
50080         
50081     },
50082
50083     /**
50084      * Clears any text/value currently set in the field
50085      */
50086     clearValue : function(){
50087         this.value = '';
50088         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50089         
50090     },
50091
50092     /**
50093      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50094      * will be displayed in the field.  If the value does not match the data value of an existing item,
50095      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50096      * Otherwise the field will be blank (although the value will still be set).
50097      * @param {String} value The value to match
50098      */
50099     setValue : function(v){
50100         var d = this.el.dom;
50101         for (var i =0; i < d.options.length;i++) {
50102             if (v == d.options[i].value) {
50103                 d.selectedIndex = i;
50104                 this.value = v;
50105                 return;
50106             }
50107         }
50108         this.clearValue();
50109     },
50110     /**
50111      * @property {Object} the last set data for the element
50112      */
50113     
50114     lastData : false,
50115     /**
50116      * Sets the value of the field based on a object which is related to the record format for the store.
50117      * @param {Object} value the value to set as. or false on reset?
50118      */
50119     setFromData : function(o){
50120         Roo.log('setfrom data?');
50121          
50122         
50123         
50124     },
50125     // private
50126     reset : function(){
50127         this.clearValue();
50128     },
50129     // private
50130     findRecord : function(prop, value){
50131         
50132         return false;
50133     
50134         var record;
50135         if(this.store.getCount() > 0){
50136             this.store.each(function(r){
50137                 if(r.data[prop] == value){
50138                     record = r;
50139                     return false;
50140                 }
50141                 return true;
50142             });
50143         }
50144         return record;
50145     },
50146     
50147     getName: function()
50148     {
50149         // returns hidden if it's set..
50150         if (!this.rendered) {return ''};
50151         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50152         
50153     },
50154      
50155
50156     
50157
50158     // private
50159     onEmptyResults : function(){
50160         Roo.log('empty results');
50161         //this.collapse();
50162     },
50163
50164     /**
50165      * Returns true if the dropdown list is expanded, else false.
50166      */
50167     isExpanded : function(){
50168         return false;
50169     },
50170
50171     /**
50172      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50173      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50174      * @param {String} value The data value of the item to select
50175      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50176      * selected item if it is not currently in view (defaults to true)
50177      * @return {Boolean} True if the value matched an item in the list, else false
50178      */
50179     selectByValue : function(v, scrollIntoView){
50180         Roo.log('select By Value');
50181         return false;
50182     
50183         if(v !== undefined && v !== null){
50184             var r = this.findRecord(this.valueField || this.displayField, v);
50185             if(r){
50186                 this.select(this.store.indexOf(r), scrollIntoView);
50187                 return true;
50188             }
50189         }
50190         return false;
50191     },
50192
50193     /**
50194      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50195      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50196      * @param {Number} index The zero-based index of the list item to select
50197      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50198      * selected item if it is not currently in view (defaults to true)
50199      */
50200     select : function(index, scrollIntoView){
50201         Roo.log('select ');
50202         return  ;
50203         
50204         this.selectedIndex = index;
50205         this.view.select(index);
50206         if(scrollIntoView !== false){
50207             var el = this.view.getNode(index);
50208             if(el){
50209                 this.innerList.scrollChildIntoView(el, false);
50210             }
50211         }
50212     },
50213
50214       
50215
50216     // private
50217     validateBlur : function(){
50218         
50219         return;
50220         
50221     },
50222
50223     // private
50224     initQuery : function(){
50225         this.doQuery(this.getRawValue());
50226     },
50227
50228     // private
50229     doForce : function(){
50230         if(this.el.dom.value.length > 0){
50231             this.el.dom.value =
50232                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50233              
50234         }
50235     },
50236
50237     /**
50238      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50239      * query allowing the query action to be canceled if needed.
50240      * @param {String} query The SQL query to execute
50241      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50242      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50243      * saved in the current store (defaults to false)
50244      */
50245     doQuery : function(q, forceAll){
50246         
50247         Roo.log('doQuery?');
50248         if(q === undefined || q === null){
50249             q = '';
50250         }
50251         var qe = {
50252             query: q,
50253             forceAll: forceAll,
50254             combo: this,
50255             cancel:false
50256         };
50257         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50258             return false;
50259         }
50260         q = qe.query;
50261         forceAll = qe.forceAll;
50262         if(forceAll === true || (q.length >= this.minChars)){
50263             if(this.lastQuery != q || this.alwaysQuery){
50264                 this.lastQuery = q;
50265                 if(this.mode == 'local'){
50266                     this.selectedIndex = -1;
50267                     if(forceAll){
50268                         this.store.clearFilter();
50269                     }else{
50270                         this.store.filter(this.displayField, q);
50271                     }
50272                     this.onLoad();
50273                 }else{
50274                     this.store.baseParams[this.queryParam] = q;
50275                     this.store.load({
50276                         params: this.getParams(q)
50277                     });
50278                     this.expand();
50279                 }
50280             }else{
50281                 this.selectedIndex = -1;
50282                 this.onLoad();   
50283             }
50284         }
50285     },
50286
50287     // private
50288     getParams : function(q){
50289         var p = {};
50290         //p[this.queryParam] = q;
50291         if(this.pageSize){
50292             p.start = 0;
50293             p.limit = this.pageSize;
50294         }
50295         return p;
50296     },
50297
50298     /**
50299      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50300      */
50301     collapse : function(){
50302         
50303     },
50304
50305     // private
50306     collapseIf : function(e){
50307         
50308     },
50309
50310     /**
50311      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50312      */
50313     expand : function(){
50314         
50315     } ,
50316
50317     // private
50318      
50319
50320     /** 
50321     * @cfg {Boolean} grow 
50322     * @hide 
50323     */
50324     /** 
50325     * @cfg {Number} growMin 
50326     * @hide 
50327     */
50328     /** 
50329     * @cfg {Number} growMax 
50330     * @hide 
50331     */
50332     /**
50333      * @hide
50334      * @method autoSize
50335      */
50336     
50337     setWidth : function()
50338     {
50339         
50340     },
50341     getResizeEl : function(){
50342         return this.el;
50343     }
50344 });//<script type="text/javasscript">
50345  
50346
50347 /**
50348  * @class Roo.DDView
50349  * A DnD enabled version of Roo.View.
50350  * @param {Element/String} container The Element in which to create the View.
50351  * @param {String} tpl The template string used to create the markup for each element of the View
50352  * @param {Object} config The configuration properties. These include all the config options of
50353  * {@link Roo.View} plus some specific to this class.<br>
50354  * <p>
50355  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50356  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50357  * <p>
50358  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50359 .x-view-drag-insert-above {
50360         border-top:1px dotted #3366cc;
50361 }
50362 .x-view-drag-insert-below {
50363         border-bottom:1px dotted #3366cc;
50364 }
50365 </code></pre>
50366  * 
50367  */
50368  
50369 Roo.DDView = function(container, tpl, config) {
50370     Roo.DDView.superclass.constructor.apply(this, arguments);
50371     this.getEl().setStyle("outline", "0px none");
50372     this.getEl().unselectable();
50373     if (this.dragGroup) {
50374                 this.setDraggable(this.dragGroup.split(","));
50375     }
50376     if (this.dropGroup) {
50377                 this.setDroppable(this.dropGroup.split(","));
50378     }
50379     if (this.deletable) {
50380         this.setDeletable();
50381     }
50382     this.isDirtyFlag = false;
50383         this.addEvents({
50384                 "drop" : true
50385         });
50386 };
50387
50388 Roo.extend(Roo.DDView, Roo.View, {
50389 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50390 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50391 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50392 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50393
50394         isFormField: true,
50395
50396         reset: Roo.emptyFn,
50397         
50398         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50399
50400         validate: function() {
50401                 return true;
50402         },
50403         
50404         destroy: function() {
50405                 this.purgeListeners();
50406                 this.getEl.removeAllListeners();
50407                 this.getEl().remove();
50408                 if (this.dragZone) {
50409                         if (this.dragZone.destroy) {
50410                                 this.dragZone.destroy();
50411                         }
50412                 }
50413                 if (this.dropZone) {
50414                         if (this.dropZone.destroy) {
50415                                 this.dropZone.destroy();
50416                         }
50417                 }
50418         },
50419
50420 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50421         getName: function() {
50422                 return this.name;
50423         },
50424
50425 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50426         setValue: function(v) {
50427                 if (!this.store) {
50428                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50429                 }
50430                 var data = {};
50431                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50432                 this.store.proxy = new Roo.data.MemoryProxy(data);
50433                 this.store.load();
50434         },
50435
50436 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50437         getValue: function() {
50438                 var result = '(';
50439                 this.store.each(function(rec) {
50440                         result += rec.id + ',';
50441                 });
50442                 return result.substr(0, result.length - 1) + ')';
50443         },
50444         
50445         getIds: function() {
50446                 var i = 0, result = new Array(this.store.getCount());
50447                 this.store.each(function(rec) {
50448                         result[i++] = rec.id;
50449                 });
50450                 return result;
50451         },
50452         
50453         isDirty: function() {
50454                 return this.isDirtyFlag;
50455         },
50456
50457 /**
50458  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50459  *      whole Element becomes the target, and this causes the drop gesture to append.
50460  */
50461     getTargetFromEvent : function(e) {
50462                 var target = e.getTarget();
50463                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50464                 target = target.parentNode;
50465                 }
50466                 if (!target) {
50467                         target = this.el.dom.lastChild || this.el.dom;
50468                 }
50469                 return target;
50470     },
50471
50472 /**
50473  *      Create the drag data which consists of an object which has the property "ddel" as
50474  *      the drag proxy element. 
50475  */
50476     getDragData : function(e) {
50477         var target = this.findItemFromChild(e.getTarget());
50478                 if(target) {
50479                         this.handleSelection(e);
50480                         var selNodes = this.getSelectedNodes();
50481             var dragData = {
50482                 source: this,
50483                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50484                 nodes: selNodes,
50485                 records: []
50486                         };
50487                         var selectedIndices = this.getSelectedIndexes();
50488                         for (var i = 0; i < selectedIndices.length; i++) {
50489                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50490                         }
50491                         if (selNodes.length == 1) {
50492                                 dragData.ddel = target.cloneNode(true); // the div element
50493                         } else {
50494                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50495                                 div.className = 'multi-proxy';
50496                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50497                                         div.appendChild(selNodes[i].cloneNode(true));
50498                                 }
50499                                 dragData.ddel = div;
50500                         }
50501             //console.log(dragData)
50502             //console.log(dragData.ddel.innerHTML)
50503                         return dragData;
50504                 }
50505         //console.log('nodragData')
50506                 return false;
50507     },
50508     
50509 /**     Specify to which ddGroup items in this DDView may be dragged. */
50510     setDraggable: function(ddGroup) {
50511         if (ddGroup instanceof Array) {
50512                 Roo.each(ddGroup, this.setDraggable, this);
50513                 return;
50514         }
50515         if (this.dragZone) {
50516                 this.dragZone.addToGroup(ddGroup);
50517         } else {
50518                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50519                                 containerScroll: true,
50520                                 ddGroup: ddGroup 
50521
50522                         });
50523 //                      Draggability implies selection. DragZone's mousedown selects the element.
50524                         if (!this.multiSelect) { this.singleSelect = true; }
50525
50526 //                      Wire the DragZone's handlers up to methods in *this*
50527                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50528                 }
50529     },
50530
50531 /**     Specify from which ddGroup this DDView accepts drops. */
50532     setDroppable: function(ddGroup) {
50533         if (ddGroup instanceof Array) {
50534                 Roo.each(ddGroup, this.setDroppable, this);
50535                 return;
50536         }
50537         if (this.dropZone) {
50538                 this.dropZone.addToGroup(ddGroup);
50539         } else {
50540                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50541                                 containerScroll: true,
50542                                 ddGroup: ddGroup
50543                         });
50544
50545 //                      Wire the DropZone's handlers up to methods in *this*
50546                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50547                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50548                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50549                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50550                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50551                 }
50552     },
50553
50554 /**     Decide whether to drop above or below a View node. */
50555     getDropPoint : function(e, n, dd){
50556         if (n == this.el.dom) { return "above"; }
50557                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50558                 var c = t + (b - t) / 2;
50559                 var y = Roo.lib.Event.getPageY(e);
50560                 if(y <= c) {
50561                         return "above";
50562                 }else{
50563                         return "below";
50564                 }
50565     },
50566
50567     onNodeEnter : function(n, dd, e, data){
50568                 return false;
50569     },
50570     
50571     onNodeOver : function(n, dd, e, data){
50572                 var pt = this.getDropPoint(e, n, dd);
50573                 // set the insert point style on the target node
50574                 var dragElClass = this.dropNotAllowed;
50575                 if (pt) {
50576                         var targetElClass;
50577                         if (pt == "above"){
50578                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50579                                 targetElClass = "x-view-drag-insert-above";
50580                         } else {
50581                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50582                                 targetElClass = "x-view-drag-insert-below";
50583                         }
50584                         if (this.lastInsertClass != targetElClass){
50585                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50586                                 this.lastInsertClass = targetElClass;
50587                         }
50588                 }
50589                 return dragElClass;
50590         },
50591
50592     onNodeOut : function(n, dd, e, data){
50593                 this.removeDropIndicators(n);
50594     },
50595
50596     onNodeDrop : function(n, dd, e, data){
50597         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50598                 return false;
50599         }
50600         var pt = this.getDropPoint(e, n, dd);
50601                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50602                 if (pt == "below") { insertAt++; }
50603                 for (var i = 0; i < data.records.length; i++) {
50604                         var r = data.records[i];
50605                         var dup = this.store.getById(r.id);
50606                         if (dup && (dd != this.dragZone)) {
50607                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50608                         } else {
50609                                 if (data.copy) {
50610                                         this.store.insert(insertAt++, r.copy());
50611                                 } else {
50612                                         data.source.isDirtyFlag = true;
50613                                         r.store.remove(r);
50614                                         this.store.insert(insertAt++, r);
50615                                 }
50616                                 this.isDirtyFlag = true;
50617                         }
50618                 }
50619                 this.dragZone.cachedTarget = null;
50620                 return true;
50621     },
50622
50623     removeDropIndicators : function(n){
50624                 if(n){
50625                         Roo.fly(n).removeClass([
50626                                 "x-view-drag-insert-above",
50627                                 "x-view-drag-insert-below"]);
50628                         this.lastInsertClass = "_noclass";
50629                 }
50630     },
50631
50632 /**
50633  *      Utility method. Add a delete option to the DDView's context menu.
50634  *      @param {String} imageUrl The URL of the "delete" icon image.
50635  */
50636         setDeletable: function(imageUrl) {
50637                 if (!this.singleSelect && !this.multiSelect) {
50638                         this.singleSelect = true;
50639                 }
50640                 var c = this.getContextMenu();
50641                 this.contextMenu.on("itemclick", function(item) {
50642                         switch (item.id) {
50643                                 case "delete":
50644                                         this.remove(this.getSelectedIndexes());
50645                                         break;
50646                         }
50647                 }, this);
50648                 this.contextMenu.add({
50649                         icon: imageUrl,
50650                         id: "delete",
50651                         text: 'Delete'
50652                 });
50653         },
50654         
50655 /**     Return the context menu for this DDView. */
50656         getContextMenu: function() {
50657                 if (!this.contextMenu) {
50658 //                      Create the View's context menu
50659                         this.contextMenu = new Roo.menu.Menu({
50660                                 id: this.id + "-contextmenu"
50661                         });
50662                         this.el.on("contextmenu", this.showContextMenu, this);
50663                 }
50664                 return this.contextMenu;
50665         },
50666         
50667         disableContextMenu: function() {
50668                 if (this.contextMenu) {
50669                         this.el.un("contextmenu", this.showContextMenu, this);
50670                 }
50671         },
50672
50673         showContextMenu: function(e, item) {
50674         item = this.findItemFromChild(e.getTarget());
50675                 if (item) {
50676                         e.stopEvent();
50677                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50678                         this.contextMenu.showAt(e.getXY());
50679             }
50680     },
50681
50682 /**
50683  *      Remove {@link Roo.data.Record}s at the specified indices.
50684  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50685  */
50686     remove: function(selectedIndices) {
50687                 selectedIndices = [].concat(selectedIndices);
50688                 for (var i = 0; i < selectedIndices.length; i++) {
50689                         var rec = this.store.getAt(selectedIndices[i]);
50690                         this.store.remove(rec);
50691                 }
50692     },
50693
50694 /**
50695  *      Double click fires the event, but also, if this is draggable, and there is only one other
50696  *      related DropZone, it transfers the selected node.
50697  */
50698     onDblClick : function(e){
50699         var item = this.findItemFromChild(e.getTarget());
50700         if(item){
50701             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50702                 return false;
50703             }
50704             if (this.dragGroup) {
50705                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50706                     while (targets.indexOf(this.dropZone) > -1) {
50707                             targets.remove(this.dropZone);
50708                                 }
50709                     if (targets.length == 1) {
50710                                         this.dragZone.cachedTarget = null;
50711                         var el = Roo.get(targets[0].getEl());
50712                         var box = el.getBox(true);
50713                         targets[0].onNodeDrop(el.dom, {
50714                                 target: el.dom,
50715                                 xy: [box.x, box.y + box.height - 1]
50716                         }, null, this.getDragData(e));
50717                     }
50718                 }
50719         }
50720     },
50721     
50722     handleSelection: function(e) {
50723                 this.dragZone.cachedTarget = null;
50724         var item = this.findItemFromChild(e.getTarget());
50725         if (!item) {
50726                 this.clearSelections(true);
50727                 return;
50728         }
50729                 if (item && (this.multiSelect || this.singleSelect)){
50730                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50731                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50732                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50733                                 this.unselect(item);
50734                         } else {
50735                                 this.select(item, this.multiSelect && e.ctrlKey);
50736                                 this.lastSelection = item;
50737                         }
50738                 }
50739     },
50740
50741     onItemClick : function(item, index, e){
50742                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50743                         return false;
50744                 }
50745                 return true;
50746     },
50747
50748     unselect : function(nodeInfo, suppressEvent){
50749                 var node = this.getNode(nodeInfo);
50750                 if(node && this.isSelected(node)){
50751                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50752                                 Roo.fly(node).removeClass(this.selectedClass);
50753                                 this.selections.remove(node);
50754                                 if(!suppressEvent){
50755                                         this.fireEvent("selectionchange", this, this.selections);
50756                                 }
50757                         }
50758                 }
50759     }
50760 });
50761 /*
50762  * Based on:
50763  * Ext JS Library 1.1.1
50764  * Copyright(c) 2006-2007, Ext JS, LLC.
50765  *
50766  * Originally Released Under LGPL - original licence link has changed is not relivant.
50767  *
50768  * Fork - LGPL
50769  * <script type="text/javascript">
50770  */
50771  
50772 /**
50773  * @class Roo.LayoutManager
50774  * @extends Roo.util.Observable
50775  * Base class for layout managers.
50776  */
50777 Roo.LayoutManager = function(container, config){
50778     Roo.LayoutManager.superclass.constructor.call(this);
50779     this.el = Roo.get(container);
50780     // ie scrollbar fix
50781     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50782         document.body.scroll = "no";
50783     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50784         this.el.position('relative');
50785     }
50786     this.id = this.el.id;
50787     this.el.addClass("x-layout-container");
50788     /** false to disable window resize monitoring @type Boolean */
50789     this.monitorWindowResize = true;
50790     this.regions = {};
50791     this.addEvents({
50792         /**
50793          * @event layout
50794          * Fires when a layout is performed. 
50795          * @param {Roo.LayoutManager} this
50796          */
50797         "layout" : true,
50798         /**
50799          * @event regionresized
50800          * Fires when the user resizes a region. 
50801          * @param {Roo.LayoutRegion} region The resized region
50802          * @param {Number} newSize The new size (width for east/west, height for north/south)
50803          */
50804         "regionresized" : true,
50805         /**
50806          * @event regioncollapsed
50807          * Fires when a region is collapsed. 
50808          * @param {Roo.LayoutRegion} region The collapsed region
50809          */
50810         "regioncollapsed" : true,
50811         /**
50812          * @event regionexpanded
50813          * Fires when a region is expanded.  
50814          * @param {Roo.LayoutRegion} region The expanded region
50815          */
50816         "regionexpanded" : true
50817     });
50818     this.updating = false;
50819     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50820 };
50821
50822 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50823     /**
50824      * Returns true if this layout is currently being updated
50825      * @return {Boolean}
50826      */
50827     isUpdating : function(){
50828         return this.updating; 
50829     },
50830     
50831     /**
50832      * Suspend the LayoutManager from doing auto-layouts while
50833      * making multiple add or remove calls
50834      */
50835     beginUpdate : function(){
50836         this.updating = true;    
50837     },
50838     
50839     /**
50840      * Restore auto-layouts and optionally disable the manager from performing a layout
50841      * @param {Boolean} noLayout true to disable a layout update 
50842      */
50843     endUpdate : function(noLayout){
50844         this.updating = false;
50845         if(!noLayout){
50846             this.layout();
50847         }    
50848     },
50849     
50850     layout: function(){
50851         
50852     },
50853     
50854     onRegionResized : function(region, newSize){
50855         this.fireEvent("regionresized", region, newSize);
50856         this.layout();
50857     },
50858     
50859     onRegionCollapsed : function(region){
50860         this.fireEvent("regioncollapsed", region);
50861     },
50862     
50863     onRegionExpanded : function(region){
50864         this.fireEvent("regionexpanded", region);
50865     },
50866         
50867     /**
50868      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50869      * performs box-model adjustments.
50870      * @return {Object} The size as an object {width: (the width), height: (the height)}
50871      */
50872     getViewSize : function(){
50873         var size;
50874         if(this.el.dom != document.body){
50875             size = this.el.getSize();
50876         }else{
50877             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50878         }
50879         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50880         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50881         return size;
50882     },
50883     
50884     /**
50885      * Returns the Element this layout is bound to.
50886      * @return {Roo.Element}
50887      */
50888     getEl : function(){
50889         return this.el;
50890     },
50891     
50892     /**
50893      * Returns the specified region.
50894      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50895      * @return {Roo.LayoutRegion}
50896      */
50897     getRegion : function(target){
50898         return this.regions[target.toLowerCase()];
50899     },
50900     
50901     onWindowResize : function(){
50902         if(this.monitorWindowResize){
50903             this.layout();
50904         }
50905     }
50906 });/*
50907  * Based on:
50908  * Ext JS Library 1.1.1
50909  * Copyright(c) 2006-2007, Ext JS, LLC.
50910  *
50911  * Originally Released Under LGPL - original licence link has changed is not relivant.
50912  *
50913  * Fork - LGPL
50914  * <script type="text/javascript">
50915  */
50916 /**
50917  * @class Roo.BorderLayout
50918  * @extends Roo.LayoutManager
50919  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50920  * please see: <br><br>
50921  * <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>
50922  * <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>
50923  * Example:
50924  <pre><code>
50925  var layout = new Roo.BorderLayout(document.body, {
50926     north: {
50927         initialSize: 25,
50928         titlebar: false
50929     },
50930     west: {
50931         split:true,
50932         initialSize: 200,
50933         minSize: 175,
50934         maxSize: 400,
50935         titlebar: true,
50936         collapsible: true
50937     },
50938     east: {
50939         split:true,
50940         initialSize: 202,
50941         minSize: 175,
50942         maxSize: 400,
50943         titlebar: true,
50944         collapsible: true
50945     },
50946     south: {
50947         split:true,
50948         initialSize: 100,
50949         minSize: 100,
50950         maxSize: 200,
50951         titlebar: true,
50952         collapsible: true
50953     },
50954     center: {
50955         titlebar: true,
50956         autoScroll:true,
50957         resizeTabs: true,
50958         minTabWidth: 50,
50959         preferredTabWidth: 150
50960     }
50961 });
50962
50963 // shorthand
50964 var CP = Roo.ContentPanel;
50965
50966 layout.beginUpdate();
50967 layout.add("north", new CP("north", "North"));
50968 layout.add("south", new CP("south", {title: "South", closable: true}));
50969 layout.add("west", new CP("west", {title: "West"}));
50970 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50971 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50972 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50973 layout.getRegion("center").showPanel("center1");
50974 layout.endUpdate();
50975 </code></pre>
50976
50977 <b>The container the layout is rendered into can be either the body element or any other element.
50978 If it is not the body element, the container needs to either be an absolute positioned element,
50979 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50980 the container size if it is not the body element.</b>
50981
50982 * @constructor
50983 * Create a new BorderLayout
50984 * @param {String/HTMLElement/Element} container The container this layout is bound to
50985 * @param {Object} config Configuration options
50986  */
50987 Roo.BorderLayout = function(container, config){
50988     config = config || {};
50989     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50990     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50991     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50992         var target = this.factory.validRegions[i];
50993         if(config[target]){
50994             this.addRegion(target, config[target]);
50995         }
50996     }
50997 };
50998
50999 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51000     /**
51001      * Creates and adds a new region if it doesn't already exist.
51002      * @param {String} target The target region key (north, south, east, west or center).
51003      * @param {Object} config The regions config object
51004      * @return {BorderLayoutRegion} The new region
51005      */
51006     addRegion : function(target, config){
51007         if(!this.regions[target]){
51008             var r = this.factory.create(target, this, config);
51009             this.bindRegion(target, r);
51010         }
51011         return this.regions[target];
51012     },
51013
51014     // private (kinda)
51015     bindRegion : function(name, r){
51016         this.regions[name] = r;
51017         r.on("visibilitychange", this.layout, this);
51018         r.on("paneladded", this.layout, this);
51019         r.on("panelremoved", this.layout, this);
51020         r.on("invalidated", this.layout, this);
51021         r.on("resized", this.onRegionResized, this);
51022         r.on("collapsed", this.onRegionCollapsed, this);
51023         r.on("expanded", this.onRegionExpanded, this);
51024     },
51025
51026     /**
51027      * Performs a layout update.
51028      */
51029     layout : function(){
51030         if(this.updating) {
51031             return;
51032         }
51033         var size = this.getViewSize();
51034         var w = size.width;
51035         var h = size.height;
51036         var centerW = w;
51037         var centerH = h;
51038         var centerY = 0;
51039         var centerX = 0;
51040         //var x = 0, y = 0;
51041
51042         var rs = this.regions;
51043         var north = rs["north"];
51044         var south = rs["south"]; 
51045         var west = rs["west"];
51046         var east = rs["east"];
51047         var center = rs["center"];
51048         //if(this.hideOnLayout){ // not supported anymore
51049             //c.el.setStyle("display", "none");
51050         //}
51051         if(north && north.isVisible()){
51052             var b = north.getBox();
51053             var m = north.getMargins();
51054             b.width = w - (m.left+m.right);
51055             b.x = m.left;
51056             b.y = m.top;
51057             centerY = b.height + b.y + m.bottom;
51058             centerH -= centerY;
51059             north.updateBox(this.safeBox(b));
51060         }
51061         if(south && south.isVisible()){
51062             var b = south.getBox();
51063             var m = south.getMargins();
51064             b.width = w - (m.left+m.right);
51065             b.x = m.left;
51066             var totalHeight = (b.height + m.top + m.bottom);
51067             b.y = h - totalHeight + m.top;
51068             centerH -= totalHeight;
51069             south.updateBox(this.safeBox(b));
51070         }
51071         if(west && west.isVisible()){
51072             var b = west.getBox();
51073             var m = west.getMargins();
51074             b.height = centerH - (m.top+m.bottom);
51075             b.x = m.left;
51076             b.y = centerY + m.top;
51077             var totalWidth = (b.width + m.left + m.right);
51078             centerX += totalWidth;
51079             centerW -= totalWidth;
51080             west.updateBox(this.safeBox(b));
51081         }
51082         if(east && east.isVisible()){
51083             var b = east.getBox();
51084             var m = east.getMargins();
51085             b.height = centerH - (m.top+m.bottom);
51086             var totalWidth = (b.width + m.left + m.right);
51087             b.x = w - totalWidth + m.left;
51088             b.y = centerY + m.top;
51089             centerW -= totalWidth;
51090             east.updateBox(this.safeBox(b));
51091         }
51092         if(center){
51093             var m = center.getMargins();
51094             var centerBox = {
51095                 x: centerX + m.left,
51096                 y: centerY + m.top,
51097                 width: centerW - (m.left+m.right),
51098                 height: centerH - (m.top+m.bottom)
51099             };
51100             //if(this.hideOnLayout){
51101                 //center.el.setStyle("display", "block");
51102             //}
51103             center.updateBox(this.safeBox(centerBox));
51104         }
51105         this.el.repaint();
51106         this.fireEvent("layout", this);
51107     },
51108
51109     // private
51110     safeBox : function(box){
51111         box.width = Math.max(0, box.width);
51112         box.height = Math.max(0, box.height);
51113         return box;
51114     },
51115
51116     /**
51117      * Adds a ContentPanel (or subclass) to this layout.
51118      * @param {String} target The target region key (north, south, east, west or center).
51119      * @param {Roo.ContentPanel} panel The panel to add
51120      * @return {Roo.ContentPanel} The added panel
51121      */
51122     add : function(target, panel){
51123          
51124         target = target.toLowerCase();
51125         return this.regions[target].add(panel);
51126     },
51127
51128     /**
51129      * Remove a ContentPanel (or subclass) to this layout.
51130      * @param {String} target The target region key (north, south, east, west or center).
51131      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51132      * @return {Roo.ContentPanel} The removed panel
51133      */
51134     remove : function(target, panel){
51135         target = target.toLowerCase();
51136         return this.regions[target].remove(panel);
51137     },
51138
51139     /**
51140      * Searches all regions for a panel with the specified id
51141      * @param {String} panelId
51142      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51143      */
51144     findPanel : function(panelId){
51145         var rs = this.regions;
51146         for(var target in rs){
51147             if(typeof rs[target] != "function"){
51148                 var p = rs[target].getPanel(panelId);
51149                 if(p){
51150                     return p;
51151                 }
51152             }
51153         }
51154         return null;
51155     },
51156
51157     /**
51158      * Searches all regions for a panel with the specified id and activates (shows) it.
51159      * @param {String/ContentPanel} panelId The panels id or the panel itself
51160      * @return {Roo.ContentPanel} The shown panel or null
51161      */
51162     showPanel : function(panelId) {
51163       var rs = this.regions;
51164       for(var target in rs){
51165          var r = rs[target];
51166          if(typeof r != "function"){
51167             if(r.hasPanel(panelId)){
51168                return r.showPanel(panelId);
51169             }
51170          }
51171       }
51172       return null;
51173    },
51174
51175    /**
51176      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51177      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51178      */
51179     restoreState : function(provider){
51180         if(!provider){
51181             provider = Roo.state.Manager;
51182         }
51183         var sm = new Roo.LayoutStateManager();
51184         sm.init(this, provider);
51185     },
51186
51187     /**
51188      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51189      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51190      * a valid ContentPanel config object.  Example:
51191      * <pre><code>
51192 // Create the main layout
51193 var layout = new Roo.BorderLayout('main-ct', {
51194     west: {
51195         split:true,
51196         minSize: 175,
51197         titlebar: true
51198     },
51199     center: {
51200         title:'Components'
51201     }
51202 }, 'main-ct');
51203
51204 // Create and add multiple ContentPanels at once via configs
51205 layout.batchAdd({
51206    west: {
51207        id: 'source-files',
51208        autoCreate:true,
51209        title:'Ext Source Files',
51210        autoScroll:true,
51211        fitToFrame:true
51212    },
51213    center : {
51214        el: cview,
51215        autoScroll:true,
51216        fitToFrame:true,
51217        toolbar: tb,
51218        resizeEl:'cbody'
51219    }
51220 });
51221 </code></pre>
51222      * @param {Object} regions An object containing ContentPanel configs by region name
51223      */
51224     batchAdd : function(regions){
51225         this.beginUpdate();
51226         for(var rname in regions){
51227             var lr = this.regions[rname];
51228             if(lr){
51229                 this.addTypedPanels(lr, regions[rname]);
51230             }
51231         }
51232         this.endUpdate();
51233     },
51234
51235     // private
51236     addTypedPanels : function(lr, ps){
51237         if(typeof ps == 'string'){
51238             lr.add(new Roo.ContentPanel(ps));
51239         }
51240         else if(ps instanceof Array){
51241             for(var i =0, len = ps.length; i < len; i++){
51242                 this.addTypedPanels(lr, ps[i]);
51243             }
51244         }
51245         else if(!ps.events){ // raw config?
51246             var el = ps.el;
51247             delete ps.el; // prevent conflict
51248             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51249         }
51250         else {  // panel object assumed!
51251             lr.add(ps);
51252         }
51253     },
51254     /**
51255      * Adds a xtype elements to the layout.
51256      * <pre><code>
51257
51258 layout.addxtype({
51259        xtype : 'ContentPanel',
51260        region: 'west',
51261        items: [ .... ]
51262    }
51263 );
51264
51265 layout.addxtype({
51266         xtype : 'NestedLayoutPanel',
51267         region: 'west',
51268         layout: {
51269            center: { },
51270            west: { }   
51271         },
51272         items : [ ... list of content panels or nested layout panels.. ]
51273    }
51274 );
51275 </code></pre>
51276      * @param {Object} cfg Xtype definition of item to add.
51277      */
51278     addxtype : function(cfg)
51279     {
51280         // basically accepts a pannel...
51281         // can accept a layout region..!?!?
51282         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51283         
51284         if (!cfg.xtype.match(/Panel$/)) {
51285             return false;
51286         }
51287         var ret = false;
51288         
51289         if (typeof(cfg.region) == 'undefined') {
51290             Roo.log("Failed to add Panel, region was not set");
51291             Roo.log(cfg);
51292             return false;
51293         }
51294         var region = cfg.region;
51295         delete cfg.region;
51296         
51297           
51298         var xitems = [];
51299         if (cfg.items) {
51300             xitems = cfg.items;
51301             delete cfg.items;
51302         }
51303         var nb = false;
51304         
51305         switch(cfg.xtype) 
51306         {
51307             case 'ContentPanel':  // ContentPanel (el, cfg)
51308             case 'ScrollPanel':  // ContentPanel (el, cfg)
51309             case 'ViewPanel': 
51310                 if(cfg.autoCreate) {
51311                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51312                 } else {
51313                     var el = this.el.createChild();
51314                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51315                 }
51316                 
51317                 this.add(region, ret);
51318                 break;
51319             
51320             
51321             case 'TreePanel': // our new panel!
51322                 cfg.el = this.el.createChild();
51323                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51324                 this.add(region, ret);
51325                 break;
51326             
51327             case 'NestedLayoutPanel': 
51328                 // create a new Layout (which is  a Border Layout...
51329                 var el = this.el.createChild();
51330                 var clayout = cfg.layout;
51331                 delete cfg.layout;
51332                 clayout.items   = clayout.items  || [];
51333                 // replace this exitems with the clayout ones..
51334                 xitems = clayout.items;
51335                  
51336                 
51337                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51338                     cfg.background = false;
51339                 }
51340                 var layout = new Roo.BorderLayout(el, clayout);
51341                 
51342                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51343                 //console.log('adding nested layout panel '  + cfg.toSource());
51344                 this.add(region, ret);
51345                 nb = {}; /// find first...
51346                 break;
51347                 
51348             case 'GridPanel': 
51349             
51350                 // needs grid and region
51351                 
51352                 //var el = this.getRegion(region).el.createChild();
51353                 var el = this.el.createChild();
51354                 // create the grid first...
51355                 
51356                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51357                 delete cfg.grid;
51358                 if (region == 'center' && this.active ) {
51359                     cfg.background = false;
51360                 }
51361                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51362                 
51363                 this.add(region, ret);
51364                 if (cfg.background) {
51365                     ret.on('activate', function(gp) {
51366                         if (!gp.grid.rendered) {
51367                             gp.grid.render();
51368                         }
51369                     });
51370                 } else {
51371                     grid.render();
51372                 }
51373                 break;
51374            
51375            
51376            
51377                 
51378                 
51379                 
51380             default:
51381                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51382                     
51383                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51384                     this.add(region, ret);
51385                 } else {
51386                 
51387                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51388                     return null;
51389                 }
51390                 
51391              // GridPanel (grid, cfg)
51392             
51393         }
51394         this.beginUpdate();
51395         // add children..
51396         var region = '';
51397         var abn = {};
51398         Roo.each(xitems, function(i)  {
51399             region = nb && i.region ? i.region : false;
51400             
51401             var add = ret.addxtype(i);
51402            
51403             if (region) {
51404                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51405                 if (!i.background) {
51406                     abn[region] = nb[region] ;
51407                 }
51408             }
51409             
51410         });
51411         this.endUpdate();
51412
51413         // make the last non-background panel active..
51414         //if (nb) { Roo.log(abn); }
51415         if (nb) {
51416             
51417             for(var r in abn) {
51418                 region = this.getRegion(r);
51419                 if (region) {
51420                     // tried using nb[r], but it does not work..
51421                      
51422                     region.showPanel(abn[r]);
51423                    
51424                 }
51425             }
51426         }
51427         return ret;
51428         
51429     }
51430 });
51431
51432 /**
51433  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51434  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51435  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51436  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51437  * <pre><code>
51438 // shorthand
51439 var CP = Roo.ContentPanel;
51440
51441 var layout = Roo.BorderLayout.create({
51442     north: {
51443         initialSize: 25,
51444         titlebar: false,
51445         panels: [new CP("north", "North")]
51446     },
51447     west: {
51448         split:true,
51449         initialSize: 200,
51450         minSize: 175,
51451         maxSize: 400,
51452         titlebar: true,
51453         collapsible: true,
51454         panels: [new CP("west", {title: "West"})]
51455     },
51456     east: {
51457         split:true,
51458         initialSize: 202,
51459         minSize: 175,
51460         maxSize: 400,
51461         titlebar: true,
51462         collapsible: true,
51463         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51464     },
51465     south: {
51466         split:true,
51467         initialSize: 100,
51468         minSize: 100,
51469         maxSize: 200,
51470         titlebar: true,
51471         collapsible: true,
51472         panels: [new CP("south", {title: "South", closable: true})]
51473     },
51474     center: {
51475         titlebar: true,
51476         autoScroll:true,
51477         resizeTabs: true,
51478         minTabWidth: 50,
51479         preferredTabWidth: 150,
51480         panels: [
51481             new CP("center1", {title: "Close Me", closable: true}),
51482             new CP("center2", {title: "Center Panel", closable: false})
51483         ]
51484     }
51485 }, document.body);
51486
51487 layout.getRegion("center").showPanel("center1");
51488 </code></pre>
51489  * @param config
51490  * @param targetEl
51491  */
51492 Roo.BorderLayout.create = function(config, targetEl){
51493     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51494     layout.beginUpdate();
51495     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51496     for(var j = 0, jlen = regions.length; j < jlen; j++){
51497         var lr = regions[j];
51498         if(layout.regions[lr] && config[lr].panels){
51499             var r = layout.regions[lr];
51500             var ps = config[lr].panels;
51501             layout.addTypedPanels(r, ps);
51502         }
51503     }
51504     layout.endUpdate();
51505     return layout;
51506 };
51507
51508 // private
51509 Roo.BorderLayout.RegionFactory = {
51510     // private
51511     validRegions : ["north","south","east","west","center"],
51512
51513     // private
51514     create : function(target, mgr, config){
51515         target = target.toLowerCase();
51516         if(config.lightweight || config.basic){
51517             return new Roo.BasicLayoutRegion(mgr, config, target);
51518         }
51519         switch(target){
51520             case "north":
51521                 return new Roo.NorthLayoutRegion(mgr, config);
51522             case "south":
51523                 return new Roo.SouthLayoutRegion(mgr, config);
51524             case "east":
51525                 return new Roo.EastLayoutRegion(mgr, config);
51526             case "west":
51527                 return new Roo.WestLayoutRegion(mgr, config);
51528             case "center":
51529                 return new Roo.CenterLayoutRegion(mgr, config);
51530         }
51531         throw 'Layout region "'+target+'" not supported.';
51532     }
51533 };/*
51534  * Based on:
51535  * Ext JS Library 1.1.1
51536  * Copyright(c) 2006-2007, Ext JS, LLC.
51537  *
51538  * Originally Released Under LGPL - original licence link has changed is not relivant.
51539  *
51540  * Fork - LGPL
51541  * <script type="text/javascript">
51542  */
51543  
51544 /**
51545  * @class Roo.BasicLayoutRegion
51546  * @extends Roo.util.Observable
51547  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51548  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51549  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51550  */
51551 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51552     this.mgr = mgr;
51553     this.position  = pos;
51554     this.events = {
51555         /**
51556          * @scope Roo.BasicLayoutRegion
51557          */
51558         
51559         /**
51560          * @event beforeremove
51561          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51562          * @param {Roo.LayoutRegion} this
51563          * @param {Roo.ContentPanel} panel The panel
51564          * @param {Object} e The cancel event object
51565          */
51566         "beforeremove" : true,
51567         /**
51568          * @event invalidated
51569          * Fires when the layout for this region is changed.
51570          * @param {Roo.LayoutRegion} this
51571          */
51572         "invalidated" : true,
51573         /**
51574          * @event visibilitychange
51575          * Fires when this region is shown or hidden 
51576          * @param {Roo.LayoutRegion} this
51577          * @param {Boolean} visibility true or false
51578          */
51579         "visibilitychange" : true,
51580         /**
51581          * @event paneladded
51582          * Fires when a panel is added. 
51583          * @param {Roo.LayoutRegion} this
51584          * @param {Roo.ContentPanel} panel The panel
51585          */
51586         "paneladded" : true,
51587         /**
51588          * @event panelremoved
51589          * Fires when a panel is removed. 
51590          * @param {Roo.LayoutRegion} this
51591          * @param {Roo.ContentPanel} panel The panel
51592          */
51593         "panelremoved" : true,
51594         /**
51595          * @event beforecollapse
51596          * Fires when this region before collapse.
51597          * @param {Roo.LayoutRegion} this
51598          */
51599         "beforecollapse" : true,
51600         /**
51601          * @event collapsed
51602          * Fires when this region is collapsed.
51603          * @param {Roo.LayoutRegion} this
51604          */
51605         "collapsed" : true,
51606         /**
51607          * @event expanded
51608          * Fires when this region is expanded.
51609          * @param {Roo.LayoutRegion} this
51610          */
51611         "expanded" : true,
51612         /**
51613          * @event slideshow
51614          * Fires when this region is slid into view.
51615          * @param {Roo.LayoutRegion} this
51616          */
51617         "slideshow" : true,
51618         /**
51619          * @event slidehide
51620          * Fires when this region slides out of view. 
51621          * @param {Roo.LayoutRegion} this
51622          */
51623         "slidehide" : true,
51624         /**
51625          * @event panelactivated
51626          * Fires when a panel is activated. 
51627          * @param {Roo.LayoutRegion} this
51628          * @param {Roo.ContentPanel} panel The activated panel
51629          */
51630         "panelactivated" : true,
51631         /**
51632          * @event resized
51633          * Fires when the user resizes this region. 
51634          * @param {Roo.LayoutRegion} this
51635          * @param {Number} newSize The new size (width for east/west, height for north/south)
51636          */
51637         "resized" : true
51638     };
51639     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51640     this.panels = new Roo.util.MixedCollection();
51641     this.panels.getKey = this.getPanelId.createDelegate(this);
51642     this.box = null;
51643     this.activePanel = null;
51644     // ensure listeners are added...
51645     
51646     if (config.listeners || config.events) {
51647         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51648             listeners : config.listeners || {},
51649             events : config.events || {}
51650         });
51651     }
51652     
51653     if(skipConfig !== true){
51654         this.applyConfig(config);
51655     }
51656 };
51657
51658 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51659     getPanelId : function(p){
51660         return p.getId();
51661     },
51662     
51663     applyConfig : function(config){
51664         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51665         this.config = config;
51666         
51667     },
51668     
51669     /**
51670      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51671      * the width, for horizontal (north, south) the height.
51672      * @param {Number} newSize The new width or height
51673      */
51674     resizeTo : function(newSize){
51675         var el = this.el ? this.el :
51676                  (this.activePanel ? this.activePanel.getEl() : null);
51677         if(el){
51678             switch(this.position){
51679                 case "east":
51680                 case "west":
51681                     el.setWidth(newSize);
51682                     this.fireEvent("resized", this, newSize);
51683                 break;
51684                 case "north":
51685                 case "south":
51686                     el.setHeight(newSize);
51687                     this.fireEvent("resized", this, newSize);
51688                 break;                
51689             }
51690         }
51691     },
51692     
51693     getBox : function(){
51694         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51695     },
51696     
51697     getMargins : function(){
51698         return this.margins;
51699     },
51700     
51701     updateBox : function(box){
51702         this.box = box;
51703         var el = this.activePanel.getEl();
51704         el.dom.style.left = box.x + "px";
51705         el.dom.style.top = box.y + "px";
51706         this.activePanel.setSize(box.width, box.height);
51707     },
51708     
51709     /**
51710      * Returns the container element for this region.
51711      * @return {Roo.Element}
51712      */
51713     getEl : function(){
51714         return this.activePanel;
51715     },
51716     
51717     /**
51718      * Returns true if this region is currently visible.
51719      * @return {Boolean}
51720      */
51721     isVisible : function(){
51722         return this.activePanel ? true : false;
51723     },
51724     
51725     setActivePanel : function(panel){
51726         panel = this.getPanel(panel);
51727         if(this.activePanel && this.activePanel != panel){
51728             this.activePanel.setActiveState(false);
51729             this.activePanel.getEl().setLeftTop(-10000,-10000);
51730         }
51731         this.activePanel = panel;
51732         panel.setActiveState(true);
51733         if(this.box){
51734             panel.setSize(this.box.width, this.box.height);
51735         }
51736         this.fireEvent("panelactivated", this, panel);
51737         this.fireEvent("invalidated");
51738     },
51739     
51740     /**
51741      * Show the specified panel.
51742      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51743      * @return {Roo.ContentPanel} The shown panel or null
51744      */
51745     showPanel : function(panel){
51746         if(panel = this.getPanel(panel)){
51747             this.setActivePanel(panel);
51748         }
51749         return panel;
51750     },
51751     
51752     /**
51753      * Get the active panel for this region.
51754      * @return {Roo.ContentPanel} The active panel or null
51755      */
51756     getActivePanel : function(){
51757         return this.activePanel;
51758     },
51759     
51760     /**
51761      * Add the passed ContentPanel(s)
51762      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51763      * @return {Roo.ContentPanel} The panel added (if only one was added)
51764      */
51765     add : function(panel){
51766         if(arguments.length > 1){
51767             for(var i = 0, len = arguments.length; i < len; i++) {
51768                 this.add(arguments[i]);
51769             }
51770             return null;
51771         }
51772         if(this.hasPanel(panel)){
51773             this.showPanel(panel);
51774             return panel;
51775         }
51776         var el = panel.getEl();
51777         if(el.dom.parentNode != this.mgr.el.dom){
51778             this.mgr.el.dom.appendChild(el.dom);
51779         }
51780         if(panel.setRegion){
51781             panel.setRegion(this);
51782         }
51783         this.panels.add(panel);
51784         el.setStyle("position", "absolute");
51785         if(!panel.background){
51786             this.setActivePanel(panel);
51787             if(this.config.initialSize && this.panels.getCount()==1){
51788                 this.resizeTo(this.config.initialSize);
51789             }
51790         }
51791         this.fireEvent("paneladded", this, panel);
51792         return panel;
51793     },
51794     
51795     /**
51796      * Returns true if the panel is in this region.
51797      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51798      * @return {Boolean}
51799      */
51800     hasPanel : function(panel){
51801         if(typeof panel == "object"){ // must be panel obj
51802             panel = panel.getId();
51803         }
51804         return this.getPanel(panel) ? true : false;
51805     },
51806     
51807     /**
51808      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51809      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51810      * @param {Boolean} preservePanel Overrides the config preservePanel option
51811      * @return {Roo.ContentPanel} The panel that was removed
51812      */
51813     remove : function(panel, preservePanel){
51814         panel = this.getPanel(panel);
51815         if(!panel){
51816             return null;
51817         }
51818         var e = {};
51819         this.fireEvent("beforeremove", this, panel, e);
51820         if(e.cancel === true){
51821             return null;
51822         }
51823         var panelId = panel.getId();
51824         this.panels.removeKey(panelId);
51825         return panel;
51826     },
51827     
51828     /**
51829      * Returns the panel specified or null if it's not in this region.
51830      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51831      * @return {Roo.ContentPanel}
51832      */
51833     getPanel : function(id){
51834         if(typeof id == "object"){ // must be panel obj
51835             return id;
51836         }
51837         return this.panels.get(id);
51838     },
51839     
51840     /**
51841      * Returns this regions position (north/south/east/west/center).
51842      * @return {String} 
51843      */
51844     getPosition: function(){
51845         return this.position;    
51846     }
51847 });/*
51848  * Based on:
51849  * Ext JS Library 1.1.1
51850  * Copyright(c) 2006-2007, Ext JS, LLC.
51851  *
51852  * Originally Released Under LGPL - original licence link has changed is not relivant.
51853  *
51854  * Fork - LGPL
51855  * <script type="text/javascript">
51856  */
51857  
51858 /**
51859  * @class Roo.LayoutRegion
51860  * @extends Roo.BasicLayoutRegion
51861  * This class represents a region in a layout manager.
51862  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51863  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51864  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51865  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51866  * @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})
51867  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51868  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51869  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51870  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51871  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51872  * @cfg {String}    title           The title for the region (overrides panel titles)
51873  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51874  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51875  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51876  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51877  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51878  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51879  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51880  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51881  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51882  * @cfg {Boolean}   showPin         True to show a pin button
51883  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51884  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51885  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51886  * @cfg {Number}    width           For East/West panels
51887  * @cfg {Number}    height          For North/South panels
51888  * @cfg {Boolean}   split           To show the splitter
51889  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51890  */
51891 Roo.LayoutRegion = function(mgr, config, pos){
51892     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51893     var dh = Roo.DomHelper;
51894     /** This region's container element 
51895     * @type Roo.Element */
51896     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51897     /** This region's title element 
51898     * @type Roo.Element */
51899
51900     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51901         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51902         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51903     ]}, true);
51904     this.titleEl.enableDisplayMode();
51905     /** This region's title text element 
51906     * @type HTMLElement */
51907     this.titleTextEl = this.titleEl.dom.firstChild;
51908     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51909     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51910     this.closeBtn.enableDisplayMode();
51911     this.closeBtn.on("click", this.closeClicked, this);
51912     this.closeBtn.hide();
51913
51914     this.createBody(config);
51915     this.visible = true;
51916     this.collapsed = false;
51917
51918     if(config.hideWhenEmpty){
51919         this.hide();
51920         this.on("paneladded", this.validateVisibility, this);
51921         this.on("panelremoved", this.validateVisibility, this);
51922     }
51923     this.applyConfig(config);
51924 };
51925
51926 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51927
51928     createBody : function(){
51929         /** This region's body element 
51930         * @type Roo.Element */
51931         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51932     },
51933
51934     applyConfig : function(c){
51935         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51936             var dh = Roo.DomHelper;
51937             if(c.titlebar !== false){
51938                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51939                 this.collapseBtn.on("click", this.collapse, this);
51940                 this.collapseBtn.enableDisplayMode();
51941
51942                 if(c.showPin === true || this.showPin){
51943                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51944                     this.stickBtn.enableDisplayMode();
51945                     this.stickBtn.on("click", this.expand, this);
51946                     this.stickBtn.hide();
51947                 }
51948             }
51949             /** This region's collapsed element
51950             * @type Roo.Element */
51951             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51952                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51953             ]}, true);
51954             if(c.floatable !== false){
51955                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51956                this.collapsedEl.on("click", this.collapseClick, this);
51957             }
51958
51959             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51960                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51961                    id: "message", unselectable: "on", style:{"float":"left"}});
51962                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51963              }
51964             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51965             this.expandBtn.on("click", this.expand, this);
51966         }
51967         if(this.collapseBtn){
51968             this.collapseBtn.setVisible(c.collapsible == true);
51969         }
51970         this.cmargins = c.cmargins || this.cmargins ||
51971                          (this.position == "west" || this.position == "east" ?
51972                              {top: 0, left: 2, right:2, bottom: 0} :
51973                              {top: 2, left: 0, right:0, bottom: 2});
51974         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51975         this.bottomTabs = c.tabPosition != "top";
51976         this.autoScroll = c.autoScroll || false;
51977         if(this.autoScroll){
51978             this.bodyEl.setStyle("overflow", "auto");
51979         }else{
51980             this.bodyEl.setStyle("overflow", "hidden");
51981         }
51982         //if(c.titlebar !== false){
51983             if((!c.titlebar && !c.title) || c.titlebar === false){
51984                 this.titleEl.hide();
51985             }else{
51986                 this.titleEl.show();
51987                 if(c.title){
51988                     this.titleTextEl.innerHTML = c.title;
51989                 }
51990             }
51991         //}
51992         this.duration = c.duration || .30;
51993         this.slideDuration = c.slideDuration || .45;
51994         this.config = c;
51995         if(c.collapsed){
51996             this.collapse(true);
51997         }
51998         if(c.hidden){
51999             this.hide();
52000         }
52001     },
52002     /**
52003      * Returns true if this region is currently visible.
52004      * @return {Boolean}
52005      */
52006     isVisible : function(){
52007         return this.visible;
52008     },
52009
52010     /**
52011      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52012      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52013      */
52014     setCollapsedTitle : function(title){
52015         title = title || "&#160;";
52016         if(this.collapsedTitleTextEl){
52017             this.collapsedTitleTextEl.innerHTML = title;
52018         }
52019     },
52020
52021     getBox : function(){
52022         var b;
52023         if(!this.collapsed){
52024             b = this.el.getBox(false, true);
52025         }else{
52026             b = this.collapsedEl.getBox(false, true);
52027         }
52028         return b;
52029     },
52030
52031     getMargins : function(){
52032         return this.collapsed ? this.cmargins : this.margins;
52033     },
52034
52035     highlight : function(){
52036         this.el.addClass("x-layout-panel-dragover");
52037     },
52038
52039     unhighlight : function(){
52040         this.el.removeClass("x-layout-panel-dragover");
52041     },
52042
52043     updateBox : function(box){
52044         this.box = box;
52045         if(!this.collapsed){
52046             this.el.dom.style.left = box.x + "px";
52047             this.el.dom.style.top = box.y + "px";
52048             this.updateBody(box.width, box.height);
52049         }else{
52050             this.collapsedEl.dom.style.left = box.x + "px";
52051             this.collapsedEl.dom.style.top = box.y + "px";
52052             this.collapsedEl.setSize(box.width, box.height);
52053         }
52054         if(this.tabs){
52055             this.tabs.autoSizeTabs();
52056         }
52057     },
52058
52059     updateBody : function(w, h){
52060         if(w !== null){
52061             this.el.setWidth(w);
52062             w -= this.el.getBorderWidth("rl");
52063             if(this.config.adjustments){
52064                 w += this.config.adjustments[0];
52065             }
52066         }
52067         if(h !== null){
52068             this.el.setHeight(h);
52069             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52070             h -= this.el.getBorderWidth("tb");
52071             if(this.config.adjustments){
52072                 h += this.config.adjustments[1];
52073             }
52074             this.bodyEl.setHeight(h);
52075             if(this.tabs){
52076                 h = this.tabs.syncHeight(h);
52077             }
52078         }
52079         if(this.panelSize){
52080             w = w !== null ? w : this.panelSize.width;
52081             h = h !== null ? h : this.panelSize.height;
52082         }
52083         if(this.activePanel){
52084             var el = this.activePanel.getEl();
52085             w = w !== null ? w : el.getWidth();
52086             h = h !== null ? h : el.getHeight();
52087             this.panelSize = {width: w, height: h};
52088             this.activePanel.setSize(w, h);
52089         }
52090         if(Roo.isIE && this.tabs){
52091             this.tabs.el.repaint();
52092         }
52093     },
52094
52095     /**
52096      * Returns the container element for this region.
52097      * @return {Roo.Element}
52098      */
52099     getEl : function(){
52100         return this.el;
52101     },
52102
52103     /**
52104      * Hides this region.
52105      */
52106     hide : function(){
52107         if(!this.collapsed){
52108             this.el.dom.style.left = "-2000px";
52109             this.el.hide();
52110         }else{
52111             this.collapsedEl.dom.style.left = "-2000px";
52112             this.collapsedEl.hide();
52113         }
52114         this.visible = false;
52115         this.fireEvent("visibilitychange", this, false);
52116     },
52117
52118     /**
52119      * Shows this region if it was previously hidden.
52120      */
52121     show : function(){
52122         if(!this.collapsed){
52123             this.el.show();
52124         }else{
52125             this.collapsedEl.show();
52126         }
52127         this.visible = true;
52128         this.fireEvent("visibilitychange", this, true);
52129     },
52130
52131     closeClicked : function(){
52132         if(this.activePanel){
52133             this.remove(this.activePanel);
52134         }
52135     },
52136
52137     collapseClick : function(e){
52138         if(this.isSlid){
52139            e.stopPropagation();
52140            this.slideIn();
52141         }else{
52142            e.stopPropagation();
52143            this.slideOut();
52144         }
52145     },
52146
52147     /**
52148      * Collapses this region.
52149      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52150      */
52151     collapse : function(skipAnim, skipCheck = false){
52152         if(this.collapsed) {
52153             return;
52154         }
52155         
52156         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52157             
52158             this.collapsed = true;
52159             if(this.split){
52160                 this.split.el.hide();
52161             }
52162             if(this.config.animate && skipAnim !== true){
52163                 this.fireEvent("invalidated", this);
52164                 this.animateCollapse();
52165             }else{
52166                 this.el.setLocation(-20000,-20000);
52167                 this.el.hide();
52168                 this.collapsedEl.show();
52169                 this.fireEvent("collapsed", this);
52170                 this.fireEvent("invalidated", this);
52171             }
52172         }
52173         
52174     },
52175
52176     animateCollapse : function(){
52177         // overridden
52178     },
52179
52180     /**
52181      * Expands this region if it was previously collapsed.
52182      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52183      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52184      */
52185     expand : function(e, skipAnim){
52186         if(e) {
52187             e.stopPropagation();
52188         }
52189         if(!this.collapsed || this.el.hasActiveFx()) {
52190             return;
52191         }
52192         if(this.isSlid){
52193             this.afterSlideIn();
52194             skipAnim = true;
52195         }
52196         this.collapsed = false;
52197         if(this.config.animate && skipAnim !== true){
52198             this.animateExpand();
52199         }else{
52200             this.el.show();
52201             if(this.split){
52202                 this.split.el.show();
52203             }
52204             this.collapsedEl.setLocation(-2000,-2000);
52205             this.collapsedEl.hide();
52206             this.fireEvent("invalidated", this);
52207             this.fireEvent("expanded", this);
52208         }
52209     },
52210
52211     animateExpand : function(){
52212         // overridden
52213     },
52214
52215     initTabs : function()
52216     {
52217         this.bodyEl.setStyle("overflow", "hidden");
52218         var ts = new Roo.TabPanel(
52219                 this.bodyEl.dom,
52220                 {
52221                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52222                     disableTooltips: this.config.disableTabTips,
52223                     toolbar : this.config.toolbar
52224                 }
52225         );
52226         if(this.config.hideTabs){
52227             ts.stripWrap.setDisplayed(false);
52228         }
52229         this.tabs = ts;
52230         ts.resizeTabs = this.config.resizeTabs === true;
52231         ts.minTabWidth = this.config.minTabWidth || 40;
52232         ts.maxTabWidth = this.config.maxTabWidth || 250;
52233         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52234         ts.monitorResize = false;
52235         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52236         ts.bodyEl.addClass('x-layout-tabs-body');
52237         this.panels.each(this.initPanelAsTab, this);
52238     },
52239
52240     initPanelAsTab : function(panel){
52241         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52242                     this.config.closeOnTab && panel.isClosable());
52243         if(panel.tabTip !== undefined){
52244             ti.setTooltip(panel.tabTip);
52245         }
52246         ti.on("activate", function(){
52247               this.setActivePanel(panel);
52248         }, this);
52249         if(this.config.closeOnTab){
52250             ti.on("beforeclose", function(t, e){
52251                 e.cancel = true;
52252                 this.remove(panel);
52253             }, this);
52254         }
52255         return ti;
52256     },
52257
52258     updatePanelTitle : function(panel, title){
52259         if(this.activePanel == panel){
52260             this.updateTitle(title);
52261         }
52262         if(this.tabs){
52263             var ti = this.tabs.getTab(panel.getEl().id);
52264             ti.setText(title);
52265             if(panel.tabTip !== undefined){
52266                 ti.setTooltip(panel.tabTip);
52267             }
52268         }
52269     },
52270
52271     updateTitle : function(title){
52272         if(this.titleTextEl && !this.config.title){
52273             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52274         }
52275     },
52276
52277     setActivePanel : function(panel){
52278         panel = this.getPanel(panel);
52279         if(this.activePanel && this.activePanel != panel){
52280             this.activePanel.setActiveState(false);
52281         }
52282         this.activePanel = panel;
52283         panel.setActiveState(true);
52284         if(this.panelSize){
52285             panel.setSize(this.panelSize.width, this.panelSize.height);
52286         }
52287         if(this.closeBtn){
52288             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52289         }
52290         this.updateTitle(panel.getTitle());
52291         if(this.tabs){
52292             this.fireEvent("invalidated", this);
52293         }
52294         this.fireEvent("panelactivated", this, panel);
52295     },
52296
52297     /**
52298      * Shows the specified panel.
52299      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52300      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52301      */
52302     showPanel : function(panel)
52303     {
52304         panel = this.getPanel(panel);
52305         if(panel){
52306             if(this.tabs){
52307                 var tab = this.tabs.getTab(panel.getEl().id);
52308                 if(tab.isHidden()){
52309                     this.tabs.unhideTab(tab.id);
52310                 }
52311                 tab.activate();
52312             }else{
52313                 this.setActivePanel(panel);
52314             }
52315         }
52316         return panel;
52317     },
52318
52319     /**
52320      * Get the active panel for this region.
52321      * @return {Roo.ContentPanel} The active panel or null
52322      */
52323     getActivePanel : function(){
52324         return this.activePanel;
52325     },
52326
52327     validateVisibility : function(){
52328         if(this.panels.getCount() < 1){
52329             this.updateTitle("&#160;");
52330             this.closeBtn.hide();
52331             this.hide();
52332         }else{
52333             if(!this.isVisible()){
52334                 this.show();
52335             }
52336         }
52337     },
52338
52339     /**
52340      * Adds the passed ContentPanel(s) to this region.
52341      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52342      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52343      */
52344     add : function(panel){
52345         if(arguments.length > 1){
52346             for(var i = 0, len = arguments.length; i < len; i++) {
52347                 this.add(arguments[i]);
52348             }
52349             return null;
52350         }
52351         if(this.hasPanel(panel)){
52352             this.showPanel(panel);
52353             return panel;
52354         }
52355         panel.setRegion(this);
52356         this.panels.add(panel);
52357         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52358             this.bodyEl.dom.appendChild(panel.getEl().dom);
52359             if(panel.background !== true){
52360                 this.setActivePanel(panel);
52361             }
52362             this.fireEvent("paneladded", this, panel);
52363             return panel;
52364         }
52365         if(!this.tabs){
52366             this.initTabs();
52367         }else{
52368             this.initPanelAsTab(panel);
52369         }
52370         if(panel.background !== true){
52371             this.tabs.activate(panel.getEl().id);
52372         }
52373         this.fireEvent("paneladded", this, panel);
52374         return panel;
52375     },
52376
52377     /**
52378      * Hides the tab for the specified panel.
52379      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52380      */
52381     hidePanel : function(panel){
52382         if(this.tabs && (panel = this.getPanel(panel))){
52383             this.tabs.hideTab(panel.getEl().id);
52384         }
52385     },
52386
52387     /**
52388      * Unhides the tab for a previously hidden panel.
52389      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52390      */
52391     unhidePanel : function(panel){
52392         if(this.tabs && (panel = this.getPanel(panel))){
52393             this.tabs.unhideTab(panel.getEl().id);
52394         }
52395     },
52396
52397     clearPanels : function(){
52398         while(this.panels.getCount() > 0){
52399              this.remove(this.panels.first());
52400         }
52401     },
52402
52403     /**
52404      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52405      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52406      * @param {Boolean} preservePanel Overrides the config preservePanel option
52407      * @return {Roo.ContentPanel} The panel that was removed
52408      */
52409     remove : function(panel, preservePanel){
52410         panel = this.getPanel(panel);
52411         if(!panel){
52412             return null;
52413         }
52414         var e = {};
52415         this.fireEvent("beforeremove", this, panel, e);
52416         if(e.cancel === true){
52417             return null;
52418         }
52419         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52420         var panelId = panel.getId();
52421         this.panels.removeKey(panelId);
52422         if(preservePanel){
52423             document.body.appendChild(panel.getEl().dom);
52424         }
52425         if(this.tabs){
52426             this.tabs.removeTab(panel.getEl().id);
52427         }else if (!preservePanel){
52428             this.bodyEl.dom.removeChild(panel.getEl().dom);
52429         }
52430         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52431             var p = this.panels.first();
52432             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52433             tempEl.appendChild(p.getEl().dom);
52434             this.bodyEl.update("");
52435             this.bodyEl.dom.appendChild(p.getEl().dom);
52436             tempEl = null;
52437             this.updateTitle(p.getTitle());
52438             this.tabs = null;
52439             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52440             this.setActivePanel(p);
52441         }
52442         panel.setRegion(null);
52443         if(this.activePanel == panel){
52444             this.activePanel = null;
52445         }
52446         if(this.config.autoDestroy !== false && preservePanel !== true){
52447             try{panel.destroy();}catch(e){}
52448         }
52449         this.fireEvent("panelremoved", this, panel);
52450         return panel;
52451     },
52452
52453     /**
52454      * Returns the TabPanel component used by this region
52455      * @return {Roo.TabPanel}
52456      */
52457     getTabs : function(){
52458         return this.tabs;
52459     },
52460
52461     createTool : function(parentEl, className){
52462         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52463             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52464         btn.addClassOnOver("x-layout-tools-button-over");
52465         return btn;
52466     }
52467 });/*
52468  * Based on:
52469  * Ext JS Library 1.1.1
52470  * Copyright(c) 2006-2007, Ext JS, LLC.
52471  *
52472  * Originally Released Under LGPL - original licence link has changed is not relivant.
52473  *
52474  * Fork - LGPL
52475  * <script type="text/javascript">
52476  */
52477  
52478
52479
52480 /**
52481  * @class Roo.SplitLayoutRegion
52482  * @extends Roo.LayoutRegion
52483  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52484  */
52485 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52486     this.cursor = cursor;
52487     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52488 };
52489
52490 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52491     splitTip : "Drag to resize.",
52492     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52493     useSplitTips : false,
52494
52495     applyConfig : function(config){
52496         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52497         if(config.split){
52498             if(!this.split){
52499                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52500                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52501                 /** The SplitBar for this region 
52502                 * @type Roo.SplitBar */
52503                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52504                 this.split.on("moved", this.onSplitMove, this);
52505                 this.split.useShim = config.useShim === true;
52506                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52507                 if(this.useSplitTips){
52508                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52509                 }
52510                 if(config.collapsible){
52511                     this.split.el.on("dblclick", this.collapse,  this);
52512                 }
52513             }
52514             if(typeof config.minSize != "undefined"){
52515                 this.split.minSize = config.minSize;
52516             }
52517             if(typeof config.maxSize != "undefined"){
52518                 this.split.maxSize = config.maxSize;
52519             }
52520             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52521                 this.hideSplitter();
52522             }
52523         }
52524     },
52525
52526     getHMaxSize : function(){
52527          var cmax = this.config.maxSize || 10000;
52528          var center = this.mgr.getRegion("center");
52529          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52530     },
52531
52532     getVMaxSize : function(){
52533          var cmax = this.config.maxSize || 10000;
52534          var center = this.mgr.getRegion("center");
52535          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52536     },
52537
52538     onSplitMove : function(split, newSize){
52539         this.fireEvent("resized", this, newSize);
52540     },
52541     
52542     /** 
52543      * Returns the {@link Roo.SplitBar} for this region.
52544      * @return {Roo.SplitBar}
52545      */
52546     getSplitBar : function(){
52547         return this.split;
52548     },
52549     
52550     hide : function(){
52551         this.hideSplitter();
52552         Roo.SplitLayoutRegion.superclass.hide.call(this);
52553     },
52554
52555     hideSplitter : function(){
52556         if(this.split){
52557             this.split.el.setLocation(-2000,-2000);
52558             this.split.el.hide();
52559         }
52560     },
52561
52562     show : function(){
52563         if(this.split){
52564             this.split.el.show();
52565         }
52566         Roo.SplitLayoutRegion.superclass.show.call(this);
52567     },
52568     
52569     beforeSlide: function(){
52570         if(Roo.isGecko){// firefox overflow auto bug workaround
52571             this.bodyEl.clip();
52572             if(this.tabs) {
52573                 this.tabs.bodyEl.clip();
52574             }
52575             if(this.activePanel){
52576                 this.activePanel.getEl().clip();
52577                 
52578                 if(this.activePanel.beforeSlide){
52579                     this.activePanel.beforeSlide();
52580                 }
52581             }
52582         }
52583     },
52584     
52585     afterSlide : function(){
52586         if(Roo.isGecko){// firefox overflow auto bug workaround
52587             this.bodyEl.unclip();
52588             if(this.tabs) {
52589                 this.tabs.bodyEl.unclip();
52590             }
52591             if(this.activePanel){
52592                 this.activePanel.getEl().unclip();
52593                 if(this.activePanel.afterSlide){
52594                     this.activePanel.afterSlide();
52595                 }
52596             }
52597         }
52598     },
52599
52600     initAutoHide : function(){
52601         if(this.autoHide !== false){
52602             if(!this.autoHideHd){
52603                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52604                 this.autoHideHd = {
52605                     "mouseout": function(e){
52606                         if(!e.within(this.el, true)){
52607                             st.delay(500);
52608                         }
52609                     },
52610                     "mouseover" : function(e){
52611                         st.cancel();
52612                     },
52613                     scope : this
52614                 };
52615             }
52616             this.el.on(this.autoHideHd);
52617         }
52618     },
52619
52620     clearAutoHide : function(){
52621         if(this.autoHide !== false){
52622             this.el.un("mouseout", this.autoHideHd.mouseout);
52623             this.el.un("mouseover", this.autoHideHd.mouseover);
52624         }
52625     },
52626
52627     clearMonitor : function(){
52628         Roo.get(document).un("click", this.slideInIf, this);
52629     },
52630
52631     // these names are backwards but not changed for compat
52632     slideOut : function(){
52633         if(this.isSlid || this.el.hasActiveFx()){
52634             return;
52635         }
52636         this.isSlid = true;
52637         if(this.collapseBtn){
52638             this.collapseBtn.hide();
52639         }
52640         this.closeBtnState = this.closeBtn.getStyle('display');
52641         this.closeBtn.hide();
52642         if(this.stickBtn){
52643             this.stickBtn.show();
52644         }
52645         this.el.show();
52646         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52647         this.beforeSlide();
52648         this.el.setStyle("z-index", 10001);
52649         this.el.slideIn(this.getSlideAnchor(), {
52650             callback: function(){
52651                 this.afterSlide();
52652                 this.initAutoHide();
52653                 Roo.get(document).on("click", this.slideInIf, this);
52654                 this.fireEvent("slideshow", this);
52655             },
52656             scope: this,
52657             block: true
52658         });
52659     },
52660
52661     afterSlideIn : function(){
52662         this.clearAutoHide();
52663         this.isSlid = false;
52664         this.clearMonitor();
52665         this.el.setStyle("z-index", "");
52666         if(this.collapseBtn){
52667             this.collapseBtn.show();
52668         }
52669         this.closeBtn.setStyle('display', this.closeBtnState);
52670         if(this.stickBtn){
52671             this.stickBtn.hide();
52672         }
52673         this.fireEvent("slidehide", this);
52674     },
52675
52676     slideIn : function(cb){
52677         if(!this.isSlid || this.el.hasActiveFx()){
52678             Roo.callback(cb);
52679             return;
52680         }
52681         this.isSlid = false;
52682         this.beforeSlide();
52683         this.el.slideOut(this.getSlideAnchor(), {
52684             callback: function(){
52685                 this.el.setLeftTop(-10000, -10000);
52686                 this.afterSlide();
52687                 this.afterSlideIn();
52688                 Roo.callback(cb);
52689             },
52690             scope: this,
52691             block: true
52692         });
52693     },
52694     
52695     slideInIf : function(e){
52696         if(!e.within(this.el)){
52697             this.slideIn();
52698         }
52699     },
52700
52701     animateCollapse : function(){
52702         this.beforeSlide();
52703         this.el.setStyle("z-index", 20000);
52704         var anchor = this.getSlideAnchor();
52705         this.el.slideOut(anchor, {
52706             callback : function(){
52707                 this.el.setStyle("z-index", "");
52708                 this.collapsedEl.slideIn(anchor, {duration:.3});
52709                 this.afterSlide();
52710                 this.el.setLocation(-10000,-10000);
52711                 this.el.hide();
52712                 this.fireEvent("collapsed", this);
52713             },
52714             scope: this,
52715             block: true
52716         });
52717     },
52718
52719     animateExpand : function(){
52720         this.beforeSlide();
52721         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52722         this.el.setStyle("z-index", 20000);
52723         this.collapsedEl.hide({
52724             duration:.1
52725         });
52726         this.el.slideIn(this.getSlideAnchor(), {
52727             callback : function(){
52728                 this.el.setStyle("z-index", "");
52729                 this.afterSlide();
52730                 if(this.split){
52731                     this.split.el.show();
52732                 }
52733                 this.fireEvent("invalidated", this);
52734                 this.fireEvent("expanded", this);
52735             },
52736             scope: this,
52737             block: true
52738         });
52739     },
52740
52741     anchors : {
52742         "west" : "left",
52743         "east" : "right",
52744         "north" : "top",
52745         "south" : "bottom"
52746     },
52747
52748     sanchors : {
52749         "west" : "l",
52750         "east" : "r",
52751         "north" : "t",
52752         "south" : "b"
52753     },
52754
52755     canchors : {
52756         "west" : "tl-tr",
52757         "east" : "tr-tl",
52758         "north" : "tl-bl",
52759         "south" : "bl-tl"
52760     },
52761
52762     getAnchor : function(){
52763         return this.anchors[this.position];
52764     },
52765
52766     getCollapseAnchor : function(){
52767         return this.canchors[this.position];
52768     },
52769
52770     getSlideAnchor : function(){
52771         return this.sanchors[this.position];
52772     },
52773
52774     getAlignAdj : function(){
52775         var cm = this.cmargins;
52776         switch(this.position){
52777             case "west":
52778                 return [0, 0];
52779             break;
52780             case "east":
52781                 return [0, 0];
52782             break;
52783             case "north":
52784                 return [0, 0];
52785             break;
52786             case "south":
52787                 return [0, 0];
52788             break;
52789         }
52790     },
52791
52792     getExpandAdj : function(){
52793         var c = this.collapsedEl, cm = this.cmargins;
52794         switch(this.position){
52795             case "west":
52796                 return [-(cm.right+c.getWidth()+cm.left), 0];
52797             break;
52798             case "east":
52799                 return [cm.right+c.getWidth()+cm.left, 0];
52800             break;
52801             case "north":
52802                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52803             break;
52804             case "south":
52805                 return [0, cm.top+cm.bottom+c.getHeight()];
52806             break;
52807         }
52808     }
52809 });/*
52810  * Based on:
52811  * Ext JS Library 1.1.1
52812  * Copyright(c) 2006-2007, Ext JS, LLC.
52813  *
52814  * Originally Released Under LGPL - original licence link has changed is not relivant.
52815  *
52816  * Fork - LGPL
52817  * <script type="text/javascript">
52818  */
52819 /*
52820  * These classes are private internal classes
52821  */
52822 Roo.CenterLayoutRegion = function(mgr, config){
52823     Roo.LayoutRegion.call(this, mgr, config, "center");
52824     this.visible = true;
52825     this.minWidth = config.minWidth || 20;
52826     this.minHeight = config.minHeight || 20;
52827 };
52828
52829 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52830     hide : function(){
52831         // center panel can't be hidden
52832     },
52833     
52834     show : function(){
52835         // center panel can't be hidden
52836     },
52837     
52838     getMinWidth: function(){
52839         return this.minWidth;
52840     },
52841     
52842     getMinHeight: function(){
52843         return this.minHeight;
52844     }
52845 });
52846
52847
52848 Roo.NorthLayoutRegion = function(mgr, config){
52849     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52850     if(this.split){
52851         this.split.placement = Roo.SplitBar.TOP;
52852         this.split.orientation = Roo.SplitBar.VERTICAL;
52853         this.split.el.addClass("x-layout-split-v");
52854     }
52855     var size = config.initialSize || config.height;
52856     if(typeof size != "undefined"){
52857         this.el.setHeight(size);
52858     }
52859 };
52860 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52861     orientation: Roo.SplitBar.VERTICAL,
52862     getBox : function(){
52863         if(this.collapsed){
52864             return this.collapsedEl.getBox();
52865         }
52866         var box = this.el.getBox();
52867         if(this.split){
52868             box.height += this.split.el.getHeight();
52869         }
52870         return box;
52871     },
52872     
52873     updateBox : function(box){
52874         if(this.split && !this.collapsed){
52875             box.height -= this.split.el.getHeight();
52876             this.split.el.setLeft(box.x);
52877             this.split.el.setTop(box.y+box.height);
52878             this.split.el.setWidth(box.width);
52879         }
52880         if(this.collapsed){
52881             this.updateBody(box.width, null);
52882         }
52883         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52884     }
52885 });
52886
52887 Roo.SouthLayoutRegion = function(mgr, config){
52888     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52889     if(this.split){
52890         this.split.placement = Roo.SplitBar.BOTTOM;
52891         this.split.orientation = Roo.SplitBar.VERTICAL;
52892         this.split.el.addClass("x-layout-split-v");
52893     }
52894     var size = config.initialSize || config.height;
52895     if(typeof size != "undefined"){
52896         this.el.setHeight(size);
52897     }
52898 };
52899 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52900     orientation: Roo.SplitBar.VERTICAL,
52901     getBox : function(){
52902         if(this.collapsed){
52903             return this.collapsedEl.getBox();
52904         }
52905         var box = this.el.getBox();
52906         if(this.split){
52907             var sh = this.split.el.getHeight();
52908             box.height += sh;
52909             box.y -= sh;
52910         }
52911         return box;
52912     },
52913     
52914     updateBox : function(box){
52915         if(this.split && !this.collapsed){
52916             var sh = this.split.el.getHeight();
52917             box.height -= sh;
52918             box.y += sh;
52919             this.split.el.setLeft(box.x);
52920             this.split.el.setTop(box.y-sh);
52921             this.split.el.setWidth(box.width);
52922         }
52923         if(this.collapsed){
52924             this.updateBody(box.width, null);
52925         }
52926         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52927     }
52928 });
52929
52930 Roo.EastLayoutRegion = function(mgr, config){
52931     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52932     if(this.split){
52933         this.split.placement = Roo.SplitBar.RIGHT;
52934         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52935         this.split.el.addClass("x-layout-split-h");
52936     }
52937     var size = config.initialSize || config.width;
52938     if(typeof size != "undefined"){
52939         this.el.setWidth(size);
52940     }
52941 };
52942 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52943     orientation: Roo.SplitBar.HORIZONTAL,
52944     getBox : function(){
52945         if(this.collapsed){
52946             return this.collapsedEl.getBox();
52947         }
52948         var box = this.el.getBox();
52949         if(this.split){
52950             var sw = this.split.el.getWidth();
52951             box.width += sw;
52952             box.x -= sw;
52953         }
52954         return box;
52955     },
52956
52957     updateBox : function(box){
52958         if(this.split && !this.collapsed){
52959             var sw = this.split.el.getWidth();
52960             box.width -= sw;
52961             this.split.el.setLeft(box.x);
52962             this.split.el.setTop(box.y);
52963             this.split.el.setHeight(box.height);
52964             box.x += sw;
52965         }
52966         if(this.collapsed){
52967             this.updateBody(null, box.height);
52968         }
52969         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52970     }
52971 });
52972
52973 Roo.WestLayoutRegion = function(mgr, config){
52974     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52975     if(this.split){
52976         this.split.placement = Roo.SplitBar.LEFT;
52977         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52978         this.split.el.addClass("x-layout-split-h");
52979     }
52980     var size = config.initialSize || config.width;
52981     if(typeof size != "undefined"){
52982         this.el.setWidth(size);
52983     }
52984 };
52985 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52986     orientation: Roo.SplitBar.HORIZONTAL,
52987     getBox : function(){
52988         if(this.collapsed){
52989             return this.collapsedEl.getBox();
52990         }
52991         var box = this.el.getBox();
52992         if(this.split){
52993             box.width += this.split.el.getWidth();
52994         }
52995         return box;
52996     },
52997     
52998     updateBox : function(box){
52999         if(this.split && !this.collapsed){
53000             var sw = this.split.el.getWidth();
53001             box.width -= sw;
53002             this.split.el.setLeft(box.x+box.width);
53003             this.split.el.setTop(box.y);
53004             this.split.el.setHeight(box.height);
53005         }
53006         if(this.collapsed){
53007             this.updateBody(null, box.height);
53008         }
53009         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53010     }
53011 });
53012 /*
53013  * Based on:
53014  * Ext JS Library 1.1.1
53015  * Copyright(c) 2006-2007, Ext JS, LLC.
53016  *
53017  * Originally Released Under LGPL - original licence link has changed is not relivant.
53018  *
53019  * Fork - LGPL
53020  * <script type="text/javascript">
53021  */
53022  
53023  
53024 /*
53025  * Private internal class for reading and applying state
53026  */
53027 Roo.LayoutStateManager = function(layout){
53028      // default empty state
53029      this.state = {
53030         north: {},
53031         south: {},
53032         east: {},
53033         west: {}       
53034     };
53035 };
53036
53037 Roo.LayoutStateManager.prototype = {
53038     init : function(layout, provider){
53039         this.provider = provider;
53040         var state = provider.get(layout.id+"-layout-state");
53041         if(state){
53042             var wasUpdating = layout.isUpdating();
53043             if(!wasUpdating){
53044                 layout.beginUpdate();
53045             }
53046             for(var key in state){
53047                 if(typeof state[key] != "function"){
53048                     var rstate = state[key];
53049                     var r = layout.getRegion(key);
53050                     if(r && rstate){
53051                         if(rstate.size){
53052                             r.resizeTo(rstate.size);
53053                         }
53054                         if(rstate.collapsed == true){
53055                             r.collapse(true);
53056                         }else{
53057                             r.expand(null, true);
53058                         }
53059                     }
53060                 }
53061             }
53062             if(!wasUpdating){
53063                 layout.endUpdate();
53064             }
53065             this.state = state; 
53066         }
53067         this.layout = layout;
53068         layout.on("regionresized", this.onRegionResized, this);
53069         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53070         layout.on("regionexpanded", this.onRegionExpanded, this);
53071     },
53072     
53073     storeState : function(){
53074         this.provider.set(this.layout.id+"-layout-state", this.state);
53075     },
53076     
53077     onRegionResized : function(region, newSize){
53078         this.state[region.getPosition()].size = newSize;
53079         this.storeState();
53080     },
53081     
53082     onRegionCollapsed : function(region){
53083         this.state[region.getPosition()].collapsed = true;
53084         this.storeState();
53085     },
53086     
53087     onRegionExpanded : function(region){
53088         this.state[region.getPosition()].collapsed = false;
53089         this.storeState();
53090     }
53091 };/*
53092  * Based on:
53093  * Ext JS Library 1.1.1
53094  * Copyright(c) 2006-2007, Ext JS, LLC.
53095  *
53096  * Originally Released Under LGPL - original licence link has changed is not relivant.
53097  *
53098  * Fork - LGPL
53099  * <script type="text/javascript">
53100  */
53101 /**
53102  * @class Roo.ContentPanel
53103  * @extends Roo.util.Observable
53104  * A basic ContentPanel element.
53105  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53106  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53107  * @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
53108  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53109  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53110  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53111  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53112  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53113  * @cfg {String} title          The title for this panel
53114  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53115  * @cfg {String} url            Calls {@link #setUrl} with this value
53116  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53117  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53118  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53119  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53120
53121  * @constructor
53122  * Create a new ContentPanel.
53123  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53124  * @param {String/Object} config A string to set only the title or a config object
53125  * @param {String} content (optional) Set the HTML content for this panel
53126  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53127  */
53128 Roo.ContentPanel = function(el, config, content){
53129     
53130      
53131     /*
53132     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53133         config = el;
53134         el = Roo.id();
53135     }
53136     if (config && config.parentLayout) { 
53137         el = config.parentLayout.el.createChild(); 
53138     }
53139     */
53140     if(el.autoCreate){ // xtype is available if this is called from factory
53141         config = el;
53142         el = Roo.id();
53143     }
53144     this.el = Roo.get(el);
53145     if(!this.el && config && config.autoCreate){
53146         if(typeof config.autoCreate == "object"){
53147             if(!config.autoCreate.id){
53148                 config.autoCreate.id = config.id||el;
53149             }
53150             this.el = Roo.DomHelper.append(document.body,
53151                         config.autoCreate, true);
53152         }else{
53153             this.el = Roo.DomHelper.append(document.body,
53154                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53155         }
53156     }
53157     this.closable = false;
53158     this.loaded = false;
53159     this.active = false;
53160     if(typeof config == "string"){
53161         this.title = config;
53162     }else{
53163         Roo.apply(this, config);
53164     }
53165     
53166     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53167         this.wrapEl = this.el.wrap();
53168         this.toolbar.container = this.el.insertSibling(false, 'before');
53169         this.toolbar = new Roo.Toolbar(this.toolbar);
53170     }
53171     
53172     // xtype created footer. - not sure if will work as we normally have to render first..
53173     if (this.footer && !this.footer.el && this.footer.xtype) {
53174         if (!this.wrapEl) {
53175             this.wrapEl = this.el.wrap();
53176         }
53177     
53178         this.footer.container = this.wrapEl.createChild();
53179          
53180         this.footer = Roo.factory(this.footer, Roo);
53181         
53182     }
53183     
53184     if(this.resizeEl){
53185         this.resizeEl = Roo.get(this.resizeEl, true);
53186     }else{
53187         this.resizeEl = this.el;
53188     }
53189     // handle view.xtype
53190     
53191  
53192     
53193     
53194     this.addEvents({
53195         /**
53196          * @event activate
53197          * Fires when this panel is activated. 
53198          * @param {Roo.ContentPanel} this
53199          */
53200         "activate" : true,
53201         /**
53202          * @event deactivate
53203          * Fires when this panel is activated. 
53204          * @param {Roo.ContentPanel} this
53205          */
53206         "deactivate" : true,
53207
53208         /**
53209          * @event resize
53210          * Fires when this panel is resized if fitToFrame is true.
53211          * @param {Roo.ContentPanel} this
53212          * @param {Number} width The width after any component adjustments
53213          * @param {Number} height The height after any component adjustments
53214          */
53215         "resize" : true,
53216         
53217          /**
53218          * @event render
53219          * Fires when this tab is created
53220          * @param {Roo.ContentPanel} this
53221          */
53222         "render" : true
53223         
53224         
53225         
53226     });
53227     
53228
53229     
53230     
53231     if(this.autoScroll){
53232         this.resizeEl.setStyle("overflow", "auto");
53233     } else {
53234         // fix randome scrolling
53235         this.el.on('scroll', function() {
53236             Roo.log('fix random scolling');
53237             this.scrollTo('top',0); 
53238         });
53239     }
53240     content = content || this.content;
53241     if(content){
53242         this.setContent(content);
53243     }
53244     if(config && config.url){
53245         this.setUrl(this.url, this.params, this.loadOnce);
53246     }
53247     
53248     
53249     
53250     Roo.ContentPanel.superclass.constructor.call(this);
53251     
53252     if (this.view && typeof(this.view.xtype) != 'undefined') {
53253         this.view.el = this.el.appendChild(document.createElement("div"));
53254         this.view = Roo.factory(this.view); 
53255         this.view.render  &&  this.view.render(false, '');  
53256     }
53257     
53258     
53259     this.fireEvent('render', this);
53260 };
53261
53262 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53263     tabTip:'',
53264     setRegion : function(region){
53265         this.region = region;
53266         if(region){
53267            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53268         }else{
53269            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53270         } 
53271     },
53272     
53273     /**
53274      * Returns the toolbar for this Panel if one was configured. 
53275      * @return {Roo.Toolbar} 
53276      */
53277     getToolbar : function(){
53278         return this.toolbar;
53279     },
53280     
53281     setActiveState : function(active){
53282         this.active = active;
53283         if(!active){
53284             this.fireEvent("deactivate", this);
53285         }else{
53286             this.fireEvent("activate", this);
53287         }
53288     },
53289     /**
53290      * Updates this panel's element
53291      * @param {String} content The new content
53292      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53293     */
53294     setContent : function(content, loadScripts){
53295         this.el.update(content, loadScripts);
53296     },
53297
53298     ignoreResize : function(w, h){
53299         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53300             return true;
53301         }else{
53302             this.lastSize = {width: w, height: h};
53303             return false;
53304         }
53305     },
53306     /**
53307      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53308      * @return {Roo.UpdateManager} The UpdateManager
53309      */
53310     getUpdateManager : function(){
53311         return this.el.getUpdateManager();
53312     },
53313      /**
53314      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53315      * @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:
53316 <pre><code>
53317 panel.load({
53318     url: "your-url.php",
53319     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53320     callback: yourFunction,
53321     scope: yourObject, //(optional scope)
53322     discardUrl: false,
53323     nocache: false,
53324     text: "Loading...",
53325     timeout: 30,
53326     scripts: false
53327 });
53328 </code></pre>
53329      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53330      * 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.
53331      * @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}
53332      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53333      * @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.
53334      * @return {Roo.ContentPanel} this
53335      */
53336     load : function(){
53337         var um = this.el.getUpdateManager();
53338         um.update.apply(um, arguments);
53339         return this;
53340     },
53341
53342
53343     /**
53344      * 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.
53345      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53346      * @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)
53347      * @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)
53348      * @return {Roo.UpdateManager} The UpdateManager
53349      */
53350     setUrl : function(url, params, loadOnce){
53351         if(this.refreshDelegate){
53352             this.removeListener("activate", this.refreshDelegate);
53353         }
53354         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53355         this.on("activate", this.refreshDelegate);
53356         return this.el.getUpdateManager();
53357     },
53358     
53359     _handleRefresh : function(url, params, loadOnce){
53360         if(!loadOnce || !this.loaded){
53361             var updater = this.el.getUpdateManager();
53362             updater.update(url, params, this._setLoaded.createDelegate(this));
53363         }
53364     },
53365     
53366     _setLoaded : function(){
53367         this.loaded = true;
53368     }, 
53369     
53370     /**
53371      * Returns this panel's id
53372      * @return {String} 
53373      */
53374     getId : function(){
53375         return this.el.id;
53376     },
53377     
53378     /** 
53379      * Returns this panel's element - used by regiosn to add.
53380      * @return {Roo.Element} 
53381      */
53382     getEl : function(){
53383         return this.wrapEl || this.el;
53384     },
53385     
53386     adjustForComponents : function(width, height)
53387     {
53388         //Roo.log('adjustForComponents ');
53389         if(this.resizeEl != this.el){
53390             width -= this.el.getFrameWidth('lr');
53391             height -= this.el.getFrameWidth('tb');
53392         }
53393         if(this.toolbar){
53394             var te = this.toolbar.getEl();
53395             height -= te.getHeight();
53396             te.setWidth(width);
53397         }
53398         if(this.footer){
53399             var te = this.footer.getEl();
53400             Roo.log("footer:" + te.getHeight());
53401             
53402             height -= te.getHeight();
53403             te.setWidth(width);
53404         }
53405         
53406         
53407         if(this.adjustments){
53408             width += this.adjustments[0];
53409             height += this.adjustments[1];
53410         }
53411         return {"width": width, "height": height};
53412     },
53413     
53414     setSize : function(width, height){
53415         if(this.fitToFrame && !this.ignoreResize(width, height)){
53416             if(this.fitContainer && this.resizeEl != this.el){
53417                 this.el.setSize(width, height);
53418             }
53419             var size = this.adjustForComponents(width, height);
53420             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53421             this.fireEvent('resize', this, size.width, size.height);
53422         }
53423     },
53424     
53425     /**
53426      * Returns this panel's title
53427      * @return {String} 
53428      */
53429     getTitle : function(){
53430         return this.title;
53431     },
53432     
53433     /**
53434      * Set this panel's title
53435      * @param {String} title
53436      */
53437     setTitle : function(title){
53438         this.title = title;
53439         if(this.region){
53440             this.region.updatePanelTitle(this, title);
53441         }
53442     },
53443     
53444     /**
53445      * Returns true is this panel was configured to be closable
53446      * @return {Boolean} 
53447      */
53448     isClosable : function(){
53449         return this.closable;
53450     },
53451     
53452     beforeSlide : function(){
53453         this.el.clip();
53454         this.resizeEl.clip();
53455     },
53456     
53457     afterSlide : function(){
53458         this.el.unclip();
53459         this.resizeEl.unclip();
53460     },
53461     
53462     /**
53463      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53464      *   Will fail silently if the {@link #setUrl} method has not been called.
53465      *   This does not activate the panel, just updates its content.
53466      */
53467     refresh : function(){
53468         if(this.refreshDelegate){
53469            this.loaded = false;
53470            this.refreshDelegate();
53471         }
53472     },
53473     
53474     /**
53475      * Destroys this panel
53476      */
53477     destroy : function(){
53478         this.el.removeAllListeners();
53479         var tempEl = document.createElement("span");
53480         tempEl.appendChild(this.el.dom);
53481         tempEl.innerHTML = "";
53482         this.el.remove();
53483         this.el = null;
53484     },
53485     
53486     /**
53487      * form - if the content panel contains a form - this is a reference to it.
53488      * @type {Roo.form.Form}
53489      */
53490     form : false,
53491     /**
53492      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53493      *    This contains a reference to it.
53494      * @type {Roo.View}
53495      */
53496     view : false,
53497     
53498       /**
53499      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53500      * <pre><code>
53501
53502 layout.addxtype({
53503        xtype : 'Form',
53504        items: [ .... ]
53505    }
53506 );
53507
53508 </code></pre>
53509      * @param {Object} cfg Xtype definition of item to add.
53510      */
53511     
53512     addxtype : function(cfg) {
53513         // add form..
53514         if (cfg.xtype.match(/^Form$/)) {
53515             
53516             var el;
53517             //if (this.footer) {
53518             //    el = this.footer.container.insertSibling(false, 'before');
53519             //} else {
53520                 el = this.el.createChild();
53521             //}
53522
53523             this.form = new  Roo.form.Form(cfg);
53524             
53525             
53526             if ( this.form.allItems.length) {
53527                 this.form.render(el.dom);
53528             }
53529             return this.form;
53530         }
53531         // should only have one of theses..
53532         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53533             // views.. should not be just added - used named prop 'view''
53534             
53535             cfg.el = this.el.appendChild(document.createElement("div"));
53536             // factory?
53537             
53538             var ret = new Roo.factory(cfg);
53539              
53540              ret.render && ret.render(false, ''); // render blank..
53541             this.view = ret;
53542             return ret;
53543         }
53544         return false;
53545     }
53546 });
53547
53548 /**
53549  * @class Roo.GridPanel
53550  * @extends Roo.ContentPanel
53551  * @constructor
53552  * Create a new GridPanel.
53553  * @param {Roo.grid.Grid} grid The grid for this panel
53554  * @param {String/Object} config A string to set only the panel's title, or a config object
53555  */
53556 Roo.GridPanel = function(grid, config){
53557     
53558   
53559     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53560         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53561         
53562     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53563     
53564     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53565     
53566     if(this.toolbar){
53567         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53568     }
53569     // xtype created footer. - not sure if will work as we normally have to render first..
53570     if (this.footer && !this.footer.el && this.footer.xtype) {
53571         
53572         this.footer.container = this.grid.getView().getFooterPanel(true);
53573         this.footer.dataSource = this.grid.dataSource;
53574         this.footer = Roo.factory(this.footer, Roo);
53575         
53576     }
53577     
53578     grid.monitorWindowResize = false; // turn off autosizing
53579     grid.autoHeight = false;
53580     grid.autoWidth = false;
53581     this.grid = grid;
53582     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53583 };
53584
53585 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53586     getId : function(){
53587         return this.grid.id;
53588     },
53589     
53590     /**
53591      * Returns the grid for this panel
53592      * @return {Roo.grid.Grid} 
53593      */
53594     getGrid : function(){
53595         return this.grid;    
53596     },
53597     
53598     setSize : function(width, height){
53599         if(!this.ignoreResize(width, height)){
53600             var grid = this.grid;
53601             var size = this.adjustForComponents(width, height);
53602             grid.getGridEl().setSize(size.width, size.height);
53603             grid.autoSize();
53604         }
53605     },
53606     
53607     beforeSlide : function(){
53608         this.grid.getView().scroller.clip();
53609     },
53610     
53611     afterSlide : function(){
53612         this.grid.getView().scroller.unclip();
53613     },
53614     
53615     destroy : function(){
53616         this.grid.destroy();
53617         delete this.grid;
53618         Roo.GridPanel.superclass.destroy.call(this); 
53619     }
53620 });
53621
53622
53623 /**
53624  * @class Roo.NestedLayoutPanel
53625  * @extends Roo.ContentPanel
53626  * @constructor
53627  * Create a new NestedLayoutPanel.
53628  * 
53629  * 
53630  * @param {Roo.BorderLayout} layout The layout for this panel
53631  * @param {String/Object} config A string to set only the title or a config object
53632  */
53633 Roo.NestedLayoutPanel = function(layout, config)
53634 {
53635     // construct with only one argument..
53636     /* FIXME - implement nicer consturctors
53637     if (layout.layout) {
53638         config = layout;
53639         layout = config.layout;
53640         delete config.layout;
53641     }
53642     if (layout.xtype && !layout.getEl) {
53643         // then layout needs constructing..
53644         layout = Roo.factory(layout, Roo);
53645     }
53646     */
53647     
53648     
53649     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53650     
53651     layout.monitorWindowResize = false; // turn off autosizing
53652     this.layout = layout;
53653     this.layout.getEl().addClass("x-layout-nested-layout");
53654     
53655     
53656     
53657     
53658 };
53659
53660 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53661
53662     setSize : function(width, height){
53663         if(!this.ignoreResize(width, height)){
53664             var size = this.adjustForComponents(width, height);
53665             var el = this.layout.getEl();
53666             el.setSize(size.width, size.height);
53667             var touch = el.dom.offsetWidth;
53668             this.layout.layout();
53669             // ie requires a double layout on the first pass
53670             if(Roo.isIE && !this.initialized){
53671                 this.initialized = true;
53672                 this.layout.layout();
53673             }
53674         }
53675     },
53676     
53677     // activate all subpanels if not currently active..
53678     
53679     setActiveState : function(active){
53680         this.active = active;
53681         if(!active){
53682             this.fireEvent("deactivate", this);
53683             return;
53684         }
53685         
53686         this.fireEvent("activate", this);
53687         // not sure if this should happen before or after..
53688         if (!this.layout) {
53689             return; // should not happen..
53690         }
53691         var reg = false;
53692         for (var r in this.layout.regions) {
53693             reg = this.layout.getRegion(r);
53694             if (reg.getActivePanel()) {
53695                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53696                 reg.setActivePanel(reg.getActivePanel());
53697                 continue;
53698             }
53699             if (!reg.panels.length) {
53700                 continue;
53701             }
53702             reg.showPanel(reg.getPanel(0));
53703         }
53704         
53705         
53706         
53707         
53708     },
53709     
53710     /**
53711      * Returns the nested BorderLayout for this panel
53712      * @return {Roo.BorderLayout} 
53713      */
53714     getLayout : function(){
53715         return this.layout;
53716     },
53717     
53718      /**
53719      * Adds a xtype elements to the layout of the nested panel
53720      * <pre><code>
53721
53722 panel.addxtype({
53723        xtype : 'ContentPanel',
53724        region: 'west',
53725        items: [ .... ]
53726    }
53727 );
53728
53729 panel.addxtype({
53730         xtype : 'NestedLayoutPanel',
53731         region: 'west',
53732         layout: {
53733            center: { },
53734            west: { }   
53735         },
53736         items : [ ... list of content panels or nested layout panels.. ]
53737    }
53738 );
53739 </code></pre>
53740      * @param {Object} cfg Xtype definition of item to add.
53741      */
53742     addxtype : function(cfg) {
53743         return this.layout.addxtype(cfg);
53744     
53745     }
53746 });
53747
53748 Roo.ScrollPanel = function(el, config, content){
53749     config = config || {};
53750     config.fitToFrame = true;
53751     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53752     
53753     this.el.dom.style.overflow = "hidden";
53754     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53755     this.el.removeClass("x-layout-inactive-content");
53756     this.el.on("mousewheel", this.onWheel, this);
53757
53758     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53759     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53760     up.unselectable(); down.unselectable();
53761     up.on("click", this.scrollUp, this);
53762     down.on("click", this.scrollDown, this);
53763     up.addClassOnOver("x-scroller-btn-over");
53764     down.addClassOnOver("x-scroller-btn-over");
53765     up.addClassOnClick("x-scroller-btn-click");
53766     down.addClassOnClick("x-scroller-btn-click");
53767     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53768
53769     this.resizeEl = this.el;
53770     this.el = wrap; this.up = up; this.down = down;
53771 };
53772
53773 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53774     increment : 100,
53775     wheelIncrement : 5,
53776     scrollUp : function(){
53777         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53778     },
53779
53780     scrollDown : function(){
53781         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53782     },
53783
53784     afterScroll : function(){
53785         var el = this.resizeEl;
53786         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53787         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53788         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53789     },
53790
53791     setSize : function(){
53792         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53793         this.afterScroll();
53794     },
53795
53796     onWheel : function(e){
53797         var d = e.getWheelDelta();
53798         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53799         this.afterScroll();
53800         e.stopEvent();
53801     },
53802
53803     setContent : function(content, loadScripts){
53804         this.resizeEl.update(content, loadScripts);
53805     }
53806
53807 });
53808
53809
53810
53811
53812
53813
53814
53815
53816
53817 /**
53818  * @class Roo.TreePanel
53819  * @extends Roo.ContentPanel
53820  * @constructor
53821  * Create a new TreePanel. - defaults to fit/scoll contents.
53822  * @param {String/Object} config A string to set only the panel's title, or a config object
53823  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53824  */
53825 Roo.TreePanel = function(config){
53826     var el = config.el;
53827     var tree = config.tree;
53828     delete config.tree; 
53829     delete config.el; // hopefull!
53830     
53831     // wrapper for IE7 strict & safari scroll issue
53832     
53833     var treeEl = el.createChild();
53834     config.resizeEl = treeEl;
53835     
53836     
53837     
53838     Roo.TreePanel.superclass.constructor.call(this, el, config);
53839  
53840  
53841     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53842     //console.log(tree);
53843     this.on('activate', function()
53844     {
53845         if (this.tree.rendered) {
53846             return;
53847         }
53848         //console.log('render tree');
53849         this.tree.render();
53850     });
53851     // this should not be needed.. - it's actually the 'el' that resizes?
53852     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53853     
53854     //this.on('resize',  function (cp, w, h) {
53855     //        this.tree.innerCt.setWidth(w);
53856     //        this.tree.innerCt.setHeight(h);
53857     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53858     //});
53859
53860         
53861     
53862 };
53863
53864 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53865     fitToFrame : true,
53866     autoScroll : true
53867 });
53868
53869
53870
53871
53872
53873
53874
53875
53876
53877
53878
53879 /*
53880  * Based on:
53881  * Ext JS Library 1.1.1
53882  * Copyright(c) 2006-2007, Ext JS, LLC.
53883  *
53884  * Originally Released Under LGPL - original licence link has changed is not relivant.
53885  *
53886  * Fork - LGPL
53887  * <script type="text/javascript">
53888  */
53889  
53890
53891 /**
53892  * @class Roo.ReaderLayout
53893  * @extends Roo.BorderLayout
53894  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53895  * center region containing two nested regions (a top one for a list view and one for item preview below),
53896  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53897  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53898  * expedites the setup of the overall layout and regions for this common application style.
53899  * Example:
53900  <pre><code>
53901 var reader = new Roo.ReaderLayout();
53902 var CP = Roo.ContentPanel;  // shortcut for adding
53903
53904 reader.beginUpdate();
53905 reader.add("north", new CP("north", "North"));
53906 reader.add("west", new CP("west", {title: "West"}));
53907 reader.add("east", new CP("east", {title: "East"}));
53908
53909 reader.regions.listView.add(new CP("listView", "List"));
53910 reader.regions.preview.add(new CP("preview", "Preview"));
53911 reader.endUpdate();
53912 </code></pre>
53913 * @constructor
53914 * Create a new ReaderLayout
53915 * @param {Object} config Configuration options
53916 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53917 * document.body if omitted)
53918 */
53919 Roo.ReaderLayout = function(config, renderTo){
53920     var c = config || {size:{}};
53921     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53922         north: c.north !== false ? Roo.apply({
53923             split:false,
53924             initialSize: 32,
53925             titlebar: false
53926         }, c.north) : false,
53927         west: c.west !== false ? Roo.apply({
53928             split:true,
53929             initialSize: 200,
53930             minSize: 175,
53931             maxSize: 400,
53932             titlebar: true,
53933             collapsible: true,
53934             animate: true,
53935             margins:{left:5,right:0,bottom:5,top:5},
53936             cmargins:{left:5,right:5,bottom:5,top:5}
53937         }, c.west) : false,
53938         east: c.east !== false ? Roo.apply({
53939             split:true,
53940             initialSize: 200,
53941             minSize: 175,
53942             maxSize: 400,
53943             titlebar: true,
53944             collapsible: true,
53945             animate: true,
53946             margins:{left:0,right:5,bottom:5,top:5},
53947             cmargins:{left:5,right:5,bottom:5,top:5}
53948         }, c.east) : false,
53949         center: Roo.apply({
53950             tabPosition: 'top',
53951             autoScroll:false,
53952             closeOnTab: true,
53953             titlebar:false,
53954             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53955         }, c.center)
53956     });
53957
53958     this.el.addClass('x-reader');
53959
53960     this.beginUpdate();
53961
53962     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53963         south: c.preview !== false ? Roo.apply({
53964             split:true,
53965             initialSize: 200,
53966             minSize: 100,
53967             autoScroll:true,
53968             collapsible:true,
53969             titlebar: true,
53970             cmargins:{top:5,left:0, right:0, bottom:0}
53971         }, c.preview) : false,
53972         center: Roo.apply({
53973             autoScroll:false,
53974             titlebar:false,
53975             minHeight:200
53976         }, c.listView)
53977     });
53978     this.add('center', new Roo.NestedLayoutPanel(inner,
53979             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53980
53981     this.endUpdate();
53982
53983     this.regions.preview = inner.getRegion('south');
53984     this.regions.listView = inner.getRegion('center');
53985 };
53986
53987 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53988  * Based on:
53989  * Ext JS Library 1.1.1
53990  * Copyright(c) 2006-2007, Ext JS, LLC.
53991  *
53992  * Originally Released Under LGPL - original licence link has changed is not relivant.
53993  *
53994  * Fork - LGPL
53995  * <script type="text/javascript">
53996  */
53997  
53998 /**
53999  * @class Roo.grid.Grid
54000  * @extends Roo.util.Observable
54001  * This class represents the primary interface of a component based grid control.
54002  * <br><br>Usage:<pre><code>
54003  var grid = new Roo.grid.Grid("my-container-id", {
54004      ds: myDataStore,
54005      cm: myColModel,
54006      selModel: mySelectionModel,
54007      autoSizeColumns: true,
54008      monitorWindowResize: false,
54009      trackMouseOver: true
54010  });
54011  // set any options
54012  grid.render();
54013  * </code></pre>
54014  * <b>Common Problems:</b><br/>
54015  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54016  * element will correct this<br/>
54017  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54018  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54019  * are unpredictable.<br/>
54020  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54021  * grid to calculate dimensions/offsets.<br/>
54022   * @constructor
54023  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54024  * The container MUST have some type of size defined for the grid to fill. The container will be
54025  * automatically set to position relative if it isn't already.
54026  * @param {Object} config A config object that sets properties on this grid.
54027  */
54028 Roo.grid.Grid = function(container, config){
54029         // initialize the container
54030         this.container = Roo.get(container);
54031         this.container.update("");
54032         this.container.setStyle("overflow", "hidden");
54033     this.container.addClass('x-grid-container');
54034
54035     this.id = this.container.id;
54036
54037     Roo.apply(this, config);
54038     // check and correct shorthanded configs
54039     if(this.ds){
54040         this.dataSource = this.ds;
54041         delete this.ds;
54042     }
54043     if(this.cm){
54044         this.colModel = this.cm;
54045         delete this.cm;
54046     }
54047     if(this.sm){
54048         this.selModel = this.sm;
54049         delete this.sm;
54050     }
54051
54052     if (this.selModel) {
54053         this.selModel = Roo.factory(this.selModel, Roo.grid);
54054         this.sm = this.selModel;
54055         this.sm.xmodule = this.xmodule || false;
54056     }
54057     if (typeof(this.colModel.config) == 'undefined') {
54058         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54059         this.cm = this.colModel;
54060         this.cm.xmodule = this.xmodule || false;
54061     }
54062     if (this.dataSource) {
54063         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54064         this.ds = this.dataSource;
54065         this.ds.xmodule = this.xmodule || false;
54066          
54067     }
54068     
54069     
54070     
54071     if(this.width){
54072         this.container.setWidth(this.width);
54073     }
54074
54075     if(this.height){
54076         this.container.setHeight(this.height);
54077     }
54078     /** @private */
54079         this.addEvents({
54080         // raw events
54081         /**
54082          * @event click
54083          * The raw click event for the entire grid.
54084          * @param {Roo.EventObject} e
54085          */
54086         "click" : true,
54087         /**
54088          * @event dblclick
54089          * The raw dblclick event for the entire grid.
54090          * @param {Roo.EventObject} e
54091          */
54092         "dblclick" : true,
54093         /**
54094          * @event contextmenu
54095          * The raw contextmenu event for the entire grid.
54096          * @param {Roo.EventObject} e
54097          */
54098         "contextmenu" : true,
54099         /**
54100          * @event mousedown
54101          * The raw mousedown event for the entire grid.
54102          * @param {Roo.EventObject} e
54103          */
54104         "mousedown" : true,
54105         /**
54106          * @event mouseup
54107          * The raw mouseup event for the entire grid.
54108          * @param {Roo.EventObject} e
54109          */
54110         "mouseup" : true,
54111         /**
54112          * @event mouseover
54113          * The raw mouseover event for the entire grid.
54114          * @param {Roo.EventObject} e
54115          */
54116         "mouseover" : true,
54117         /**
54118          * @event mouseout
54119          * The raw mouseout event for the entire grid.
54120          * @param {Roo.EventObject} e
54121          */
54122         "mouseout" : true,
54123         /**
54124          * @event keypress
54125          * The raw keypress event for the entire grid.
54126          * @param {Roo.EventObject} e
54127          */
54128         "keypress" : true,
54129         /**
54130          * @event keydown
54131          * The raw keydown event for the entire grid.
54132          * @param {Roo.EventObject} e
54133          */
54134         "keydown" : true,
54135
54136         // custom events
54137
54138         /**
54139          * @event cellclick
54140          * Fires when a cell is clicked
54141          * @param {Grid} this
54142          * @param {Number} rowIndex
54143          * @param {Number} columnIndex
54144          * @param {Roo.EventObject} e
54145          */
54146         "cellclick" : true,
54147         /**
54148          * @event celldblclick
54149          * Fires when a cell is double clicked
54150          * @param {Grid} this
54151          * @param {Number} rowIndex
54152          * @param {Number} columnIndex
54153          * @param {Roo.EventObject} e
54154          */
54155         "celldblclick" : true,
54156         /**
54157          * @event rowclick
54158          * Fires when a row is clicked
54159          * @param {Grid} this
54160          * @param {Number} rowIndex
54161          * @param {Roo.EventObject} e
54162          */
54163         "rowclick" : true,
54164         /**
54165          * @event rowdblclick
54166          * Fires when a row is double clicked
54167          * @param {Grid} this
54168          * @param {Number} rowIndex
54169          * @param {Roo.EventObject} e
54170          */
54171         "rowdblclick" : true,
54172         /**
54173          * @event headerclick
54174          * Fires when a header is clicked
54175          * @param {Grid} this
54176          * @param {Number} columnIndex
54177          * @param {Roo.EventObject} e
54178          */
54179         "headerclick" : true,
54180         /**
54181          * @event headerdblclick
54182          * Fires when a header cell is double clicked
54183          * @param {Grid} this
54184          * @param {Number} columnIndex
54185          * @param {Roo.EventObject} e
54186          */
54187         "headerdblclick" : true,
54188         /**
54189          * @event rowcontextmenu
54190          * Fires when a row is right clicked
54191          * @param {Grid} this
54192          * @param {Number} rowIndex
54193          * @param {Roo.EventObject} e
54194          */
54195         "rowcontextmenu" : true,
54196         /**
54197          * @event cellcontextmenu
54198          * Fires when a cell is right clicked
54199          * @param {Grid} this
54200          * @param {Number} rowIndex
54201          * @param {Number} cellIndex
54202          * @param {Roo.EventObject} e
54203          */
54204          "cellcontextmenu" : true,
54205         /**
54206          * @event headercontextmenu
54207          * Fires when a header is right clicked
54208          * @param {Grid} this
54209          * @param {Number} columnIndex
54210          * @param {Roo.EventObject} e
54211          */
54212         "headercontextmenu" : true,
54213         /**
54214          * @event bodyscroll
54215          * Fires when the body element is scrolled
54216          * @param {Number} scrollLeft
54217          * @param {Number} scrollTop
54218          */
54219         "bodyscroll" : true,
54220         /**
54221          * @event columnresize
54222          * Fires when the user resizes a column
54223          * @param {Number} columnIndex
54224          * @param {Number} newSize
54225          */
54226         "columnresize" : true,
54227         /**
54228          * @event columnmove
54229          * Fires when the user moves a column
54230          * @param {Number} oldIndex
54231          * @param {Number} newIndex
54232          */
54233         "columnmove" : true,
54234         /**
54235          * @event startdrag
54236          * Fires when row(s) start being dragged
54237          * @param {Grid} this
54238          * @param {Roo.GridDD} dd The drag drop object
54239          * @param {event} e The raw browser event
54240          */
54241         "startdrag" : true,
54242         /**
54243          * @event enddrag
54244          * Fires when a drag operation is complete
54245          * @param {Grid} this
54246          * @param {Roo.GridDD} dd The drag drop object
54247          * @param {event} e The raw browser event
54248          */
54249         "enddrag" : true,
54250         /**
54251          * @event dragdrop
54252          * Fires when dragged row(s) are dropped on a valid DD target
54253          * @param {Grid} this
54254          * @param {Roo.GridDD} dd The drag drop object
54255          * @param {String} targetId The target drag drop object
54256          * @param {event} e The raw browser event
54257          */
54258         "dragdrop" : true,
54259         /**
54260          * @event dragover
54261          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54262          * @param {Grid} this
54263          * @param {Roo.GridDD} dd The drag drop object
54264          * @param {String} targetId The target drag drop object
54265          * @param {event} e The raw browser event
54266          */
54267         "dragover" : true,
54268         /**
54269          * @event dragenter
54270          *  Fires when the dragged row(s) first cross another DD target while being dragged
54271          * @param {Grid} this
54272          * @param {Roo.GridDD} dd The drag drop object
54273          * @param {String} targetId The target drag drop object
54274          * @param {event} e The raw browser event
54275          */
54276         "dragenter" : true,
54277         /**
54278          * @event dragout
54279          * Fires when the dragged row(s) leave another DD target while being dragged
54280          * @param {Grid} this
54281          * @param {Roo.GridDD} dd The drag drop object
54282          * @param {String} targetId The target drag drop object
54283          * @param {event} e The raw browser event
54284          */
54285         "dragout" : true,
54286         /**
54287          * @event rowclass
54288          * Fires when a row is rendered, so you can change add a style to it.
54289          * @param {GridView} gridview   The grid view
54290          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54291          */
54292         'rowclass' : true,
54293
54294         /**
54295          * @event render
54296          * Fires when the grid is rendered
54297          * @param {Grid} grid
54298          */
54299         'render' : true
54300     });
54301
54302     Roo.grid.Grid.superclass.constructor.call(this);
54303 };
54304 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54305     
54306     /**
54307      * @cfg {String} ddGroup - drag drop group.
54308      */
54309
54310     /**
54311      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54312      */
54313     minColumnWidth : 25,
54314
54315     /**
54316      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54317      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54318      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54319      */
54320     autoSizeColumns : false,
54321
54322     /**
54323      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54324      */
54325     autoSizeHeaders : true,
54326
54327     /**
54328      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54329      */
54330     monitorWindowResize : true,
54331
54332     /**
54333      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54334      * rows measured to get a columns size. Default is 0 (all rows).
54335      */
54336     maxRowsToMeasure : 0,
54337
54338     /**
54339      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54340      */
54341     trackMouseOver : true,
54342
54343     /**
54344     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54345     */
54346     
54347     /**
54348     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54349     */
54350     enableDragDrop : false,
54351     
54352     /**
54353     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54354     */
54355     enableColumnMove : true,
54356     
54357     /**
54358     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54359     */
54360     enableColumnHide : true,
54361     
54362     /**
54363     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54364     */
54365     enableRowHeightSync : false,
54366     
54367     /**
54368     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54369     */
54370     stripeRows : true,
54371     
54372     /**
54373     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54374     */
54375     autoHeight : false,
54376
54377     /**
54378      * @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.
54379      */
54380     autoExpandColumn : false,
54381
54382     /**
54383     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54384     * Default is 50.
54385     */
54386     autoExpandMin : 50,
54387
54388     /**
54389     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54390     */
54391     autoExpandMax : 1000,
54392
54393     /**
54394     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54395     */
54396     view : null,
54397
54398     /**
54399     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54400     */
54401     loadMask : false,
54402     /**
54403     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54404     */
54405     dropTarget: false,
54406     
54407    
54408     
54409     // private
54410     rendered : false,
54411
54412     /**
54413     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54414     * of a fixed width. Default is false.
54415     */
54416     /**
54417     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54418     */
54419     /**
54420      * Called once after all setup has been completed and the grid is ready to be rendered.
54421      * @return {Roo.grid.Grid} this
54422      */
54423     render : function()
54424     {
54425         var c = this.container;
54426         // try to detect autoHeight/width mode
54427         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54428             this.autoHeight = true;
54429         }
54430         var view = this.getView();
54431         view.init(this);
54432
54433         c.on("click", this.onClick, this);
54434         c.on("dblclick", this.onDblClick, this);
54435         c.on("contextmenu", this.onContextMenu, this);
54436         c.on("keydown", this.onKeyDown, this);
54437         if (Roo.isTouch) {
54438             c.on("touchstart", this.onTouchStart, this);
54439         }
54440
54441         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54442
54443         this.getSelectionModel().init(this);
54444
54445         view.render();
54446
54447         if(this.loadMask){
54448             this.loadMask = new Roo.LoadMask(this.container,
54449                     Roo.apply({store:this.dataSource}, this.loadMask));
54450         }
54451         
54452         
54453         if (this.toolbar && this.toolbar.xtype) {
54454             this.toolbar.container = this.getView().getHeaderPanel(true);
54455             this.toolbar = new Roo.Toolbar(this.toolbar);
54456         }
54457         if (this.footer && this.footer.xtype) {
54458             this.footer.dataSource = this.getDataSource();
54459             this.footer.container = this.getView().getFooterPanel(true);
54460             this.footer = Roo.factory(this.footer, Roo);
54461         }
54462         if (this.dropTarget && this.dropTarget.xtype) {
54463             delete this.dropTarget.xtype;
54464             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54465         }
54466         
54467         
54468         this.rendered = true;
54469         this.fireEvent('render', this);
54470         return this;
54471     },
54472
54473         /**
54474          * Reconfigures the grid to use a different Store and Column Model.
54475          * The View will be bound to the new objects and refreshed.
54476          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54477          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54478          */
54479     reconfigure : function(dataSource, colModel){
54480         if(this.loadMask){
54481             this.loadMask.destroy();
54482             this.loadMask = new Roo.LoadMask(this.container,
54483                     Roo.apply({store:dataSource}, this.loadMask));
54484         }
54485         this.view.bind(dataSource, colModel);
54486         this.dataSource = dataSource;
54487         this.colModel = colModel;
54488         this.view.refresh(true);
54489     },
54490
54491     // private
54492     onKeyDown : function(e){
54493         this.fireEvent("keydown", e);
54494     },
54495
54496     /**
54497      * Destroy this grid.
54498      * @param {Boolean} removeEl True to remove the element
54499      */
54500     destroy : function(removeEl, keepListeners){
54501         if(this.loadMask){
54502             this.loadMask.destroy();
54503         }
54504         var c = this.container;
54505         c.removeAllListeners();
54506         this.view.destroy();
54507         this.colModel.purgeListeners();
54508         if(!keepListeners){
54509             this.purgeListeners();
54510         }
54511         c.update("");
54512         if(removeEl === true){
54513             c.remove();
54514         }
54515     },
54516
54517     // private
54518     processEvent : function(name, e){
54519         // does this fire select???
54520         //Roo.log('grid:processEvent '  + name);
54521         
54522         if (name != 'touchstart' ) {
54523             this.fireEvent(name, e);    
54524         }
54525         
54526         var t = e.getTarget();
54527         var v = this.view;
54528         var header = v.findHeaderIndex(t);
54529         if(header !== false){
54530             var ename = name == 'touchstart' ? 'click' : name;
54531              
54532             this.fireEvent("header" + ename, this, header, e);
54533         }else{
54534             var row = v.findRowIndex(t);
54535             var cell = v.findCellIndex(t);
54536             if (name == 'touchstart') {
54537                 // first touch is always a click.
54538                 // hopefull this happens after selection is updated.?
54539                 name = false;
54540                 
54541                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54542                     var cs = this.selModel.getSelectedCell();
54543                     if (row == cs[0] && cell == cs[1]){
54544                         name = 'dblclick';
54545                     }
54546                 }
54547                 if (typeof(this.selModel.getSelections) != 'undefined') {
54548                     var cs = this.selModel.getSelections();
54549                     var ds = this.dataSource;
54550                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54551                         name = 'dblclick';
54552                     }
54553                 }
54554                 if (!name) {
54555                     return;
54556                 }
54557             }
54558             
54559             
54560             if(row !== false){
54561                 this.fireEvent("row" + name, this, row, e);
54562                 if(cell !== false){
54563                     this.fireEvent("cell" + name, this, row, cell, e);
54564                 }
54565             }
54566         }
54567     },
54568
54569     // private
54570     onClick : function(e){
54571         this.processEvent("click", e);
54572     },
54573    // private
54574     onTouchStart : function(e){
54575         this.processEvent("touchstart", e);
54576     },
54577
54578     // private
54579     onContextMenu : function(e, t){
54580         this.processEvent("contextmenu", e);
54581     },
54582
54583     // private
54584     onDblClick : function(e){
54585         this.processEvent("dblclick", e);
54586     },
54587
54588     // private
54589     walkCells : function(row, col, step, fn, scope){
54590         var cm = this.colModel, clen = cm.getColumnCount();
54591         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54592         if(step < 0){
54593             if(col < 0){
54594                 row--;
54595                 first = false;
54596             }
54597             while(row >= 0){
54598                 if(!first){
54599                     col = clen-1;
54600                 }
54601                 first = false;
54602                 while(col >= 0){
54603                     if(fn.call(scope || this, row, col, cm) === true){
54604                         return [row, col];
54605                     }
54606                     col--;
54607                 }
54608                 row--;
54609             }
54610         } else {
54611             if(col >= clen){
54612                 row++;
54613                 first = false;
54614             }
54615             while(row < rlen){
54616                 if(!first){
54617                     col = 0;
54618                 }
54619                 first = false;
54620                 while(col < clen){
54621                     if(fn.call(scope || this, row, col, cm) === true){
54622                         return [row, col];
54623                     }
54624                     col++;
54625                 }
54626                 row++;
54627             }
54628         }
54629         return null;
54630     },
54631
54632     // private
54633     getSelections : function(){
54634         return this.selModel.getSelections();
54635     },
54636
54637     /**
54638      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54639      * but if manual update is required this method will initiate it.
54640      */
54641     autoSize : function(){
54642         if(this.rendered){
54643             this.view.layout();
54644             if(this.view.adjustForScroll){
54645                 this.view.adjustForScroll();
54646             }
54647         }
54648     },
54649
54650     /**
54651      * Returns the grid's underlying element.
54652      * @return {Element} The element
54653      */
54654     getGridEl : function(){
54655         return this.container;
54656     },
54657
54658     // private for compatibility, overridden by editor grid
54659     stopEditing : function(){},
54660
54661     /**
54662      * Returns the grid's SelectionModel.
54663      * @return {SelectionModel}
54664      */
54665     getSelectionModel : function(){
54666         if(!this.selModel){
54667             this.selModel = new Roo.grid.RowSelectionModel();
54668         }
54669         return this.selModel;
54670     },
54671
54672     /**
54673      * Returns the grid's DataSource.
54674      * @return {DataSource}
54675      */
54676     getDataSource : function(){
54677         return this.dataSource;
54678     },
54679
54680     /**
54681      * Returns the grid's ColumnModel.
54682      * @return {ColumnModel}
54683      */
54684     getColumnModel : function(){
54685         return this.colModel;
54686     },
54687
54688     /**
54689      * Returns the grid's GridView object.
54690      * @return {GridView}
54691      */
54692     getView : function(){
54693         if(!this.view){
54694             this.view = new Roo.grid.GridView(this.viewConfig);
54695         }
54696         return this.view;
54697     },
54698     /**
54699      * Called to get grid's drag proxy text, by default returns this.ddText.
54700      * @return {String}
54701      */
54702     getDragDropText : function(){
54703         var count = this.selModel.getCount();
54704         return String.format(this.ddText, count, count == 1 ? '' : 's');
54705     }
54706 });
54707 /**
54708  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54709  * %0 is replaced with the number of selected rows.
54710  * @type String
54711  */
54712 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54713  * Based on:
54714  * Ext JS Library 1.1.1
54715  * Copyright(c) 2006-2007, Ext JS, LLC.
54716  *
54717  * Originally Released Under LGPL - original licence link has changed is not relivant.
54718  *
54719  * Fork - LGPL
54720  * <script type="text/javascript">
54721  */
54722  
54723 Roo.grid.AbstractGridView = function(){
54724         this.grid = null;
54725         
54726         this.events = {
54727             "beforerowremoved" : true,
54728             "beforerowsinserted" : true,
54729             "beforerefresh" : true,
54730             "rowremoved" : true,
54731             "rowsinserted" : true,
54732             "rowupdated" : true,
54733             "refresh" : true
54734         };
54735     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54736 };
54737
54738 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54739     rowClass : "x-grid-row",
54740     cellClass : "x-grid-cell",
54741     tdClass : "x-grid-td",
54742     hdClass : "x-grid-hd",
54743     splitClass : "x-grid-hd-split",
54744     
54745     init: function(grid){
54746         this.grid = grid;
54747                 var cid = this.grid.getGridEl().id;
54748         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54749         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54750         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54751         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54752         },
54753         
54754     getColumnRenderers : function(){
54755         var renderers = [];
54756         var cm = this.grid.colModel;
54757         var colCount = cm.getColumnCount();
54758         for(var i = 0; i < colCount; i++){
54759             renderers[i] = cm.getRenderer(i);
54760         }
54761         return renderers;
54762     },
54763     
54764     getColumnIds : function(){
54765         var ids = [];
54766         var cm = this.grid.colModel;
54767         var colCount = cm.getColumnCount();
54768         for(var i = 0; i < colCount; i++){
54769             ids[i] = cm.getColumnId(i);
54770         }
54771         return ids;
54772     },
54773     
54774     getDataIndexes : function(){
54775         if(!this.indexMap){
54776             this.indexMap = this.buildIndexMap();
54777         }
54778         return this.indexMap.colToData;
54779     },
54780     
54781     getColumnIndexByDataIndex : function(dataIndex){
54782         if(!this.indexMap){
54783             this.indexMap = this.buildIndexMap();
54784         }
54785         return this.indexMap.dataToCol[dataIndex];
54786     },
54787     
54788     /**
54789      * Set a css style for a column dynamically. 
54790      * @param {Number} colIndex The index of the column
54791      * @param {String} name The css property name
54792      * @param {String} value The css value
54793      */
54794     setCSSStyle : function(colIndex, name, value){
54795         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54796         Roo.util.CSS.updateRule(selector, name, value);
54797     },
54798     
54799     generateRules : function(cm){
54800         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54801         Roo.util.CSS.removeStyleSheet(rulesId);
54802         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54803             var cid = cm.getColumnId(i);
54804             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54805                          this.tdSelector, cid, " {\n}\n",
54806                          this.hdSelector, cid, " {\n}\n",
54807                          this.splitSelector, cid, " {\n}\n");
54808         }
54809         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54810     }
54811 });/*
54812  * Based on:
54813  * Ext JS Library 1.1.1
54814  * Copyright(c) 2006-2007, Ext JS, LLC.
54815  *
54816  * Originally Released Under LGPL - original licence link has changed is not relivant.
54817  *
54818  * Fork - LGPL
54819  * <script type="text/javascript">
54820  */
54821
54822 // private
54823 // This is a support class used internally by the Grid components
54824 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54825     this.grid = grid;
54826     this.view = grid.getView();
54827     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54828     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54829     if(hd2){
54830         this.setHandleElId(Roo.id(hd));
54831         this.setOuterHandleElId(Roo.id(hd2));
54832     }
54833     this.scroll = false;
54834 };
54835 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54836     maxDragWidth: 120,
54837     getDragData : function(e){
54838         var t = Roo.lib.Event.getTarget(e);
54839         var h = this.view.findHeaderCell(t);
54840         if(h){
54841             return {ddel: h.firstChild, header:h};
54842         }
54843         return false;
54844     },
54845
54846     onInitDrag : function(e){
54847         this.view.headersDisabled = true;
54848         var clone = this.dragData.ddel.cloneNode(true);
54849         clone.id = Roo.id();
54850         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54851         this.proxy.update(clone);
54852         return true;
54853     },
54854
54855     afterValidDrop : function(){
54856         var v = this.view;
54857         setTimeout(function(){
54858             v.headersDisabled = false;
54859         }, 50);
54860     },
54861
54862     afterInvalidDrop : function(){
54863         var v = this.view;
54864         setTimeout(function(){
54865             v.headersDisabled = false;
54866         }, 50);
54867     }
54868 });
54869 /*
54870  * Based on:
54871  * Ext JS Library 1.1.1
54872  * Copyright(c) 2006-2007, Ext JS, LLC.
54873  *
54874  * Originally Released Under LGPL - original licence link has changed is not relivant.
54875  *
54876  * Fork - LGPL
54877  * <script type="text/javascript">
54878  */
54879 // private
54880 // This is a support class used internally by the Grid components
54881 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54882     this.grid = grid;
54883     this.view = grid.getView();
54884     // split the proxies so they don't interfere with mouse events
54885     this.proxyTop = Roo.DomHelper.append(document.body, {
54886         cls:"col-move-top", html:"&#160;"
54887     }, true);
54888     this.proxyBottom = Roo.DomHelper.append(document.body, {
54889         cls:"col-move-bottom", html:"&#160;"
54890     }, true);
54891     this.proxyTop.hide = this.proxyBottom.hide = function(){
54892         this.setLeftTop(-100,-100);
54893         this.setStyle("visibility", "hidden");
54894     };
54895     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54896     // temporarily disabled
54897     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54898     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54899 };
54900 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54901     proxyOffsets : [-4, -9],
54902     fly: Roo.Element.fly,
54903
54904     getTargetFromEvent : function(e){
54905         var t = Roo.lib.Event.getTarget(e);
54906         var cindex = this.view.findCellIndex(t);
54907         if(cindex !== false){
54908             return this.view.getHeaderCell(cindex);
54909         }
54910         return null;
54911     },
54912
54913     nextVisible : function(h){
54914         var v = this.view, cm = this.grid.colModel;
54915         h = h.nextSibling;
54916         while(h){
54917             if(!cm.isHidden(v.getCellIndex(h))){
54918                 return h;
54919             }
54920             h = h.nextSibling;
54921         }
54922         return null;
54923     },
54924
54925     prevVisible : function(h){
54926         var v = this.view, cm = this.grid.colModel;
54927         h = h.prevSibling;
54928         while(h){
54929             if(!cm.isHidden(v.getCellIndex(h))){
54930                 return h;
54931             }
54932             h = h.prevSibling;
54933         }
54934         return null;
54935     },
54936
54937     positionIndicator : function(h, n, e){
54938         var x = Roo.lib.Event.getPageX(e);
54939         var r = Roo.lib.Dom.getRegion(n.firstChild);
54940         var px, pt, py = r.top + this.proxyOffsets[1];
54941         if((r.right - x) <= (r.right-r.left)/2){
54942             px = r.right+this.view.borderWidth;
54943             pt = "after";
54944         }else{
54945             px = r.left;
54946             pt = "before";
54947         }
54948         var oldIndex = this.view.getCellIndex(h);
54949         var newIndex = this.view.getCellIndex(n);
54950
54951         if(this.grid.colModel.isFixed(newIndex)){
54952             return false;
54953         }
54954
54955         var locked = this.grid.colModel.isLocked(newIndex);
54956
54957         if(pt == "after"){
54958             newIndex++;
54959         }
54960         if(oldIndex < newIndex){
54961             newIndex--;
54962         }
54963         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54964             return false;
54965         }
54966         px +=  this.proxyOffsets[0];
54967         this.proxyTop.setLeftTop(px, py);
54968         this.proxyTop.show();
54969         if(!this.bottomOffset){
54970             this.bottomOffset = this.view.mainHd.getHeight();
54971         }
54972         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54973         this.proxyBottom.show();
54974         return pt;
54975     },
54976
54977     onNodeEnter : function(n, dd, e, data){
54978         if(data.header != n){
54979             this.positionIndicator(data.header, n, e);
54980         }
54981     },
54982
54983     onNodeOver : function(n, dd, e, data){
54984         var result = false;
54985         if(data.header != n){
54986             result = this.positionIndicator(data.header, n, e);
54987         }
54988         if(!result){
54989             this.proxyTop.hide();
54990             this.proxyBottom.hide();
54991         }
54992         return result ? this.dropAllowed : this.dropNotAllowed;
54993     },
54994
54995     onNodeOut : function(n, dd, e, data){
54996         this.proxyTop.hide();
54997         this.proxyBottom.hide();
54998     },
54999
55000     onNodeDrop : function(n, dd, e, data){
55001         var h = data.header;
55002         if(h != n){
55003             var cm = this.grid.colModel;
55004             var x = Roo.lib.Event.getPageX(e);
55005             var r = Roo.lib.Dom.getRegion(n.firstChild);
55006             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55007             var oldIndex = this.view.getCellIndex(h);
55008             var newIndex = this.view.getCellIndex(n);
55009             var locked = cm.isLocked(newIndex);
55010             if(pt == "after"){
55011                 newIndex++;
55012             }
55013             if(oldIndex < newIndex){
55014                 newIndex--;
55015             }
55016             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55017                 return false;
55018             }
55019             cm.setLocked(oldIndex, locked, true);
55020             cm.moveColumn(oldIndex, newIndex);
55021             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55022             return true;
55023         }
55024         return false;
55025     }
55026 });
55027 /*
55028  * Based on:
55029  * Ext JS Library 1.1.1
55030  * Copyright(c) 2006-2007, Ext JS, LLC.
55031  *
55032  * Originally Released Under LGPL - original licence link has changed is not relivant.
55033  *
55034  * Fork - LGPL
55035  * <script type="text/javascript">
55036  */
55037   
55038 /**
55039  * @class Roo.grid.GridView
55040  * @extends Roo.util.Observable
55041  *
55042  * @constructor
55043  * @param {Object} config
55044  */
55045 Roo.grid.GridView = function(config){
55046     Roo.grid.GridView.superclass.constructor.call(this);
55047     this.el = null;
55048
55049     Roo.apply(this, config);
55050 };
55051
55052 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55053
55054     unselectable :  'unselectable="on"',
55055     unselectableCls :  'x-unselectable',
55056     
55057     
55058     rowClass : "x-grid-row",
55059
55060     cellClass : "x-grid-col",
55061
55062     tdClass : "x-grid-td",
55063
55064     hdClass : "x-grid-hd",
55065
55066     splitClass : "x-grid-split",
55067
55068     sortClasses : ["sort-asc", "sort-desc"],
55069
55070     enableMoveAnim : false,
55071
55072     hlColor: "C3DAF9",
55073
55074     dh : Roo.DomHelper,
55075
55076     fly : Roo.Element.fly,
55077
55078     css : Roo.util.CSS,
55079
55080     borderWidth: 1,
55081
55082     splitOffset: 3,
55083
55084     scrollIncrement : 22,
55085
55086     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55087
55088     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55089
55090     bind : function(ds, cm){
55091         if(this.ds){
55092             this.ds.un("load", this.onLoad, this);
55093             this.ds.un("datachanged", this.onDataChange, this);
55094             this.ds.un("add", this.onAdd, this);
55095             this.ds.un("remove", this.onRemove, this);
55096             this.ds.un("update", this.onUpdate, this);
55097             this.ds.un("clear", this.onClear, this);
55098         }
55099         if(ds){
55100             ds.on("load", this.onLoad, this);
55101             ds.on("datachanged", this.onDataChange, this);
55102             ds.on("add", this.onAdd, this);
55103             ds.on("remove", this.onRemove, this);
55104             ds.on("update", this.onUpdate, this);
55105             ds.on("clear", this.onClear, this);
55106         }
55107         this.ds = ds;
55108
55109         if(this.cm){
55110             this.cm.un("widthchange", this.onColWidthChange, this);
55111             this.cm.un("headerchange", this.onHeaderChange, this);
55112             this.cm.un("hiddenchange", this.onHiddenChange, this);
55113             this.cm.un("columnmoved", this.onColumnMove, this);
55114             this.cm.un("columnlockchange", this.onColumnLock, this);
55115         }
55116         if(cm){
55117             this.generateRules(cm);
55118             cm.on("widthchange", this.onColWidthChange, this);
55119             cm.on("headerchange", this.onHeaderChange, this);
55120             cm.on("hiddenchange", this.onHiddenChange, this);
55121             cm.on("columnmoved", this.onColumnMove, this);
55122             cm.on("columnlockchange", this.onColumnLock, this);
55123         }
55124         this.cm = cm;
55125     },
55126
55127     init: function(grid){
55128         Roo.grid.GridView.superclass.init.call(this, grid);
55129
55130         this.bind(grid.dataSource, grid.colModel);
55131
55132         grid.on("headerclick", this.handleHeaderClick, this);
55133
55134         if(grid.trackMouseOver){
55135             grid.on("mouseover", this.onRowOver, this);
55136             grid.on("mouseout", this.onRowOut, this);
55137         }
55138         grid.cancelTextSelection = function(){};
55139         this.gridId = grid.id;
55140
55141         var tpls = this.templates || {};
55142
55143         if(!tpls.master){
55144             tpls.master = new Roo.Template(
55145                '<div class="x-grid" hidefocus="true">',
55146                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55147                   '<div class="x-grid-topbar"></div>',
55148                   '<div class="x-grid-scroller"><div></div></div>',
55149                   '<div class="x-grid-locked">',
55150                       '<div class="x-grid-header">{lockedHeader}</div>',
55151                       '<div class="x-grid-body">{lockedBody}</div>',
55152                   "</div>",
55153                   '<div class="x-grid-viewport">',
55154                       '<div class="x-grid-header">{header}</div>',
55155                       '<div class="x-grid-body">{body}</div>',
55156                   "</div>",
55157                   '<div class="x-grid-bottombar"></div>',
55158                  
55159                   '<div class="x-grid-resize-proxy">&#160;</div>',
55160                "</div>"
55161             );
55162             tpls.master.disableformats = true;
55163         }
55164
55165         if(!tpls.header){
55166             tpls.header = new Roo.Template(
55167                '<table border="0" cellspacing="0" cellpadding="0">',
55168                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55169                "</table>{splits}"
55170             );
55171             tpls.header.disableformats = true;
55172         }
55173         tpls.header.compile();
55174
55175         if(!tpls.hcell){
55176             tpls.hcell = new Roo.Template(
55177                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55178                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55179                 "</div></td>"
55180              );
55181              tpls.hcell.disableFormats = true;
55182         }
55183         tpls.hcell.compile();
55184
55185         if(!tpls.hsplit){
55186             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55187                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55188             tpls.hsplit.disableFormats = true;
55189         }
55190         tpls.hsplit.compile();
55191
55192         if(!tpls.body){
55193             tpls.body = new Roo.Template(
55194                '<table border="0" cellspacing="0" cellpadding="0">',
55195                "<tbody>{rows}</tbody>",
55196                "</table>"
55197             );
55198             tpls.body.disableFormats = true;
55199         }
55200         tpls.body.compile();
55201
55202         if(!tpls.row){
55203             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55204             tpls.row.disableFormats = true;
55205         }
55206         tpls.row.compile();
55207
55208         if(!tpls.cell){
55209             tpls.cell = new Roo.Template(
55210                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55211                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55212                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55213                 "</td>"
55214             );
55215             tpls.cell.disableFormats = true;
55216         }
55217         tpls.cell.compile();
55218
55219         this.templates = tpls;
55220     },
55221
55222     // remap these for backwards compat
55223     onColWidthChange : function(){
55224         this.updateColumns.apply(this, arguments);
55225     },
55226     onHeaderChange : function(){
55227         this.updateHeaders.apply(this, arguments);
55228     }, 
55229     onHiddenChange : function(){
55230         this.handleHiddenChange.apply(this, arguments);
55231     },
55232     onColumnMove : function(){
55233         this.handleColumnMove.apply(this, arguments);
55234     },
55235     onColumnLock : function(){
55236         this.handleLockChange.apply(this, arguments);
55237     },
55238
55239     onDataChange : function(){
55240         this.refresh();
55241         this.updateHeaderSortState();
55242     },
55243
55244     onClear : function(){
55245         this.refresh();
55246     },
55247
55248     onUpdate : function(ds, record){
55249         this.refreshRow(record);
55250     },
55251
55252     refreshRow : function(record){
55253         var ds = this.ds, index;
55254         if(typeof record == 'number'){
55255             index = record;
55256             record = ds.getAt(index);
55257         }else{
55258             index = ds.indexOf(record);
55259         }
55260         this.insertRows(ds, index, index, true);
55261         this.onRemove(ds, record, index+1, true);
55262         this.syncRowHeights(index, index);
55263         this.layout();
55264         this.fireEvent("rowupdated", this, index, record);
55265     },
55266
55267     onAdd : function(ds, records, index){
55268         this.insertRows(ds, index, index + (records.length-1));
55269     },
55270
55271     onRemove : function(ds, record, index, isUpdate){
55272         if(isUpdate !== true){
55273             this.fireEvent("beforerowremoved", this, index, record);
55274         }
55275         var bt = this.getBodyTable(), lt = this.getLockedTable();
55276         if(bt.rows[index]){
55277             bt.firstChild.removeChild(bt.rows[index]);
55278         }
55279         if(lt.rows[index]){
55280             lt.firstChild.removeChild(lt.rows[index]);
55281         }
55282         if(isUpdate !== true){
55283             this.stripeRows(index);
55284             this.syncRowHeights(index, index);
55285             this.layout();
55286             this.fireEvent("rowremoved", this, index, record);
55287         }
55288     },
55289
55290     onLoad : function(){
55291         this.scrollToTop();
55292     },
55293
55294     /**
55295      * Scrolls the grid to the top
55296      */
55297     scrollToTop : function(){
55298         if(this.scroller){
55299             this.scroller.dom.scrollTop = 0;
55300             this.syncScroll();
55301         }
55302     },
55303
55304     /**
55305      * Gets a panel in the header of the grid that can be used for toolbars etc.
55306      * After modifying the contents of this panel a call to grid.autoSize() may be
55307      * required to register any changes in size.
55308      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55309      * @return Roo.Element
55310      */
55311     getHeaderPanel : function(doShow){
55312         if(doShow){
55313             this.headerPanel.show();
55314         }
55315         return this.headerPanel;
55316     },
55317
55318     /**
55319      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55320      * After modifying the contents of this panel a call to grid.autoSize() may be
55321      * required to register any changes in size.
55322      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55323      * @return Roo.Element
55324      */
55325     getFooterPanel : function(doShow){
55326         if(doShow){
55327             this.footerPanel.show();
55328         }
55329         return this.footerPanel;
55330     },
55331
55332     initElements : function(){
55333         var E = Roo.Element;
55334         var el = this.grid.getGridEl().dom.firstChild;
55335         var cs = el.childNodes;
55336
55337         this.el = new E(el);
55338         
55339          this.focusEl = new E(el.firstChild);
55340         this.focusEl.swallowEvent("click", true);
55341         
55342         this.headerPanel = new E(cs[1]);
55343         this.headerPanel.enableDisplayMode("block");
55344
55345         this.scroller = new E(cs[2]);
55346         this.scrollSizer = new E(this.scroller.dom.firstChild);
55347
55348         this.lockedWrap = new E(cs[3]);
55349         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55350         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55351
55352         this.mainWrap = new E(cs[4]);
55353         this.mainHd = new E(this.mainWrap.dom.firstChild);
55354         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55355
55356         this.footerPanel = new E(cs[5]);
55357         this.footerPanel.enableDisplayMode("block");
55358
55359         this.resizeProxy = new E(cs[6]);
55360
55361         this.headerSelector = String.format(
55362            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55363            this.lockedHd.id, this.mainHd.id
55364         );
55365
55366         this.splitterSelector = String.format(
55367            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55368            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55369         );
55370     },
55371     idToCssName : function(s)
55372     {
55373         return s.replace(/[^a-z0-9]+/ig, '-');
55374     },
55375
55376     getHeaderCell : function(index){
55377         return Roo.DomQuery.select(this.headerSelector)[index];
55378     },
55379
55380     getHeaderCellMeasure : function(index){
55381         return this.getHeaderCell(index).firstChild;
55382     },
55383
55384     getHeaderCellText : function(index){
55385         return this.getHeaderCell(index).firstChild.firstChild;
55386     },
55387
55388     getLockedTable : function(){
55389         return this.lockedBody.dom.firstChild;
55390     },
55391
55392     getBodyTable : function(){
55393         return this.mainBody.dom.firstChild;
55394     },
55395
55396     getLockedRow : function(index){
55397         return this.getLockedTable().rows[index];
55398     },
55399
55400     getRow : function(index){
55401         return this.getBodyTable().rows[index];
55402     },
55403
55404     getRowComposite : function(index){
55405         if(!this.rowEl){
55406             this.rowEl = new Roo.CompositeElementLite();
55407         }
55408         var els = [], lrow, mrow;
55409         if(lrow = this.getLockedRow(index)){
55410             els.push(lrow);
55411         }
55412         if(mrow = this.getRow(index)){
55413             els.push(mrow);
55414         }
55415         this.rowEl.elements = els;
55416         return this.rowEl;
55417     },
55418     /**
55419      * Gets the 'td' of the cell
55420      * 
55421      * @param {Integer} rowIndex row to select
55422      * @param {Integer} colIndex column to select
55423      * 
55424      * @return {Object} 
55425      */
55426     getCell : function(rowIndex, colIndex){
55427         var locked = this.cm.getLockedCount();
55428         var source;
55429         if(colIndex < locked){
55430             source = this.lockedBody.dom.firstChild;
55431         }else{
55432             source = this.mainBody.dom.firstChild;
55433             colIndex -= locked;
55434         }
55435         return source.rows[rowIndex].childNodes[colIndex];
55436     },
55437
55438     getCellText : function(rowIndex, colIndex){
55439         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55440     },
55441
55442     getCellBox : function(cell){
55443         var b = this.fly(cell).getBox();
55444         if(Roo.isOpera){ // opera fails to report the Y
55445             b.y = cell.offsetTop + this.mainBody.getY();
55446         }
55447         return b;
55448     },
55449
55450     getCellIndex : function(cell){
55451         var id = String(cell.className).match(this.cellRE);
55452         if(id){
55453             return parseInt(id[1], 10);
55454         }
55455         return 0;
55456     },
55457
55458     findHeaderIndex : function(n){
55459         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55460         return r ? this.getCellIndex(r) : false;
55461     },
55462
55463     findHeaderCell : function(n){
55464         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55465         return r ? r : false;
55466     },
55467
55468     findRowIndex : function(n){
55469         if(!n){
55470             return false;
55471         }
55472         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55473         return r ? r.rowIndex : false;
55474     },
55475
55476     findCellIndex : function(node){
55477         var stop = this.el.dom;
55478         while(node && node != stop){
55479             if(this.findRE.test(node.className)){
55480                 return this.getCellIndex(node);
55481             }
55482             node = node.parentNode;
55483         }
55484         return false;
55485     },
55486
55487     getColumnId : function(index){
55488         return this.cm.getColumnId(index);
55489     },
55490
55491     getSplitters : function()
55492     {
55493         if(this.splitterSelector){
55494            return Roo.DomQuery.select(this.splitterSelector);
55495         }else{
55496             return null;
55497       }
55498     },
55499
55500     getSplitter : function(index){
55501         return this.getSplitters()[index];
55502     },
55503
55504     onRowOver : function(e, t){
55505         var row;
55506         if((row = this.findRowIndex(t)) !== false){
55507             this.getRowComposite(row).addClass("x-grid-row-over");
55508         }
55509     },
55510
55511     onRowOut : function(e, t){
55512         var row;
55513         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55514             this.getRowComposite(row).removeClass("x-grid-row-over");
55515         }
55516     },
55517
55518     renderHeaders : function(){
55519         var cm = this.cm;
55520         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55521         var cb = [], lb = [], sb = [], lsb = [], p = {};
55522         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55523             p.cellId = "x-grid-hd-0-" + i;
55524             p.splitId = "x-grid-csplit-0-" + i;
55525             p.id = cm.getColumnId(i);
55526             p.value = cm.getColumnHeader(i) || "";
55527             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55528             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55529             if(!cm.isLocked(i)){
55530                 cb[cb.length] = ct.apply(p);
55531                 sb[sb.length] = st.apply(p);
55532             }else{
55533                 lb[lb.length] = ct.apply(p);
55534                 lsb[lsb.length] = st.apply(p);
55535             }
55536         }
55537         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55538                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55539     },
55540
55541     updateHeaders : function(){
55542         var html = this.renderHeaders();
55543         this.lockedHd.update(html[0]);
55544         this.mainHd.update(html[1]);
55545     },
55546
55547     /**
55548      * Focuses the specified row.
55549      * @param {Number} row The row index
55550      */
55551     focusRow : function(row)
55552     {
55553         //Roo.log('GridView.focusRow');
55554         var x = this.scroller.dom.scrollLeft;
55555         this.focusCell(row, 0, false);
55556         this.scroller.dom.scrollLeft = x;
55557     },
55558
55559     /**
55560      * Focuses the specified cell.
55561      * @param {Number} row The row index
55562      * @param {Number} col The column index
55563      * @param {Boolean} hscroll false to disable horizontal scrolling
55564      */
55565     focusCell : function(row, col, hscroll)
55566     {
55567         //Roo.log('GridView.focusCell');
55568         var el = this.ensureVisible(row, col, hscroll);
55569         this.focusEl.alignTo(el, "tl-tl");
55570         if(Roo.isGecko){
55571             this.focusEl.focus();
55572         }else{
55573             this.focusEl.focus.defer(1, this.focusEl);
55574         }
55575     },
55576
55577     /**
55578      * Scrolls the specified cell into view
55579      * @param {Number} row The row index
55580      * @param {Number} col The column index
55581      * @param {Boolean} hscroll false to disable horizontal scrolling
55582      */
55583     ensureVisible : function(row, col, hscroll)
55584     {
55585         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55586         //return null; //disable for testing.
55587         if(typeof row != "number"){
55588             row = row.rowIndex;
55589         }
55590         if(row < 0 && row >= this.ds.getCount()){
55591             return  null;
55592         }
55593         col = (col !== undefined ? col : 0);
55594         var cm = this.grid.colModel;
55595         while(cm.isHidden(col)){
55596             col++;
55597         }
55598
55599         var el = this.getCell(row, col);
55600         if(!el){
55601             return null;
55602         }
55603         var c = this.scroller.dom;
55604
55605         var ctop = parseInt(el.offsetTop, 10);
55606         var cleft = parseInt(el.offsetLeft, 10);
55607         var cbot = ctop + el.offsetHeight;
55608         var cright = cleft + el.offsetWidth;
55609         
55610         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55611         var stop = parseInt(c.scrollTop, 10);
55612         var sleft = parseInt(c.scrollLeft, 10);
55613         var sbot = stop + ch;
55614         var sright = sleft + c.clientWidth;
55615         /*
55616         Roo.log('GridView.ensureVisible:' +
55617                 ' ctop:' + ctop +
55618                 ' c.clientHeight:' + c.clientHeight +
55619                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55620                 ' stop:' + stop +
55621                 ' cbot:' + cbot +
55622                 ' sbot:' + sbot +
55623                 ' ch:' + ch  
55624                 );
55625         */
55626         if(ctop < stop){
55627              c.scrollTop = ctop;
55628             //Roo.log("set scrolltop to ctop DISABLE?");
55629         }else if(cbot > sbot){
55630             //Roo.log("set scrolltop to cbot-ch");
55631             c.scrollTop = cbot-ch;
55632         }
55633         
55634         if(hscroll !== false){
55635             if(cleft < sleft){
55636                 c.scrollLeft = cleft;
55637             }else if(cright > sright){
55638                 c.scrollLeft = cright-c.clientWidth;
55639             }
55640         }
55641          
55642         return el;
55643     },
55644
55645     updateColumns : function(){
55646         this.grid.stopEditing();
55647         var cm = this.grid.colModel, colIds = this.getColumnIds();
55648         //var totalWidth = cm.getTotalWidth();
55649         var pos = 0;
55650         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55651             //if(cm.isHidden(i)) continue;
55652             var w = cm.getColumnWidth(i);
55653             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55654             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55655         }
55656         this.updateSplitters();
55657     },
55658
55659     generateRules : function(cm){
55660         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55661         Roo.util.CSS.removeStyleSheet(rulesId);
55662         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55663             var cid = cm.getColumnId(i);
55664             var align = '';
55665             if(cm.config[i].align){
55666                 align = 'text-align:'+cm.config[i].align+';';
55667             }
55668             var hidden = '';
55669             if(cm.isHidden(i)){
55670                 hidden = 'display:none;';
55671             }
55672             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55673             ruleBuf.push(
55674                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55675                     this.hdSelector, cid, " {\n", align, width, "}\n",
55676                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55677                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55678         }
55679         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55680     },
55681
55682     updateSplitters : function(){
55683         var cm = this.cm, s = this.getSplitters();
55684         if(s){ // splitters not created yet
55685             var pos = 0, locked = true;
55686             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55687                 if(cm.isHidden(i)) {
55688                     continue;
55689                 }
55690                 var w = cm.getColumnWidth(i); // make sure it's a number
55691                 if(!cm.isLocked(i) && locked){
55692                     pos = 0;
55693                     locked = false;
55694                 }
55695                 pos += w;
55696                 s[i].style.left = (pos-this.splitOffset) + "px";
55697             }
55698         }
55699     },
55700
55701     handleHiddenChange : function(colModel, colIndex, hidden){
55702         if(hidden){
55703             this.hideColumn(colIndex);
55704         }else{
55705             this.unhideColumn(colIndex);
55706         }
55707     },
55708
55709     hideColumn : function(colIndex){
55710         var cid = this.getColumnId(colIndex);
55711         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55712         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55713         if(Roo.isSafari){
55714             this.updateHeaders();
55715         }
55716         this.updateSplitters();
55717         this.layout();
55718     },
55719
55720     unhideColumn : function(colIndex){
55721         var cid = this.getColumnId(colIndex);
55722         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55723         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55724
55725         if(Roo.isSafari){
55726             this.updateHeaders();
55727         }
55728         this.updateSplitters();
55729         this.layout();
55730     },
55731
55732     insertRows : function(dm, firstRow, lastRow, isUpdate){
55733         if(firstRow == 0 && lastRow == dm.getCount()-1){
55734             this.refresh();
55735         }else{
55736             if(!isUpdate){
55737                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55738             }
55739             var s = this.getScrollState();
55740             var markup = this.renderRows(firstRow, lastRow);
55741             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55742             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55743             this.restoreScroll(s);
55744             if(!isUpdate){
55745                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55746                 this.syncRowHeights(firstRow, lastRow);
55747                 this.stripeRows(firstRow);
55748                 this.layout();
55749             }
55750         }
55751     },
55752
55753     bufferRows : function(markup, target, index){
55754         var before = null, trows = target.rows, tbody = target.tBodies[0];
55755         if(index < trows.length){
55756             before = trows[index];
55757         }
55758         var b = document.createElement("div");
55759         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55760         var rows = b.firstChild.rows;
55761         for(var i = 0, len = rows.length; i < len; i++){
55762             if(before){
55763                 tbody.insertBefore(rows[0], before);
55764             }else{
55765                 tbody.appendChild(rows[0]);
55766             }
55767         }
55768         b.innerHTML = "";
55769         b = null;
55770     },
55771
55772     deleteRows : function(dm, firstRow, lastRow){
55773         if(dm.getRowCount()<1){
55774             this.fireEvent("beforerefresh", this);
55775             this.mainBody.update("");
55776             this.lockedBody.update("");
55777             this.fireEvent("refresh", this);
55778         }else{
55779             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55780             var bt = this.getBodyTable();
55781             var tbody = bt.firstChild;
55782             var rows = bt.rows;
55783             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55784                 tbody.removeChild(rows[firstRow]);
55785             }
55786             this.stripeRows(firstRow);
55787             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55788         }
55789     },
55790
55791     updateRows : function(dataSource, firstRow, lastRow){
55792         var s = this.getScrollState();
55793         this.refresh();
55794         this.restoreScroll(s);
55795     },
55796
55797     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55798         if(!noRefresh){
55799            this.refresh();
55800         }
55801         this.updateHeaderSortState();
55802     },
55803
55804     getScrollState : function(){
55805         
55806         var sb = this.scroller.dom;
55807         return {left: sb.scrollLeft, top: sb.scrollTop};
55808     },
55809
55810     stripeRows : function(startRow){
55811         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55812             return;
55813         }
55814         startRow = startRow || 0;
55815         var rows = this.getBodyTable().rows;
55816         var lrows = this.getLockedTable().rows;
55817         var cls = ' x-grid-row-alt ';
55818         for(var i = startRow, len = rows.length; i < len; i++){
55819             var row = rows[i], lrow = lrows[i];
55820             var isAlt = ((i+1) % 2 == 0);
55821             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55822             if(isAlt == hasAlt){
55823                 continue;
55824             }
55825             if(isAlt){
55826                 row.className += " x-grid-row-alt";
55827             }else{
55828                 row.className = row.className.replace("x-grid-row-alt", "");
55829             }
55830             if(lrow){
55831                 lrow.className = row.className;
55832             }
55833         }
55834     },
55835
55836     restoreScroll : function(state){
55837         //Roo.log('GridView.restoreScroll');
55838         var sb = this.scroller.dom;
55839         sb.scrollLeft = state.left;
55840         sb.scrollTop = state.top;
55841         this.syncScroll();
55842     },
55843
55844     syncScroll : function(){
55845         //Roo.log('GridView.syncScroll');
55846         var sb = this.scroller.dom;
55847         var sh = this.mainHd.dom;
55848         var bs = this.mainBody.dom;
55849         var lv = this.lockedBody.dom;
55850         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55851         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55852     },
55853
55854     handleScroll : function(e){
55855         this.syncScroll();
55856         var sb = this.scroller.dom;
55857         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55858         e.stopEvent();
55859     },
55860
55861     handleWheel : function(e){
55862         var d = e.getWheelDelta();
55863         this.scroller.dom.scrollTop -= d*22;
55864         // set this here to prevent jumpy scrolling on large tables
55865         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55866         e.stopEvent();
55867     },
55868
55869     renderRows : function(startRow, endRow){
55870         // pull in all the crap needed to render rows
55871         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55872         var colCount = cm.getColumnCount();
55873
55874         if(ds.getCount() < 1){
55875             return ["", ""];
55876         }
55877
55878         // build a map for all the columns
55879         var cs = [];
55880         for(var i = 0; i < colCount; i++){
55881             var name = cm.getDataIndex(i);
55882             cs[i] = {
55883                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55884                 renderer : cm.getRenderer(i),
55885                 id : cm.getColumnId(i),
55886                 locked : cm.isLocked(i),
55887                 has_editor : cm.isCellEditable(i)
55888             };
55889         }
55890
55891         startRow = startRow || 0;
55892         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55893
55894         // records to render
55895         var rs = ds.getRange(startRow, endRow);
55896
55897         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55898     },
55899
55900     // As much as I hate to duplicate code, this was branched because FireFox really hates
55901     // [].join("") on strings. The performance difference was substantial enough to
55902     // branch this function
55903     doRender : Roo.isGecko ?
55904             function(cs, rs, ds, startRow, colCount, stripe){
55905                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55906                 // buffers
55907                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55908                 
55909                 var hasListener = this.grid.hasListener('rowclass');
55910                 var rowcfg = {};
55911                 for(var j = 0, len = rs.length; j < len; j++){
55912                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55913                     for(var i = 0; i < colCount; i++){
55914                         c = cs[i];
55915                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55916                         p.id = c.id;
55917                         p.css = p.attr = "";
55918                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55919                         if(p.value == undefined || p.value === "") {
55920                             p.value = "&#160;";
55921                         }
55922                         if(c.has_editor){
55923                             p.css += ' x-grid-editable-cell';
55924                         }
55925                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55926                             p.css +=  ' x-grid-dirty-cell';
55927                         }
55928                         var markup = ct.apply(p);
55929                         if(!c.locked){
55930                             cb+= markup;
55931                         }else{
55932                             lcb+= markup;
55933                         }
55934                     }
55935                     var alt = [];
55936                     if(stripe && ((rowIndex+1) % 2 == 0)){
55937                         alt.push("x-grid-row-alt")
55938                     }
55939                     if(r.dirty){
55940                         alt.push(  " x-grid-dirty-row");
55941                     }
55942                     rp.cells = lcb;
55943                     if(this.getRowClass){
55944                         alt.push(this.getRowClass(r, rowIndex));
55945                     }
55946                     if (hasListener) {
55947                         rowcfg = {
55948                              
55949                             record: r,
55950                             rowIndex : rowIndex,
55951                             rowClass : ''
55952                         };
55953                         this.grid.fireEvent('rowclass', this, rowcfg);
55954                         alt.push(rowcfg.rowClass);
55955                     }
55956                     rp.alt = alt.join(" ");
55957                     lbuf+= rt.apply(rp);
55958                     rp.cells = cb;
55959                     buf+=  rt.apply(rp);
55960                 }
55961                 return [lbuf, buf];
55962             } :
55963             function(cs, rs, ds, startRow, colCount, stripe){
55964                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55965                 // buffers
55966                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55967                 var hasListener = this.grid.hasListener('rowclass');
55968  
55969                 var rowcfg = {};
55970                 for(var j = 0, len = rs.length; j < len; j++){
55971                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55972                     for(var i = 0; i < colCount; i++){
55973                         c = cs[i];
55974                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55975                         p.id = c.id;
55976                         p.css = p.attr = "";
55977                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55978                         if(p.value == undefined || p.value === "") {
55979                             p.value = "&#160;";
55980                         }
55981                         //Roo.log(c);
55982                          if(c.has_editor){
55983                             p.css += ' x-grid-editable-cell';
55984                         }
55985                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55986                             p.css += ' x-grid-dirty-cell' 
55987                         }
55988                         
55989                         var markup = ct.apply(p);
55990                         if(!c.locked){
55991                             cb[cb.length] = markup;
55992                         }else{
55993                             lcb[lcb.length] = markup;
55994                         }
55995                     }
55996                     var alt = [];
55997                     if(stripe && ((rowIndex+1) % 2 == 0)){
55998                         alt.push( "x-grid-row-alt");
55999                     }
56000                     if(r.dirty){
56001                         alt.push(" x-grid-dirty-row");
56002                     }
56003                     rp.cells = lcb;
56004                     if(this.getRowClass){
56005                         alt.push( this.getRowClass(r, rowIndex));
56006                     }
56007                     if (hasListener) {
56008                         rowcfg = {
56009                              
56010                             record: r,
56011                             rowIndex : rowIndex,
56012                             rowClass : ''
56013                         };
56014                         this.grid.fireEvent('rowclass', this, rowcfg);
56015                         alt.push(rowcfg.rowClass);
56016                     }
56017                     
56018                     rp.alt = alt.join(" ");
56019                     rp.cells = lcb.join("");
56020                     lbuf[lbuf.length] = rt.apply(rp);
56021                     rp.cells = cb.join("");
56022                     buf[buf.length] =  rt.apply(rp);
56023                 }
56024                 return [lbuf.join(""), buf.join("")];
56025             },
56026
56027     renderBody : function(){
56028         var markup = this.renderRows();
56029         var bt = this.templates.body;
56030         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56031     },
56032
56033     /**
56034      * Refreshes the grid
56035      * @param {Boolean} headersToo
56036      */
56037     refresh : function(headersToo){
56038         this.fireEvent("beforerefresh", this);
56039         this.grid.stopEditing();
56040         var result = this.renderBody();
56041         this.lockedBody.update(result[0]);
56042         this.mainBody.update(result[1]);
56043         if(headersToo === true){
56044             this.updateHeaders();
56045             this.updateColumns();
56046             this.updateSplitters();
56047             this.updateHeaderSortState();
56048         }
56049         this.syncRowHeights();
56050         this.layout();
56051         this.fireEvent("refresh", this);
56052     },
56053
56054     handleColumnMove : function(cm, oldIndex, newIndex){
56055         this.indexMap = null;
56056         var s = this.getScrollState();
56057         this.refresh(true);
56058         this.restoreScroll(s);
56059         this.afterMove(newIndex);
56060     },
56061
56062     afterMove : function(colIndex){
56063         if(this.enableMoveAnim && Roo.enableFx){
56064             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56065         }
56066         // if multisort - fix sortOrder, and reload..
56067         if (this.grid.dataSource.multiSort) {
56068             // the we can call sort again..
56069             var dm = this.grid.dataSource;
56070             var cm = this.grid.colModel;
56071             var so = [];
56072             for(var i = 0; i < cm.config.length; i++ ) {
56073                 
56074                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56075                     continue; // dont' bother, it's not in sort list or being set.
56076                 }
56077                 
56078                 so.push(cm.config[i].dataIndex);
56079             };
56080             dm.sortOrder = so;
56081             dm.load(dm.lastOptions);
56082             
56083             
56084         }
56085         
56086     },
56087
56088     updateCell : function(dm, rowIndex, dataIndex){
56089         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56090         if(typeof colIndex == "undefined"){ // not present in grid
56091             return;
56092         }
56093         var cm = this.grid.colModel;
56094         var cell = this.getCell(rowIndex, colIndex);
56095         var cellText = this.getCellText(rowIndex, colIndex);
56096
56097         var p = {
56098             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56099             id : cm.getColumnId(colIndex),
56100             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56101         };
56102         var renderer = cm.getRenderer(colIndex);
56103         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56104         if(typeof val == "undefined" || val === "") {
56105             val = "&#160;";
56106         }
56107         cellText.innerHTML = val;
56108         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56109         this.syncRowHeights(rowIndex, rowIndex);
56110     },
56111
56112     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56113         var maxWidth = 0;
56114         if(this.grid.autoSizeHeaders){
56115             var h = this.getHeaderCellMeasure(colIndex);
56116             maxWidth = Math.max(maxWidth, h.scrollWidth);
56117         }
56118         var tb, index;
56119         if(this.cm.isLocked(colIndex)){
56120             tb = this.getLockedTable();
56121             index = colIndex;
56122         }else{
56123             tb = this.getBodyTable();
56124             index = colIndex - this.cm.getLockedCount();
56125         }
56126         if(tb && tb.rows){
56127             var rows = tb.rows;
56128             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56129             for(var i = 0; i < stopIndex; i++){
56130                 var cell = rows[i].childNodes[index].firstChild;
56131                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56132             }
56133         }
56134         return maxWidth + /*margin for error in IE*/ 5;
56135     },
56136     /**
56137      * Autofit a column to its content.
56138      * @param {Number} colIndex
56139      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56140      */
56141      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56142          if(this.cm.isHidden(colIndex)){
56143              return; // can't calc a hidden column
56144          }
56145         if(forceMinSize){
56146             var cid = this.cm.getColumnId(colIndex);
56147             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56148            if(this.grid.autoSizeHeaders){
56149                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56150            }
56151         }
56152         var newWidth = this.calcColumnWidth(colIndex);
56153         this.cm.setColumnWidth(colIndex,
56154             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56155         if(!suppressEvent){
56156             this.grid.fireEvent("columnresize", colIndex, newWidth);
56157         }
56158     },
56159
56160     /**
56161      * Autofits all columns to their content and then expands to fit any extra space in the grid
56162      */
56163      autoSizeColumns : function(){
56164         var cm = this.grid.colModel;
56165         var colCount = cm.getColumnCount();
56166         for(var i = 0; i < colCount; i++){
56167             this.autoSizeColumn(i, true, true);
56168         }
56169         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56170             this.fitColumns();
56171         }else{
56172             this.updateColumns();
56173             this.layout();
56174         }
56175     },
56176
56177     /**
56178      * Autofits all columns to the grid's width proportionate with their current size
56179      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56180      */
56181     fitColumns : function(reserveScrollSpace){
56182         var cm = this.grid.colModel;
56183         var colCount = cm.getColumnCount();
56184         var cols = [];
56185         var width = 0;
56186         var i, w;
56187         for (i = 0; i < colCount; i++){
56188             if(!cm.isHidden(i) && !cm.isFixed(i)){
56189                 w = cm.getColumnWidth(i);
56190                 cols.push(i);
56191                 cols.push(w);
56192                 width += w;
56193             }
56194         }
56195         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56196         if(reserveScrollSpace){
56197             avail -= 17;
56198         }
56199         var frac = (avail - cm.getTotalWidth())/width;
56200         while (cols.length){
56201             w = cols.pop();
56202             i = cols.pop();
56203             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56204         }
56205         this.updateColumns();
56206         this.layout();
56207     },
56208
56209     onRowSelect : function(rowIndex){
56210         var row = this.getRowComposite(rowIndex);
56211         row.addClass("x-grid-row-selected");
56212     },
56213
56214     onRowDeselect : function(rowIndex){
56215         var row = this.getRowComposite(rowIndex);
56216         row.removeClass("x-grid-row-selected");
56217     },
56218
56219     onCellSelect : function(row, col){
56220         var cell = this.getCell(row, col);
56221         if(cell){
56222             Roo.fly(cell).addClass("x-grid-cell-selected");
56223         }
56224     },
56225
56226     onCellDeselect : function(row, col){
56227         var cell = this.getCell(row, col);
56228         if(cell){
56229             Roo.fly(cell).removeClass("x-grid-cell-selected");
56230         }
56231     },
56232
56233     updateHeaderSortState : function(){
56234         
56235         // sort state can be single { field: xxx, direction : yyy}
56236         // or   { xxx=>ASC , yyy : DESC ..... }
56237         
56238         var mstate = {};
56239         if (!this.ds.multiSort) { 
56240             var state = this.ds.getSortState();
56241             if(!state){
56242                 return;
56243             }
56244             mstate[state.field] = state.direction;
56245             // FIXME... - this is not used here.. but might be elsewhere..
56246             this.sortState = state;
56247             
56248         } else {
56249             mstate = this.ds.sortToggle;
56250         }
56251         //remove existing sort classes..
56252         
56253         var sc = this.sortClasses;
56254         var hds = this.el.select(this.headerSelector).removeClass(sc);
56255         
56256         for(var f in mstate) {
56257         
56258             var sortColumn = this.cm.findColumnIndex(f);
56259             
56260             if(sortColumn != -1){
56261                 var sortDir = mstate[f];        
56262                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56263             }
56264         }
56265         
56266          
56267         
56268     },
56269
56270
56271     handleHeaderClick : function(g, index,e){
56272         
56273         Roo.log("header click");
56274         
56275         if (Roo.isTouch) {
56276             // touch events on header are handled by context
56277             this.handleHdCtx(g,index,e);
56278             return;
56279         }
56280         
56281         
56282         if(this.headersDisabled){
56283             return;
56284         }
56285         var dm = g.dataSource, cm = g.colModel;
56286         if(!cm.isSortable(index)){
56287             return;
56288         }
56289         g.stopEditing();
56290         
56291         if (dm.multiSort) {
56292             // update the sortOrder
56293             var so = [];
56294             for(var i = 0; i < cm.config.length; i++ ) {
56295                 
56296                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56297                     continue; // dont' bother, it's not in sort list or being set.
56298                 }
56299                 
56300                 so.push(cm.config[i].dataIndex);
56301             };
56302             dm.sortOrder = so;
56303         }
56304         
56305         
56306         dm.sort(cm.getDataIndex(index));
56307     },
56308
56309
56310     destroy : function(){
56311         if(this.colMenu){
56312             this.colMenu.removeAll();
56313             Roo.menu.MenuMgr.unregister(this.colMenu);
56314             this.colMenu.getEl().remove();
56315             delete this.colMenu;
56316         }
56317         if(this.hmenu){
56318             this.hmenu.removeAll();
56319             Roo.menu.MenuMgr.unregister(this.hmenu);
56320             this.hmenu.getEl().remove();
56321             delete this.hmenu;
56322         }
56323         if(this.grid.enableColumnMove){
56324             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56325             if(dds){
56326                 for(var dd in dds){
56327                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56328                         var elid = dds[dd].dragElId;
56329                         dds[dd].unreg();
56330                         Roo.get(elid).remove();
56331                     } else if(dds[dd].config.isTarget){
56332                         dds[dd].proxyTop.remove();
56333                         dds[dd].proxyBottom.remove();
56334                         dds[dd].unreg();
56335                     }
56336                     if(Roo.dd.DDM.locationCache[dd]){
56337                         delete Roo.dd.DDM.locationCache[dd];
56338                     }
56339                 }
56340                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56341             }
56342         }
56343         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56344         this.bind(null, null);
56345         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56346     },
56347
56348     handleLockChange : function(){
56349         this.refresh(true);
56350     },
56351
56352     onDenyColumnLock : function(){
56353
56354     },
56355
56356     onDenyColumnHide : function(){
56357
56358     },
56359
56360     handleHdMenuClick : function(item){
56361         var index = this.hdCtxIndex;
56362         var cm = this.cm, ds = this.ds;
56363         switch(item.id){
56364             case "asc":
56365                 ds.sort(cm.getDataIndex(index), "ASC");
56366                 break;
56367             case "desc":
56368                 ds.sort(cm.getDataIndex(index), "DESC");
56369                 break;
56370             case "lock":
56371                 var lc = cm.getLockedCount();
56372                 if(cm.getColumnCount(true) <= lc+1){
56373                     this.onDenyColumnLock();
56374                     return;
56375                 }
56376                 if(lc != index){
56377                     cm.setLocked(index, true, true);
56378                     cm.moveColumn(index, lc);
56379                     this.grid.fireEvent("columnmove", index, lc);
56380                 }else{
56381                     cm.setLocked(index, true);
56382                 }
56383             break;
56384             case "unlock":
56385                 var lc = cm.getLockedCount();
56386                 if((lc-1) != index){
56387                     cm.setLocked(index, false, true);
56388                     cm.moveColumn(index, lc-1);
56389                     this.grid.fireEvent("columnmove", index, lc-1);
56390                 }else{
56391                     cm.setLocked(index, false);
56392                 }
56393             break;
56394             case 'wider': // used to expand cols on touch..
56395             case 'narrow':
56396                 var cw = cm.getColumnWidth(index);
56397                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56398                 cw = Math.max(0, cw);
56399                 cw = Math.min(cw,4000);
56400                 cm.setColumnWidth(index, cw);
56401                 break;
56402                 
56403             default:
56404                 index = cm.getIndexById(item.id.substr(4));
56405                 if(index != -1){
56406                     if(item.checked && cm.getColumnCount(true) <= 1){
56407                         this.onDenyColumnHide();
56408                         return false;
56409                     }
56410                     cm.setHidden(index, item.checked);
56411                 }
56412         }
56413         return true;
56414     },
56415
56416     beforeColMenuShow : function(){
56417         var cm = this.cm,  colCount = cm.getColumnCount();
56418         this.colMenu.removeAll();
56419         for(var i = 0; i < colCount; i++){
56420             this.colMenu.add(new Roo.menu.CheckItem({
56421                 id: "col-"+cm.getColumnId(i),
56422                 text: cm.getColumnHeader(i),
56423                 checked: !cm.isHidden(i),
56424                 hideOnClick:false
56425             }));
56426         }
56427     },
56428
56429     handleHdCtx : function(g, index, e){
56430         e.stopEvent();
56431         var hd = this.getHeaderCell(index);
56432         this.hdCtxIndex = index;
56433         var ms = this.hmenu.items, cm = this.cm;
56434         ms.get("asc").setDisabled(!cm.isSortable(index));
56435         ms.get("desc").setDisabled(!cm.isSortable(index));
56436         if(this.grid.enableColLock !== false){
56437             ms.get("lock").setDisabled(cm.isLocked(index));
56438             ms.get("unlock").setDisabled(!cm.isLocked(index));
56439         }
56440         this.hmenu.show(hd, "tl-bl");
56441     },
56442
56443     handleHdOver : function(e){
56444         var hd = this.findHeaderCell(e.getTarget());
56445         if(hd && !this.headersDisabled){
56446             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56447                this.fly(hd).addClass("x-grid-hd-over");
56448             }
56449         }
56450     },
56451
56452     handleHdOut : function(e){
56453         var hd = this.findHeaderCell(e.getTarget());
56454         if(hd){
56455             this.fly(hd).removeClass("x-grid-hd-over");
56456         }
56457     },
56458
56459     handleSplitDblClick : function(e, t){
56460         var i = this.getCellIndex(t);
56461         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56462             this.autoSizeColumn(i, true);
56463             this.layout();
56464         }
56465     },
56466
56467     render : function(){
56468
56469         var cm = this.cm;
56470         var colCount = cm.getColumnCount();
56471
56472         if(this.grid.monitorWindowResize === true){
56473             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56474         }
56475         var header = this.renderHeaders();
56476         var body = this.templates.body.apply({rows:""});
56477         var html = this.templates.master.apply({
56478             lockedBody: body,
56479             body: body,
56480             lockedHeader: header[0],
56481             header: header[1]
56482         });
56483
56484         //this.updateColumns();
56485
56486         this.grid.getGridEl().dom.innerHTML = html;
56487
56488         this.initElements();
56489         
56490         // a kludge to fix the random scolling effect in webkit
56491         this.el.on("scroll", function() {
56492             this.el.dom.scrollTop=0; // hopefully not recursive..
56493         },this);
56494
56495         this.scroller.on("scroll", this.handleScroll, this);
56496         this.lockedBody.on("mousewheel", this.handleWheel, this);
56497         this.mainBody.on("mousewheel", this.handleWheel, this);
56498
56499         this.mainHd.on("mouseover", this.handleHdOver, this);
56500         this.mainHd.on("mouseout", this.handleHdOut, this);
56501         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56502                 {delegate: "."+this.splitClass});
56503
56504         this.lockedHd.on("mouseover", this.handleHdOver, this);
56505         this.lockedHd.on("mouseout", this.handleHdOut, this);
56506         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56507                 {delegate: "."+this.splitClass});
56508
56509         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56510             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56511         }
56512
56513         this.updateSplitters();
56514
56515         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56516             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56517             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56518         }
56519
56520         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56521             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56522             this.hmenu.add(
56523                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56524                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56525             );
56526             if(this.grid.enableColLock !== false){
56527                 this.hmenu.add('-',
56528                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56529                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56530                 );
56531             }
56532             if (Roo.isTouch) {
56533                  this.hmenu.add('-',
56534                     {id:"wider", text: this.columnsWiderText},
56535                     {id:"narrow", text: this.columnsNarrowText }
56536                 );
56537                 
56538                  
56539             }
56540             
56541             if(this.grid.enableColumnHide !== false){
56542
56543                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56544                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56545                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56546
56547                 this.hmenu.add('-',
56548                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56549                 );
56550             }
56551             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56552
56553             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56554         }
56555
56556         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56557             this.dd = new Roo.grid.GridDragZone(this.grid, {
56558                 ddGroup : this.grid.ddGroup || 'GridDD'
56559             });
56560             
56561         }
56562
56563         /*
56564         for(var i = 0; i < colCount; i++){
56565             if(cm.isHidden(i)){
56566                 this.hideColumn(i);
56567             }
56568             if(cm.config[i].align){
56569                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56570                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56571             }
56572         }*/
56573         
56574         this.updateHeaderSortState();
56575
56576         this.beforeInitialResize();
56577         this.layout(true);
56578
56579         // two part rendering gives faster view to the user
56580         this.renderPhase2.defer(1, this);
56581     },
56582
56583     renderPhase2 : function(){
56584         // render the rows now
56585         this.refresh();
56586         if(this.grid.autoSizeColumns){
56587             this.autoSizeColumns();
56588         }
56589     },
56590
56591     beforeInitialResize : function(){
56592
56593     },
56594
56595     onColumnSplitterMoved : function(i, w){
56596         this.userResized = true;
56597         var cm = this.grid.colModel;
56598         cm.setColumnWidth(i, w, true);
56599         var cid = cm.getColumnId(i);
56600         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56601         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56602         this.updateSplitters();
56603         this.layout();
56604         this.grid.fireEvent("columnresize", i, w);
56605     },
56606
56607     syncRowHeights : function(startIndex, endIndex){
56608         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56609             startIndex = startIndex || 0;
56610             var mrows = this.getBodyTable().rows;
56611             var lrows = this.getLockedTable().rows;
56612             var len = mrows.length-1;
56613             endIndex = Math.min(endIndex || len, len);
56614             for(var i = startIndex; i <= endIndex; i++){
56615                 var m = mrows[i], l = lrows[i];
56616                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56617                 m.style.height = l.style.height = h + "px";
56618             }
56619         }
56620     },
56621
56622     layout : function(initialRender, is2ndPass){
56623         var g = this.grid;
56624         var auto = g.autoHeight;
56625         var scrollOffset = 16;
56626         var c = g.getGridEl(), cm = this.cm,
56627                 expandCol = g.autoExpandColumn,
56628                 gv = this;
56629         //c.beginMeasure();
56630
56631         if(!c.dom.offsetWidth){ // display:none?
56632             if(initialRender){
56633                 this.lockedWrap.show();
56634                 this.mainWrap.show();
56635             }
56636             return;
56637         }
56638
56639         var hasLock = this.cm.isLocked(0);
56640
56641         var tbh = this.headerPanel.getHeight();
56642         var bbh = this.footerPanel.getHeight();
56643
56644         if(auto){
56645             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56646             var newHeight = ch + c.getBorderWidth("tb");
56647             if(g.maxHeight){
56648                 newHeight = Math.min(g.maxHeight, newHeight);
56649             }
56650             c.setHeight(newHeight);
56651         }
56652
56653         if(g.autoWidth){
56654             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56655         }
56656
56657         var s = this.scroller;
56658
56659         var csize = c.getSize(true);
56660
56661         this.el.setSize(csize.width, csize.height);
56662
56663         this.headerPanel.setWidth(csize.width);
56664         this.footerPanel.setWidth(csize.width);
56665
56666         var hdHeight = this.mainHd.getHeight();
56667         var vw = csize.width;
56668         var vh = csize.height - (tbh + bbh);
56669
56670         s.setSize(vw, vh);
56671
56672         var bt = this.getBodyTable();
56673         
56674         if(cm.getLockedCount() == cm.config.length){
56675             bt = this.getLockedTable();
56676         }
56677         
56678         var ltWidth = hasLock ?
56679                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56680
56681         var scrollHeight = bt.offsetHeight;
56682         var scrollWidth = ltWidth + bt.offsetWidth;
56683         var vscroll = false, hscroll = false;
56684
56685         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56686
56687         var lw = this.lockedWrap, mw = this.mainWrap;
56688         var lb = this.lockedBody, mb = this.mainBody;
56689
56690         setTimeout(function(){
56691             var t = s.dom.offsetTop;
56692             var w = s.dom.clientWidth,
56693                 h = s.dom.clientHeight;
56694
56695             lw.setTop(t);
56696             lw.setSize(ltWidth, h);
56697
56698             mw.setLeftTop(ltWidth, t);
56699             mw.setSize(w-ltWidth, h);
56700
56701             lb.setHeight(h-hdHeight);
56702             mb.setHeight(h-hdHeight);
56703
56704             if(is2ndPass !== true && !gv.userResized && expandCol){
56705                 // high speed resize without full column calculation
56706                 
56707                 var ci = cm.getIndexById(expandCol);
56708                 if (ci < 0) {
56709                     ci = cm.findColumnIndex(expandCol);
56710                 }
56711                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56712                 var expandId = cm.getColumnId(ci);
56713                 var  tw = cm.getTotalWidth(false);
56714                 var currentWidth = cm.getColumnWidth(ci);
56715                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56716                 if(currentWidth != cw){
56717                     cm.setColumnWidth(ci, cw, true);
56718                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56719                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56720                     gv.updateSplitters();
56721                     gv.layout(false, true);
56722                 }
56723             }
56724
56725             if(initialRender){
56726                 lw.show();
56727                 mw.show();
56728             }
56729             //c.endMeasure();
56730         }, 10);
56731     },
56732
56733     onWindowResize : function(){
56734         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56735             return;
56736         }
56737         this.layout();
56738     },
56739
56740     appendFooter : function(parentEl){
56741         return null;
56742     },
56743
56744     sortAscText : "Sort Ascending",
56745     sortDescText : "Sort Descending",
56746     lockText : "Lock Column",
56747     unlockText : "Unlock Column",
56748     columnsText : "Columns",
56749  
56750     columnsWiderText : "Wider",
56751     columnsNarrowText : "Thinner"
56752 });
56753
56754
56755 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56756     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56757     this.proxy.el.addClass('x-grid3-col-dd');
56758 };
56759
56760 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56761     handleMouseDown : function(e){
56762
56763     },
56764
56765     callHandleMouseDown : function(e){
56766         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56767     }
56768 });
56769 /*
56770  * Based on:
56771  * Ext JS Library 1.1.1
56772  * Copyright(c) 2006-2007, Ext JS, LLC.
56773  *
56774  * Originally Released Under LGPL - original licence link has changed is not relivant.
56775  *
56776  * Fork - LGPL
56777  * <script type="text/javascript">
56778  */
56779  
56780 // private
56781 // This is a support class used internally by the Grid components
56782 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56783     this.grid = grid;
56784     this.view = grid.getView();
56785     this.proxy = this.view.resizeProxy;
56786     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56787         "gridSplitters" + this.grid.getGridEl().id, {
56788         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56789     });
56790     this.setHandleElId(Roo.id(hd));
56791     this.setOuterHandleElId(Roo.id(hd2));
56792     this.scroll = false;
56793 };
56794 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56795     fly: Roo.Element.fly,
56796
56797     b4StartDrag : function(x, y){
56798         this.view.headersDisabled = true;
56799         this.proxy.setHeight(this.view.mainWrap.getHeight());
56800         var w = this.cm.getColumnWidth(this.cellIndex);
56801         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56802         this.resetConstraints();
56803         this.setXConstraint(minw, 1000);
56804         this.setYConstraint(0, 0);
56805         this.minX = x - minw;
56806         this.maxX = x + 1000;
56807         this.startPos = x;
56808         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56809     },
56810
56811
56812     handleMouseDown : function(e){
56813         ev = Roo.EventObject.setEvent(e);
56814         var t = this.fly(ev.getTarget());
56815         if(t.hasClass("x-grid-split")){
56816             this.cellIndex = this.view.getCellIndex(t.dom);
56817             this.split = t.dom;
56818             this.cm = this.grid.colModel;
56819             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56820                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56821             }
56822         }
56823     },
56824
56825     endDrag : function(e){
56826         this.view.headersDisabled = false;
56827         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56828         var diff = endX - this.startPos;
56829         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56830     },
56831
56832     autoOffset : function(){
56833         this.setDelta(0,0);
56834     }
56835 });/*
56836  * Based on:
56837  * Ext JS Library 1.1.1
56838  * Copyright(c) 2006-2007, Ext JS, LLC.
56839  *
56840  * Originally Released Under LGPL - original licence link has changed is not relivant.
56841  *
56842  * Fork - LGPL
56843  * <script type="text/javascript">
56844  */
56845  
56846 // private
56847 // This is a support class used internally by the Grid components
56848 Roo.grid.GridDragZone = function(grid, config){
56849     this.view = grid.getView();
56850     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56851     if(this.view.lockedBody){
56852         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56853         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56854     }
56855     this.scroll = false;
56856     this.grid = grid;
56857     this.ddel = document.createElement('div');
56858     this.ddel.className = 'x-grid-dd-wrap';
56859 };
56860
56861 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56862     ddGroup : "GridDD",
56863
56864     getDragData : function(e){
56865         var t = Roo.lib.Event.getTarget(e);
56866         var rowIndex = this.view.findRowIndex(t);
56867         var sm = this.grid.selModel;
56868             
56869         //Roo.log(rowIndex);
56870         
56871         if (sm.getSelectedCell) {
56872             // cell selection..
56873             if (!sm.getSelectedCell()) {
56874                 return false;
56875             }
56876             if (rowIndex != sm.getSelectedCell()[0]) {
56877                 return false;
56878             }
56879         
56880         }
56881         
56882         if(rowIndex !== false){
56883             
56884             // if editorgrid.. 
56885             
56886             
56887             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56888                
56889             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56890               //  
56891             //}
56892             if (e.hasModifier()){
56893                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56894             }
56895             
56896             Roo.log("getDragData");
56897             
56898             return {
56899                 grid: this.grid,
56900                 ddel: this.ddel,
56901                 rowIndex: rowIndex,
56902                 selections:sm.getSelections ? sm.getSelections() : (
56903                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56904                 )
56905             };
56906         }
56907         return false;
56908     },
56909
56910     onInitDrag : function(e){
56911         var data = this.dragData;
56912         this.ddel.innerHTML = this.grid.getDragDropText();
56913         this.proxy.update(this.ddel);
56914         // fire start drag?
56915     },
56916
56917     afterRepair : function(){
56918         this.dragging = false;
56919     },
56920
56921     getRepairXY : function(e, data){
56922         return false;
56923     },
56924
56925     onEndDrag : function(data, e){
56926         // fire end drag?
56927     },
56928
56929     onValidDrop : function(dd, e, id){
56930         // fire drag drop?
56931         this.hideProxy();
56932     },
56933
56934     beforeInvalidDrop : function(e, id){
56935
56936     }
56937 });/*
56938  * Based on:
56939  * Ext JS Library 1.1.1
56940  * Copyright(c) 2006-2007, Ext JS, LLC.
56941  *
56942  * Originally Released Under LGPL - original licence link has changed is not relivant.
56943  *
56944  * Fork - LGPL
56945  * <script type="text/javascript">
56946  */
56947  
56948
56949 /**
56950  * @class Roo.grid.ColumnModel
56951  * @extends Roo.util.Observable
56952  * This is the default implementation of a ColumnModel used by the Grid. It defines
56953  * the columns in the grid.
56954  * <br>Usage:<br>
56955  <pre><code>
56956  var colModel = new Roo.grid.ColumnModel([
56957         {header: "Ticker", width: 60, sortable: true, locked: true},
56958         {header: "Company Name", width: 150, sortable: true},
56959         {header: "Market Cap.", width: 100, sortable: true},
56960         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56961         {header: "Employees", width: 100, sortable: true, resizable: false}
56962  ]);
56963  </code></pre>
56964  * <p>
56965  
56966  * The config options listed for this class are options which may appear in each
56967  * individual column definition.
56968  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56969  * @constructor
56970  * @param {Object} config An Array of column config objects. See this class's
56971  * config objects for details.
56972 */
56973 Roo.grid.ColumnModel = function(config){
56974         /**
56975      * The config passed into the constructor
56976      */
56977     this.config = config;
56978     this.lookup = {};
56979
56980     // if no id, create one
56981     // if the column does not have a dataIndex mapping,
56982     // map it to the order it is in the config
56983     for(var i = 0, len = config.length; i < len; i++){
56984         var c = config[i];
56985         if(typeof c.dataIndex == "undefined"){
56986             c.dataIndex = i;
56987         }
56988         if(typeof c.renderer == "string"){
56989             c.renderer = Roo.util.Format[c.renderer];
56990         }
56991         if(typeof c.id == "undefined"){
56992             c.id = Roo.id();
56993         }
56994         if(c.editor && c.editor.xtype){
56995             c.editor  = Roo.factory(c.editor, Roo.grid);
56996         }
56997         if(c.editor && c.editor.isFormField){
56998             c.editor = new Roo.grid.GridEditor(c.editor);
56999         }
57000         this.lookup[c.id] = c;
57001     }
57002
57003     /**
57004      * The width of columns which have no width specified (defaults to 100)
57005      * @type Number
57006      */
57007     this.defaultWidth = 100;
57008
57009     /**
57010      * Default sortable of columns which have no sortable specified (defaults to false)
57011      * @type Boolean
57012      */
57013     this.defaultSortable = false;
57014
57015     this.addEvents({
57016         /**
57017              * @event widthchange
57018              * Fires when the width of a column changes.
57019              * @param {ColumnModel} this
57020              * @param {Number} columnIndex The column index
57021              * @param {Number} newWidth The new width
57022              */
57023             "widthchange": true,
57024         /**
57025              * @event headerchange
57026              * Fires when the text of a header changes.
57027              * @param {ColumnModel} this
57028              * @param {Number} columnIndex The column index
57029              * @param {Number} newText The new header text
57030              */
57031             "headerchange": true,
57032         /**
57033              * @event hiddenchange
57034              * Fires when a column is hidden or "unhidden".
57035              * @param {ColumnModel} this
57036              * @param {Number} columnIndex The column index
57037              * @param {Boolean} hidden true if hidden, false otherwise
57038              */
57039             "hiddenchange": true,
57040             /**
57041          * @event columnmoved
57042          * Fires when a column is moved.
57043          * @param {ColumnModel} this
57044          * @param {Number} oldIndex
57045          * @param {Number} newIndex
57046          */
57047         "columnmoved" : true,
57048         /**
57049          * @event columlockchange
57050          * Fires when a column's locked state is changed
57051          * @param {ColumnModel} this
57052          * @param {Number} colIndex
57053          * @param {Boolean} locked true if locked
57054          */
57055         "columnlockchange" : true
57056     });
57057     Roo.grid.ColumnModel.superclass.constructor.call(this);
57058 };
57059 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57060     /**
57061      * @cfg {String} header The header text to display in the Grid view.
57062      */
57063     /**
57064      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57065      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57066      * specified, the column's index is used as an index into the Record's data Array.
57067      */
57068     /**
57069      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57070      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57071      */
57072     /**
57073      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57074      * Defaults to the value of the {@link #defaultSortable} property.
57075      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57076      */
57077     /**
57078      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57079      */
57080     /**
57081      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57082      */
57083     /**
57084      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57085      */
57086     /**
57087      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57088      */
57089     /**
57090      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57091      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57092      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57093      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57094      */
57095        /**
57096      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57097      */
57098     /**
57099      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57100      */
57101     /**
57102      * @cfg {String} cursor (Optional)
57103      */
57104     /**
57105      * @cfg {String} tooltip (Optional)
57106      */
57107     /**
57108      * @cfg {Number} xs (Optional)
57109      */
57110     /**
57111      * @cfg {Number} sm (Optional)
57112      */
57113     /**
57114      * @cfg {Number} md (Optional)
57115      */
57116     /**
57117      * @cfg {Number} lg (Optional)
57118      */
57119     /**
57120      * Returns the id of the column at the specified index.
57121      * @param {Number} index The column index
57122      * @return {String} the id
57123      */
57124     getColumnId : function(index){
57125         return this.config[index].id;
57126     },
57127
57128     /**
57129      * Returns the column for a specified id.
57130      * @param {String} id The column id
57131      * @return {Object} the column
57132      */
57133     getColumnById : function(id){
57134         return this.lookup[id];
57135     },
57136
57137     
57138     /**
57139      * Returns the column for a specified dataIndex.
57140      * @param {String} dataIndex The column dataIndex
57141      * @return {Object|Boolean} the column or false if not found
57142      */
57143     getColumnByDataIndex: function(dataIndex){
57144         var index = this.findColumnIndex(dataIndex);
57145         return index > -1 ? this.config[index] : false;
57146     },
57147     
57148     /**
57149      * Returns the index for a specified column id.
57150      * @param {String} id The column id
57151      * @return {Number} the index, or -1 if not found
57152      */
57153     getIndexById : function(id){
57154         for(var i = 0, len = this.config.length; i < len; i++){
57155             if(this.config[i].id == id){
57156                 return i;
57157             }
57158         }
57159         return -1;
57160     },
57161     
57162     /**
57163      * Returns the index for a specified column dataIndex.
57164      * @param {String} dataIndex The column dataIndex
57165      * @return {Number} the index, or -1 if not found
57166      */
57167     
57168     findColumnIndex : function(dataIndex){
57169         for(var i = 0, len = this.config.length; i < len; i++){
57170             if(this.config[i].dataIndex == dataIndex){
57171                 return i;
57172             }
57173         }
57174         return -1;
57175     },
57176     
57177     
57178     moveColumn : function(oldIndex, newIndex){
57179         var c = this.config[oldIndex];
57180         this.config.splice(oldIndex, 1);
57181         this.config.splice(newIndex, 0, c);
57182         this.dataMap = null;
57183         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57184     },
57185
57186     isLocked : function(colIndex){
57187         return this.config[colIndex].locked === true;
57188     },
57189
57190     setLocked : function(colIndex, value, suppressEvent){
57191         if(this.isLocked(colIndex) == value){
57192             return;
57193         }
57194         this.config[colIndex].locked = value;
57195         if(!suppressEvent){
57196             this.fireEvent("columnlockchange", this, colIndex, value);
57197         }
57198     },
57199
57200     getTotalLockedWidth : function(){
57201         var totalWidth = 0;
57202         for(var i = 0; i < this.config.length; i++){
57203             if(this.isLocked(i) && !this.isHidden(i)){
57204                 this.totalWidth += this.getColumnWidth(i);
57205             }
57206         }
57207         return totalWidth;
57208     },
57209
57210     getLockedCount : function(){
57211         for(var i = 0, len = this.config.length; i < len; i++){
57212             if(!this.isLocked(i)){
57213                 return i;
57214             }
57215         }
57216         
57217         return this.config.length;
57218     },
57219
57220     /**
57221      * Returns the number of columns.
57222      * @return {Number}
57223      */
57224     getColumnCount : function(visibleOnly){
57225         if(visibleOnly === true){
57226             var c = 0;
57227             for(var i = 0, len = this.config.length; i < len; i++){
57228                 if(!this.isHidden(i)){
57229                     c++;
57230                 }
57231             }
57232             return c;
57233         }
57234         return this.config.length;
57235     },
57236
57237     /**
57238      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57239      * @param {Function} fn
57240      * @param {Object} scope (optional)
57241      * @return {Array} result
57242      */
57243     getColumnsBy : function(fn, scope){
57244         var r = [];
57245         for(var i = 0, len = this.config.length; i < len; i++){
57246             var c = this.config[i];
57247             if(fn.call(scope||this, c, i) === true){
57248                 r[r.length] = c;
57249             }
57250         }
57251         return r;
57252     },
57253
57254     /**
57255      * Returns true if the specified column is sortable.
57256      * @param {Number} col The column index
57257      * @return {Boolean}
57258      */
57259     isSortable : function(col){
57260         if(typeof this.config[col].sortable == "undefined"){
57261             return this.defaultSortable;
57262         }
57263         return this.config[col].sortable;
57264     },
57265
57266     /**
57267      * Returns the rendering (formatting) function defined for the column.
57268      * @param {Number} col The column index.
57269      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57270      */
57271     getRenderer : function(col){
57272         if(!this.config[col].renderer){
57273             return Roo.grid.ColumnModel.defaultRenderer;
57274         }
57275         return this.config[col].renderer;
57276     },
57277
57278     /**
57279      * Sets the rendering (formatting) function for a column.
57280      * @param {Number} col The column index
57281      * @param {Function} fn The function to use to process the cell's raw data
57282      * to return HTML markup for the grid view. The render function is called with
57283      * the following parameters:<ul>
57284      * <li>Data value.</li>
57285      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57286      * <li>css A CSS style string to apply to the table cell.</li>
57287      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57288      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57289      * <li>Row index</li>
57290      * <li>Column index</li>
57291      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57292      */
57293     setRenderer : function(col, fn){
57294         this.config[col].renderer = fn;
57295     },
57296
57297     /**
57298      * Returns the width for the specified column.
57299      * @param {Number} col The column index
57300      * @return {Number}
57301      */
57302     getColumnWidth : function(col){
57303         return this.config[col].width * 1 || this.defaultWidth;
57304     },
57305
57306     /**
57307      * Sets the width for a column.
57308      * @param {Number} col The column index
57309      * @param {Number} width The new width
57310      */
57311     setColumnWidth : function(col, width, suppressEvent){
57312         this.config[col].width = width;
57313         this.totalWidth = null;
57314         if(!suppressEvent){
57315              this.fireEvent("widthchange", this, col, width);
57316         }
57317     },
57318
57319     /**
57320      * Returns the total width of all columns.
57321      * @param {Boolean} includeHidden True to include hidden column widths
57322      * @return {Number}
57323      */
57324     getTotalWidth : function(includeHidden){
57325         if(!this.totalWidth){
57326             this.totalWidth = 0;
57327             for(var i = 0, len = this.config.length; i < len; i++){
57328                 if(includeHidden || !this.isHidden(i)){
57329                     this.totalWidth += this.getColumnWidth(i);
57330                 }
57331             }
57332         }
57333         return this.totalWidth;
57334     },
57335
57336     /**
57337      * Returns the header for the specified column.
57338      * @param {Number} col The column index
57339      * @return {String}
57340      */
57341     getColumnHeader : function(col){
57342         return this.config[col].header;
57343     },
57344
57345     /**
57346      * Sets the header for a column.
57347      * @param {Number} col The column index
57348      * @param {String} header The new header
57349      */
57350     setColumnHeader : function(col, header){
57351         this.config[col].header = header;
57352         this.fireEvent("headerchange", this, col, header);
57353     },
57354
57355     /**
57356      * Returns the tooltip for the specified column.
57357      * @param {Number} col The column index
57358      * @return {String}
57359      */
57360     getColumnTooltip : function(col){
57361             return this.config[col].tooltip;
57362     },
57363     /**
57364      * Sets the tooltip for a column.
57365      * @param {Number} col The column index
57366      * @param {String} tooltip The new tooltip
57367      */
57368     setColumnTooltip : function(col, tooltip){
57369             this.config[col].tooltip = tooltip;
57370     },
57371
57372     /**
57373      * Returns the dataIndex for the specified column.
57374      * @param {Number} col The column index
57375      * @return {Number}
57376      */
57377     getDataIndex : function(col){
57378         return this.config[col].dataIndex;
57379     },
57380
57381     /**
57382      * Sets the dataIndex for a column.
57383      * @param {Number} col The column index
57384      * @param {Number} dataIndex The new dataIndex
57385      */
57386     setDataIndex : function(col, dataIndex){
57387         this.config[col].dataIndex = dataIndex;
57388     },
57389
57390     
57391     
57392     /**
57393      * Returns true if the cell is editable.
57394      * @param {Number} colIndex The column index
57395      * @param {Number} rowIndex The row index - this is nto actually used..?
57396      * @return {Boolean}
57397      */
57398     isCellEditable : function(colIndex, rowIndex){
57399         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57400     },
57401
57402     /**
57403      * Returns the editor defined for the cell/column.
57404      * return false or null to disable editing.
57405      * @param {Number} colIndex The column index
57406      * @param {Number} rowIndex The row index
57407      * @return {Object}
57408      */
57409     getCellEditor : function(colIndex, rowIndex){
57410         return this.config[colIndex].editor;
57411     },
57412
57413     /**
57414      * Sets if a column is editable.
57415      * @param {Number} col The column index
57416      * @param {Boolean} editable True if the column is editable
57417      */
57418     setEditable : function(col, editable){
57419         this.config[col].editable = editable;
57420     },
57421
57422
57423     /**
57424      * Returns true if the column is hidden.
57425      * @param {Number} colIndex The column index
57426      * @return {Boolean}
57427      */
57428     isHidden : function(colIndex){
57429         return this.config[colIndex].hidden;
57430     },
57431
57432
57433     /**
57434      * Returns true if the column width cannot be changed
57435      */
57436     isFixed : function(colIndex){
57437         return this.config[colIndex].fixed;
57438     },
57439
57440     /**
57441      * Returns true if the column can be resized
57442      * @return {Boolean}
57443      */
57444     isResizable : function(colIndex){
57445         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57446     },
57447     /**
57448      * Sets if a column is hidden.
57449      * @param {Number} colIndex The column index
57450      * @param {Boolean} hidden True if the column is hidden
57451      */
57452     setHidden : function(colIndex, hidden){
57453         this.config[colIndex].hidden = hidden;
57454         this.totalWidth = null;
57455         this.fireEvent("hiddenchange", this, colIndex, hidden);
57456     },
57457
57458     /**
57459      * Sets the editor for a column.
57460      * @param {Number} col The column index
57461      * @param {Object} editor The editor object
57462      */
57463     setEditor : function(col, editor){
57464         this.config[col].editor = editor;
57465     }
57466 });
57467
57468 Roo.grid.ColumnModel.defaultRenderer = function(value)
57469 {
57470     if(typeof value == "object") {
57471         return value;
57472     }
57473         if(typeof value == "string" && value.length < 1){
57474             return "&#160;";
57475         }
57476     
57477         return String.format("{0}", value);
57478 };
57479
57480 // Alias for backwards compatibility
57481 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57482 /*
57483  * Based on:
57484  * Ext JS Library 1.1.1
57485  * Copyright(c) 2006-2007, Ext JS, LLC.
57486  *
57487  * Originally Released Under LGPL - original licence link has changed is not relivant.
57488  *
57489  * Fork - LGPL
57490  * <script type="text/javascript">
57491  */
57492
57493 /**
57494  * @class Roo.grid.AbstractSelectionModel
57495  * @extends Roo.util.Observable
57496  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57497  * implemented by descendant classes.  This class should not be directly instantiated.
57498  * @constructor
57499  */
57500 Roo.grid.AbstractSelectionModel = function(){
57501     this.locked = false;
57502     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57503 };
57504
57505 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57506     /** @ignore Called by the grid automatically. Do not call directly. */
57507     init : function(grid){
57508         this.grid = grid;
57509         this.initEvents();
57510     },
57511
57512     /**
57513      * Locks the selections.
57514      */
57515     lock : function(){
57516         this.locked = true;
57517     },
57518
57519     /**
57520      * Unlocks the selections.
57521      */
57522     unlock : function(){
57523         this.locked = false;
57524     },
57525
57526     /**
57527      * Returns true if the selections are locked.
57528      * @return {Boolean}
57529      */
57530     isLocked : function(){
57531         return this.locked;
57532     }
57533 });/*
57534  * Based on:
57535  * Ext JS Library 1.1.1
57536  * Copyright(c) 2006-2007, Ext JS, LLC.
57537  *
57538  * Originally Released Under LGPL - original licence link has changed is not relivant.
57539  *
57540  * Fork - LGPL
57541  * <script type="text/javascript">
57542  */
57543 /**
57544  * @extends Roo.grid.AbstractSelectionModel
57545  * @class Roo.grid.RowSelectionModel
57546  * The default SelectionModel used by {@link Roo.grid.Grid}.
57547  * It supports multiple selections and keyboard selection/navigation. 
57548  * @constructor
57549  * @param {Object} config
57550  */
57551 Roo.grid.RowSelectionModel = function(config){
57552     Roo.apply(this, config);
57553     this.selections = new Roo.util.MixedCollection(false, function(o){
57554         return o.id;
57555     });
57556
57557     this.last = false;
57558     this.lastActive = false;
57559
57560     this.addEvents({
57561         /**
57562              * @event selectionchange
57563              * Fires when the selection changes
57564              * @param {SelectionModel} this
57565              */
57566             "selectionchange" : true,
57567         /**
57568              * @event afterselectionchange
57569              * Fires after the selection changes (eg. by key press or clicking)
57570              * @param {SelectionModel} this
57571              */
57572             "afterselectionchange" : true,
57573         /**
57574              * @event beforerowselect
57575              * Fires when a row is selected being selected, return false to cancel.
57576              * @param {SelectionModel} this
57577              * @param {Number} rowIndex The selected index
57578              * @param {Boolean} keepExisting False if other selections will be cleared
57579              */
57580             "beforerowselect" : true,
57581         /**
57582              * @event rowselect
57583              * Fires when a row is selected.
57584              * @param {SelectionModel} this
57585              * @param {Number} rowIndex The selected index
57586              * @param {Roo.data.Record} r The record
57587              */
57588             "rowselect" : true,
57589         /**
57590              * @event rowdeselect
57591              * Fires when a row is deselected.
57592              * @param {SelectionModel} this
57593              * @param {Number} rowIndex The selected index
57594              */
57595         "rowdeselect" : true
57596     });
57597     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57598     this.locked = false;
57599 };
57600
57601 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57602     /**
57603      * @cfg {Boolean} singleSelect
57604      * True to allow selection of only one row at a time (defaults to false)
57605      */
57606     singleSelect : false,
57607
57608     // private
57609     initEvents : function(){
57610
57611         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57612             this.grid.on("mousedown", this.handleMouseDown, this);
57613         }else{ // allow click to work like normal
57614             this.grid.on("rowclick", this.handleDragableRowClick, this);
57615         }
57616
57617         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57618             "up" : function(e){
57619                 if(!e.shiftKey){
57620                     this.selectPrevious(e.shiftKey);
57621                 }else if(this.last !== false && this.lastActive !== false){
57622                     var last = this.last;
57623                     this.selectRange(this.last,  this.lastActive-1);
57624                     this.grid.getView().focusRow(this.lastActive);
57625                     if(last !== false){
57626                         this.last = last;
57627                     }
57628                 }else{
57629                     this.selectFirstRow();
57630                 }
57631                 this.fireEvent("afterselectionchange", this);
57632             },
57633             "down" : function(e){
57634                 if(!e.shiftKey){
57635                     this.selectNext(e.shiftKey);
57636                 }else if(this.last !== false && this.lastActive !== false){
57637                     var last = this.last;
57638                     this.selectRange(this.last,  this.lastActive+1);
57639                     this.grid.getView().focusRow(this.lastActive);
57640                     if(last !== false){
57641                         this.last = last;
57642                     }
57643                 }else{
57644                     this.selectFirstRow();
57645                 }
57646                 this.fireEvent("afterselectionchange", this);
57647             },
57648             scope: this
57649         });
57650
57651         var view = this.grid.view;
57652         view.on("refresh", this.onRefresh, this);
57653         view.on("rowupdated", this.onRowUpdated, this);
57654         view.on("rowremoved", this.onRemove, this);
57655     },
57656
57657     // private
57658     onRefresh : function(){
57659         var ds = this.grid.dataSource, i, v = this.grid.view;
57660         var s = this.selections;
57661         s.each(function(r){
57662             if((i = ds.indexOfId(r.id)) != -1){
57663                 v.onRowSelect(i);
57664                 s.add(ds.getAt(i)); // updating the selection relate data
57665             }else{
57666                 s.remove(r);
57667             }
57668         });
57669     },
57670
57671     // private
57672     onRemove : function(v, index, r){
57673         this.selections.remove(r);
57674     },
57675
57676     // private
57677     onRowUpdated : function(v, index, r){
57678         if(this.isSelected(r)){
57679             v.onRowSelect(index);
57680         }
57681     },
57682
57683     /**
57684      * Select records.
57685      * @param {Array} records The records to select
57686      * @param {Boolean} keepExisting (optional) True to keep existing selections
57687      */
57688     selectRecords : function(records, keepExisting){
57689         if(!keepExisting){
57690             this.clearSelections();
57691         }
57692         var ds = this.grid.dataSource;
57693         for(var i = 0, len = records.length; i < len; i++){
57694             this.selectRow(ds.indexOf(records[i]), true);
57695         }
57696     },
57697
57698     /**
57699      * Gets the number of selected rows.
57700      * @return {Number}
57701      */
57702     getCount : function(){
57703         return this.selections.length;
57704     },
57705
57706     /**
57707      * Selects the first row in the grid.
57708      */
57709     selectFirstRow : function(){
57710         this.selectRow(0);
57711     },
57712
57713     /**
57714      * Select the last row.
57715      * @param {Boolean} keepExisting (optional) True to keep existing selections
57716      */
57717     selectLastRow : function(keepExisting){
57718         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57719     },
57720
57721     /**
57722      * Selects the row immediately following the last selected row.
57723      * @param {Boolean} keepExisting (optional) True to keep existing selections
57724      */
57725     selectNext : function(keepExisting){
57726         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57727             this.selectRow(this.last+1, keepExisting);
57728             this.grid.getView().focusRow(this.last);
57729         }
57730     },
57731
57732     /**
57733      * Selects the row that precedes the last selected row.
57734      * @param {Boolean} keepExisting (optional) True to keep existing selections
57735      */
57736     selectPrevious : function(keepExisting){
57737         if(this.last){
57738             this.selectRow(this.last-1, keepExisting);
57739             this.grid.getView().focusRow(this.last);
57740         }
57741     },
57742
57743     /**
57744      * Returns the selected records
57745      * @return {Array} Array of selected records
57746      */
57747     getSelections : function(){
57748         return [].concat(this.selections.items);
57749     },
57750
57751     /**
57752      * Returns the first selected record.
57753      * @return {Record}
57754      */
57755     getSelected : function(){
57756         return this.selections.itemAt(0);
57757     },
57758
57759
57760     /**
57761      * Clears all selections.
57762      */
57763     clearSelections : function(fast){
57764         if(this.locked) {
57765             return;
57766         }
57767         if(fast !== true){
57768             var ds = this.grid.dataSource;
57769             var s = this.selections;
57770             s.each(function(r){
57771                 this.deselectRow(ds.indexOfId(r.id));
57772             }, this);
57773             s.clear();
57774         }else{
57775             this.selections.clear();
57776         }
57777         this.last = false;
57778     },
57779
57780
57781     /**
57782      * Selects all rows.
57783      */
57784     selectAll : function(){
57785         if(this.locked) {
57786             return;
57787         }
57788         this.selections.clear();
57789         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57790             this.selectRow(i, true);
57791         }
57792     },
57793
57794     /**
57795      * Returns True if there is a selection.
57796      * @return {Boolean}
57797      */
57798     hasSelection : function(){
57799         return this.selections.length > 0;
57800     },
57801
57802     /**
57803      * Returns True if the specified row is selected.
57804      * @param {Number/Record} record The record or index of the record to check
57805      * @return {Boolean}
57806      */
57807     isSelected : function(index){
57808         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57809         return (r && this.selections.key(r.id) ? true : false);
57810     },
57811
57812     /**
57813      * Returns True if the specified record id is selected.
57814      * @param {String} id The id of record to check
57815      * @return {Boolean}
57816      */
57817     isIdSelected : function(id){
57818         return (this.selections.key(id) ? true : false);
57819     },
57820
57821     // private
57822     handleMouseDown : function(e, t){
57823         var view = this.grid.getView(), rowIndex;
57824         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57825             return;
57826         };
57827         if(e.shiftKey && this.last !== false){
57828             var last = this.last;
57829             this.selectRange(last, rowIndex, e.ctrlKey);
57830             this.last = last; // reset the last
57831             view.focusRow(rowIndex);
57832         }else{
57833             var isSelected = this.isSelected(rowIndex);
57834             if(e.button !== 0 && isSelected){
57835                 view.focusRow(rowIndex);
57836             }else if(e.ctrlKey && isSelected){
57837                 this.deselectRow(rowIndex);
57838             }else if(!isSelected){
57839                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57840                 view.focusRow(rowIndex);
57841             }
57842         }
57843         this.fireEvent("afterselectionchange", this);
57844     },
57845     // private
57846     handleDragableRowClick :  function(grid, rowIndex, e) 
57847     {
57848         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57849             this.selectRow(rowIndex, false);
57850             grid.view.focusRow(rowIndex);
57851              this.fireEvent("afterselectionchange", this);
57852         }
57853     },
57854     
57855     /**
57856      * Selects multiple rows.
57857      * @param {Array} rows Array of the indexes of the row to select
57858      * @param {Boolean} keepExisting (optional) True to keep existing selections
57859      */
57860     selectRows : function(rows, keepExisting){
57861         if(!keepExisting){
57862             this.clearSelections();
57863         }
57864         for(var i = 0, len = rows.length; i < len; i++){
57865             this.selectRow(rows[i], true);
57866         }
57867     },
57868
57869     /**
57870      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57871      * @param {Number} startRow The index of the first row in the range
57872      * @param {Number} endRow The index of the last row in the range
57873      * @param {Boolean} keepExisting (optional) True to retain existing selections
57874      */
57875     selectRange : function(startRow, endRow, keepExisting){
57876         if(this.locked) {
57877             return;
57878         }
57879         if(!keepExisting){
57880             this.clearSelections();
57881         }
57882         if(startRow <= endRow){
57883             for(var i = startRow; i <= endRow; i++){
57884                 this.selectRow(i, true);
57885             }
57886         }else{
57887             for(var i = startRow; i >= endRow; i--){
57888                 this.selectRow(i, true);
57889             }
57890         }
57891     },
57892
57893     /**
57894      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57895      * @param {Number} startRow The index of the first row in the range
57896      * @param {Number} endRow The index of the last row in the range
57897      */
57898     deselectRange : function(startRow, endRow, preventViewNotify){
57899         if(this.locked) {
57900             return;
57901         }
57902         for(var i = startRow; i <= endRow; i++){
57903             this.deselectRow(i, preventViewNotify);
57904         }
57905     },
57906
57907     /**
57908      * Selects a row.
57909      * @param {Number} row The index of the row to select
57910      * @param {Boolean} keepExisting (optional) True to keep existing selections
57911      */
57912     selectRow : function(index, keepExisting, preventViewNotify){
57913         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57914             return;
57915         }
57916         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57917             if(!keepExisting || this.singleSelect){
57918                 this.clearSelections();
57919             }
57920             var r = this.grid.dataSource.getAt(index);
57921             this.selections.add(r);
57922             this.last = this.lastActive = index;
57923             if(!preventViewNotify){
57924                 this.grid.getView().onRowSelect(index);
57925             }
57926             this.fireEvent("rowselect", this, index, r);
57927             this.fireEvent("selectionchange", this);
57928         }
57929     },
57930
57931     /**
57932      * Deselects a row.
57933      * @param {Number} row The index of the row to deselect
57934      */
57935     deselectRow : function(index, preventViewNotify){
57936         if(this.locked) {
57937             return;
57938         }
57939         if(this.last == index){
57940             this.last = false;
57941         }
57942         if(this.lastActive == index){
57943             this.lastActive = false;
57944         }
57945         var r = this.grid.dataSource.getAt(index);
57946         this.selections.remove(r);
57947         if(!preventViewNotify){
57948             this.grid.getView().onRowDeselect(index);
57949         }
57950         this.fireEvent("rowdeselect", this, index);
57951         this.fireEvent("selectionchange", this);
57952     },
57953
57954     // private
57955     restoreLast : function(){
57956         if(this._last){
57957             this.last = this._last;
57958         }
57959     },
57960
57961     // private
57962     acceptsNav : function(row, col, cm){
57963         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57964     },
57965
57966     // private
57967     onEditorKey : function(field, e){
57968         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57969         if(k == e.TAB){
57970             e.stopEvent();
57971             ed.completeEdit();
57972             if(e.shiftKey){
57973                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57974             }else{
57975                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57976             }
57977         }else if(k == e.ENTER && !e.ctrlKey){
57978             e.stopEvent();
57979             ed.completeEdit();
57980             if(e.shiftKey){
57981                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57982             }else{
57983                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57984             }
57985         }else if(k == e.ESC){
57986             ed.cancelEdit();
57987         }
57988         if(newCell){
57989             g.startEditing(newCell[0], newCell[1]);
57990         }
57991     }
57992 });/*
57993  * Based on:
57994  * Ext JS Library 1.1.1
57995  * Copyright(c) 2006-2007, Ext JS, LLC.
57996  *
57997  * Originally Released Under LGPL - original licence link has changed is not relivant.
57998  *
57999  * Fork - LGPL
58000  * <script type="text/javascript">
58001  */
58002 /**
58003  * @class Roo.grid.CellSelectionModel
58004  * @extends Roo.grid.AbstractSelectionModel
58005  * This class provides the basic implementation for cell selection in a grid.
58006  * @constructor
58007  * @param {Object} config The object containing the configuration of this model.
58008  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58009  */
58010 Roo.grid.CellSelectionModel = function(config){
58011     Roo.apply(this, config);
58012
58013     this.selection = null;
58014
58015     this.addEvents({
58016         /**
58017              * @event beforerowselect
58018              * Fires before a cell is selected.
58019              * @param {SelectionModel} this
58020              * @param {Number} rowIndex The selected row index
58021              * @param {Number} colIndex The selected cell index
58022              */
58023             "beforecellselect" : true,
58024         /**
58025              * @event cellselect
58026              * Fires when a cell is selected.
58027              * @param {SelectionModel} this
58028              * @param {Number} rowIndex The selected row index
58029              * @param {Number} colIndex The selected cell index
58030              */
58031             "cellselect" : true,
58032         /**
58033              * @event selectionchange
58034              * Fires when the active selection changes.
58035              * @param {SelectionModel} this
58036              * @param {Object} selection null for no selection or an object (o) with two properties
58037                 <ul>
58038                 <li>o.record: the record object for the row the selection is in</li>
58039                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58040                 </ul>
58041              */
58042             "selectionchange" : true,
58043         /**
58044              * @event tabend
58045              * Fires when the tab (or enter) was pressed on the last editable cell
58046              * You can use this to trigger add new row.
58047              * @param {SelectionModel} this
58048              */
58049             "tabend" : true,
58050          /**
58051              * @event beforeeditnext
58052              * Fires before the next editable sell is made active
58053              * You can use this to skip to another cell or fire the tabend
58054              *    if you set cell to false
58055              * @param {Object} eventdata object : { cell : [ row, col ] } 
58056              */
58057             "beforeeditnext" : true
58058     });
58059     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58060 };
58061
58062 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58063     
58064     enter_is_tab: false,
58065
58066     /** @ignore */
58067     initEvents : function(){
58068         this.grid.on("mousedown", this.handleMouseDown, this);
58069         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58070         var view = this.grid.view;
58071         view.on("refresh", this.onViewChange, this);
58072         view.on("rowupdated", this.onRowUpdated, this);
58073         view.on("beforerowremoved", this.clearSelections, this);
58074         view.on("beforerowsinserted", this.clearSelections, this);
58075         if(this.grid.isEditor){
58076             this.grid.on("beforeedit", this.beforeEdit,  this);
58077         }
58078     },
58079
58080         //private
58081     beforeEdit : function(e){
58082         this.select(e.row, e.column, false, true, e.record);
58083     },
58084
58085         //private
58086     onRowUpdated : function(v, index, r){
58087         if(this.selection && this.selection.record == r){
58088             v.onCellSelect(index, this.selection.cell[1]);
58089         }
58090     },
58091
58092         //private
58093     onViewChange : function(){
58094         this.clearSelections(true);
58095     },
58096
58097         /**
58098          * Returns the currently selected cell,.
58099          * @return {Array} The selected cell (row, column) or null if none selected.
58100          */
58101     getSelectedCell : function(){
58102         return this.selection ? this.selection.cell : null;
58103     },
58104
58105     /**
58106      * Clears all selections.
58107      * @param {Boolean} true to prevent the gridview from being notified about the change.
58108      */
58109     clearSelections : function(preventNotify){
58110         var s = this.selection;
58111         if(s){
58112             if(preventNotify !== true){
58113                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58114             }
58115             this.selection = null;
58116             this.fireEvent("selectionchange", this, null);
58117         }
58118     },
58119
58120     /**
58121      * Returns true if there is a selection.
58122      * @return {Boolean}
58123      */
58124     hasSelection : function(){
58125         return this.selection ? true : false;
58126     },
58127
58128     /** @ignore */
58129     handleMouseDown : function(e, t){
58130         var v = this.grid.getView();
58131         if(this.isLocked()){
58132             return;
58133         };
58134         var row = v.findRowIndex(t);
58135         var cell = v.findCellIndex(t);
58136         if(row !== false && cell !== false){
58137             this.select(row, cell);
58138         }
58139     },
58140
58141     /**
58142      * Selects a cell.
58143      * @param {Number} rowIndex
58144      * @param {Number} collIndex
58145      */
58146     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58147         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58148             this.clearSelections();
58149             r = r || this.grid.dataSource.getAt(rowIndex);
58150             this.selection = {
58151                 record : r,
58152                 cell : [rowIndex, colIndex]
58153             };
58154             if(!preventViewNotify){
58155                 var v = this.grid.getView();
58156                 v.onCellSelect(rowIndex, colIndex);
58157                 if(preventFocus !== true){
58158                     v.focusCell(rowIndex, colIndex);
58159                 }
58160             }
58161             this.fireEvent("cellselect", this, rowIndex, colIndex);
58162             this.fireEvent("selectionchange", this, this.selection);
58163         }
58164     },
58165
58166         //private
58167     isSelectable : function(rowIndex, colIndex, cm){
58168         return !cm.isHidden(colIndex);
58169     },
58170
58171     /** @ignore */
58172     handleKeyDown : function(e){
58173         //Roo.log('Cell Sel Model handleKeyDown');
58174         if(!e.isNavKeyPress()){
58175             return;
58176         }
58177         var g = this.grid, s = this.selection;
58178         if(!s){
58179             e.stopEvent();
58180             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58181             if(cell){
58182                 this.select(cell[0], cell[1]);
58183             }
58184             return;
58185         }
58186         var sm = this;
58187         var walk = function(row, col, step){
58188             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58189         };
58190         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58191         var newCell;
58192
58193       
58194
58195         switch(k){
58196             case e.TAB:
58197                 // handled by onEditorKey
58198                 if (g.isEditor && g.editing) {
58199                     return;
58200                 }
58201                 if(e.shiftKey) {
58202                     newCell = walk(r, c-1, -1);
58203                 } else {
58204                     newCell = walk(r, c+1, 1);
58205                 }
58206                 break;
58207             
58208             case e.DOWN:
58209                newCell = walk(r+1, c, 1);
58210                 break;
58211             
58212             case e.UP:
58213                 newCell = walk(r-1, c, -1);
58214                 break;
58215             
58216             case e.RIGHT:
58217                 newCell = walk(r, c+1, 1);
58218                 break;
58219             
58220             case e.LEFT:
58221                 newCell = walk(r, c-1, -1);
58222                 break;
58223             
58224             case e.ENTER:
58225                 
58226                 if(g.isEditor && !g.editing){
58227                    g.startEditing(r, c);
58228                    e.stopEvent();
58229                    return;
58230                 }
58231                 
58232                 
58233              break;
58234         };
58235         if(newCell){
58236             this.select(newCell[0], newCell[1]);
58237             e.stopEvent();
58238             
58239         }
58240     },
58241
58242     acceptsNav : function(row, col, cm){
58243         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58244     },
58245     /**
58246      * Selects a cell.
58247      * @param {Number} field (not used) - as it's normally used as a listener
58248      * @param {Number} e - event - fake it by using
58249      *
58250      * var e = Roo.EventObjectImpl.prototype;
58251      * e.keyCode = e.TAB
58252      *
58253      * 
58254      */
58255     onEditorKey : function(field, e){
58256         
58257         var k = e.getKey(),
58258             newCell,
58259             g = this.grid,
58260             ed = g.activeEditor,
58261             forward = false;
58262         ///Roo.log('onEditorKey' + k);
58263         
58264         
58265         if (this.enter_is_tab && k == e.ENTER) {
58266             k = e.TAB;
58267         }
58268         
58269         if(k == e.TAB){
58270             if(e.shiftKey){
58271                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58272             }else{
58273                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58274                 forward = true;
58275             }
58276             
58277             e.stopEvent();
58278             
58279         } else if(k == e.ENTER &&  !e.ctrlKey){
58280             ed.completeEdit();
58281             e.stopEvent();
58282             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58283         
58284                 } else if(k == e.ESC){
58285             ed.cancelEdit();
58286         }
58287                 
58288         if (newCell) {
58289             var ecall = { cell : newCell, forward : forward };
58290             this.fireEvent('beforeeditnext', ecall );
58291             newCell = ecall.cell;
58292                         forward = ecall.forward;
58293         }
58294                 
58295         if(newCell){
58296             //Roo.log('next cell after edit');
58297             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58298         } else if (forward) {
58299             // tabbed past last
58300             this.fireEvent.defer(100, this, ['tabend',this]);
58301         }
58302     }
58303 });/*
58304  * Based on:
58305  * Ext JS Library 1.1.1
58306  * Copyright(c) 2006-2007, Ext JS, LLC.
58307  *
58308  * Originally Released Under LGPL - original licence link has changed is not relivant.
58309  *
58310  * Fork - LGPL
58311  * <script type="text/javascript">
58312  */
58313  
58314 /**
58315  * @class Roo.grid.EditorGrid
58316  * @extends Roo.grid.Grid
58317  * Class for creating and editable grid.
58318  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58319  * The container MUST have some type of size defined for the grid to fill. The container will be 
58320  * automatically set to position relative if it isn't already.
58321  * @param {Object} dataSource The data model to bind to
58322  * @param {Object} colModel The column model with info about this grid's columns
58323  */
58324 Roo.grid.EditorGrid = function(container, config){
58325     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58326     this.getGridEl().addClass("xedit-grid");
58327
58328     if(!this.selModel){
58329         this.selModel = new Roo.grid.CellSelectionModel();
58330     }
58331
58332     this.activeEditor = null;
58333
58334         this.addEvents({
58335             /**
58336              * @event beforeedit
58337              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58338              * <ul style="padding:5px;padding-left:16px;">
58339              * <li>grid - This grid</li>
58340              * <li>record - The record being edited</li>
58341              * <li>field - The field name being edited</li>
58342              * <li>value - The value for the field being edited.</li>
58343              * <li>row - The grid row index</li>
58344              * <li>column - The grid column index</li>
58345              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58346              * </ul>
58347              * @param {Object} e An edit event (see above for description)
58348              */
58349             "beforeedit" : true,
58350             /**
58351              * @event afteredit
58352              * Fires after a cell is edited. <br />
58353              * <ul style="padding:5px;padding-left:16px;">
58354              * <li>grid - This grid</li>
58355              * <li>record - The record being edited</li>
58356              * <li>field - The field name being edited</li>
58357              * <li>value - The value being set</li>
58358              * <li>originalValue - The original value for the field, before the edit.</li>
58359              * <li>row - The grid row index</li>
58360              * <li>column - The grid column index</li>
58361              * </ul>
58362              * @param {Object} e An edit event (see above for description)
58363              */
58364             "afteredit" : true,
58365             /**
58366              * @event validateedit
58367              * Fires after a cell is edited, but before the value is set in the record. 
58368          * You can use this to modify the value being set in the field, Return false
58369              * to cancel the change. The edit event object has the following properties <br />
58370              * <ul style="padding:5px;padding-left:16px;">
58371          * <li>editor - This editor</li>
58372              * <li>grid - This grid</li>
58373              * <li>record - The record being edited</li>
58374              * <li>field - The field name being edited</li>
58375              * <li>value - The value being set</li>
58376              * <li>originalValue - The original value for the field, before the edit.</li>
58377              * <li>row - The grid row index</li>
58378              * <li>column - The grid column index</li>
58379              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58380              * </ul>
58381              * @param {Object} e An edit event (see above for description)
58382              */
58383             "validateedit" : true
58384         });
58385     this.on("bodyscroll", this.stopEditing,  this);
58386     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58387 };
58388
58389 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58390     /**
58391      * @cfg {Number} clicksToEdit
58392      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58393      */
58394     clicksToEdit: 2,
58395
58396     // private
58397     isEditor : true,
58398     // private
58399     trackMouseOver: false, // causes very odd FF errors
58400
58401     onCellDblClick : function(g, row, col){
58402         this.startEditing(row, col);
58403     },
58404
58405     onEditComplete : function(ed, value, startValue){
58406         this.editing = false;
58407         this.activeEditor = null;
58408         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58409         var r = ed.record;
58410         var field = this.colModel.getDataIndex(ed.col);
58411         var e = {
58412             grid: this,
58413             record: r,
58414             field: field,
58415             originalValue: startValue,
58416             value: value,
58417             row: ed.row,
58418             column: ed.col,
58419             cancel:false,
58420             editor: ed
58421         };
58422         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58423         cell.show();
58424           
58425         if(String(value) !== String(startValue)){
58426             
58427             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58428                 r.set(field, e.value);
58429                 // if we are dealing with a combo box..
58430                 // then we also set the 'name' colum to be the displayField
58431                 if (ed.field.displayField && ed.field.name) {
58432                     r.set(ed.field.name, ed.field.el.dom.value);
58433                 }
58434                 
58435                 delete e.cancel; //?? why!!!
58436                 this.fireEvent("afteredit", e);
58437             }
58438         } else {
58439             this.fireEvent("afteredit", e); // always fire it!
58440         }
58441         this.view.focusCell(ed.row, ed.col);
58442     },
58443
58444     /**
58445      * Starts editing the specified for the specified row/column
58446      * @param {Number} rowIndex
58447      * @param {Number} colIndex
58448      */
58449     startEditing : function(row, col){
58450         this.stopEditing();
58451         if(this.colModel.isCellEditable(col, row)){
58452             this.view.ensureVisible(row, col, true);
58453           
58454             var r = this.dataSource.getAt(row);
58455             var field = this.colModel.getDataIndex(col);
58456             var cell = Roo.get(this.view.getCell(row,col));
58457             var e = {
58458                 grid: this,
58459                 record: r,
58460                 field: field,
58461                 value: r.data[field],
58462                 row: row,
58463                 column: col,
58464                 cancel:false 
58465             };
58466             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58467                 this.editing = true;
58468                 var ed = this.colModel.getCellEditor(col, row);
58469                 
58470                 if (!ed) {
58471                     return;
58472                 }
58473                 if(!ed.rendered){
58474                     ed.render(ed.parentEl || document.body);
58475                 }
58476                 ed.field.reset();
58477                
58478                 cell.hide();
58479                 
58480                 (function(){ // complex but required for focus issues in safari, ie and opera
58481                     ed.row = row;
58482                     ed.col = col;
58483                     ed.record = r;
58484                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58485                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58486                     this.activeEditor = ed;
58487                     var v = r.data[field];
58488                     ed.startEdit(this.view.getCell(row, col), v);
58489                     // combo's with 'displayField and name set
58490                     if (ed.field.displayField && ed.field.name) {
58491                         ed.field.el.dom.value = r.data[ed.field.name];
58492                     }
58493                     
58494                     
58495                 }).defer(50, this);
58496             }
58497         }
58498     },
58499         
58500     /**
58501      * Stops any active editing
58502      */
58503     stopEditing : function(){
58504         if(this.activeEditor){
58505             this.activeEditor.completeEdit();
58506         }
58507         this.activeEditor = null;
58508     },
58509         
58510          /**
58511      * Called to get grid's drag proxy text, by default returns this.ddText.
58512      * @return {String}
58513      */
58514     getDragDropText : function(){
58515         var count = this.selModel.getSelectedCell() ? 1 : 0;
58516         return String.format(this.ddText, count, count == 1 ? '' : 's');
58517     }
58518         
58519 });/*
58520  * Based on:
58521  * Ext JS Library 1.1.1
58522  * Copyright(c) 2006-2007, Ext JS, LLC.
58523  *
58524  * Originally Released Under LGPL - original licence link has changed is not relivant.
58525  *
58526  * Fork - LGPL
58527  * <script type="text/javascript">
58528  */
58529
58530 // private - not really -- you end up using it !
58531 // This is a support class used internally by the Grid components
58532
58533 /**
58534  * @class Roo.grid.GridEditor
58535  * @extends Roo.Editor
58536  * Class for creating and editable grid elements.
58537  * @param {Object} config any settings (must include field)
58538  */
58539 Roo.grid.GridEditor = function(field, config){
58540     if (!config && field.field) {
58541         config = field;
58542         field = Roo.factory(config.field, Roo.form);
58543     }
58544     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58545     field.monitorTab = false;
58546 };
58547
58548 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58549     
58550     /**
58551      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58552      */
58553     
58554     alignment: "tl-tl",
58555     autoSize: "width",
58556     hideEl : false,
58557     cls: "x-small-editor x-grid-editor",
58558     shim:false,
58559     shadow:"frame"
58560 });/*
58561  * Based on:
58562  * Ext JS Library 1.1.1
58563  * Copyright(c) 2006-2007, Ext JS, LLC.
58564  *
58565  * Originally Released Under LGPL - original licence link has changed is not relivant.
58566  *
58567  * Fork - LGPL
58568  * <script type="text/javascript">
58569  */
58570   
58571
58572   
58573 Roo.grid.PropertyRecord = Roo.data.Record.create([
58574     {name:'name',type:'string'},  'value'
58575 ]);
58576
58577
58578 Roo.grid.PropertyStore = function(grid, source){
58579     this.grid = grid;
58580     this.store = new Roo.data.Store({
58581         recordType : Roo.grid.PropertyRecord
58582     });
58583     this.store.on('update', this.onUpdate,  this);
58584     if(source){
58585         this.setSource(source);
58586     }
58587     Roo.grid.PropertyStore.superclass.constructor.call(this);
58588 };
58589
58590
58591
58592 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58593     setSource : function(o){
58594         this.source = o;
58595         this.store.removeAll();
58596         var data = [];
58597         for(var k in o){
58598             if(this.isEditableValue(o[k])){
58599                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58600             }
58601         }
58602         this.store.loadRecords({records: data}, {}, true);
58603     },
58604
58605     onUpdate : function(ds, record, type){
58606         if(type == Roo.data.Record.EDIT){
58607             var v = record.data['value'];
58608             var oldValue = record.modified['value'];
58609             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58610                 this.source[record.id] = v;
58611                 record.commit();
58612                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58613             }else{
58614                 record.reject();
58615             }
58616         }
58617     },
58618
58619     getProperty : function(row){
58620        return this.store.getAt(row);
58621     },
58622
58623     isEditableValue: function(val){
58624         if(val && val instanceof Date){
58625             return true;
58626         }else if(typeof val == 'object' || typeof val == 'function'){
58627             return false;
58628         }
58629         return true;
58630     },
58631
58632     setValue : function(prop, value){
58633         this.source[prop] = value;
58634         this.store.getById(prop).set('value', value);
58635     },
58636
58637     getSource : function(){
58638         return this.source;
58639     }
58640 });
58641
58642 Roo.grid.PropertyColumnModel = function(grid, store){
58643     this.grid = grid;
58644     var g = Roo.grid;
58645     g.PropertyColumnModel.superclass.constructor.call(this, [
58646         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58647         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58648     ]);
58649     this.store = store;
58650     this.bselect = Roo.DomHelper.append(document.body, {
58651         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58652             {tag: 'option', value: 'true', html: 'true'},
58653             {tag: 'option', value: 'false', html: 'false'}
58654         ]
58655     });
58656     Roo.id(this.bselect);
58657     var f = Roo.form;
58658     this.editors = {
58659         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58660         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58661         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58662         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58663         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58664     };
58665     this.renderCellDelegate = this.renderCell.createDelegate(this);
58666     this.renderPropDelegate = this.renderProp.createDelegate(this);
58667 };
58668
58669 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58670     
58671     
58672     nameText : 'Name',
58673     valueText : 'Value',
58674     
58675     dateFormat : 'm/j/Y',
58676     
58677     
58678     renderDate : function(dateVal){
58679         return dateVal.dateFormat(this.dateFormat);
58680     },
58681
58682     renderBool : function(bVal){
58683         return bVal ? 'true' : 'false';
58684     },
58685
58686     isCellEditable : function(colIndex, rowIndex){
58687         return colIndex == 1;
58688     },
58689
58690     getRenderer : function(col){
58691         return col == 1 ?
58692             this.renderCellDelegate : this.renderPropDelegate;
58693     },
58694
58695     renderProp : function(v){
58696         return this.getPropertyName(v);
58697     },
58698
58699     renderCell : function(val){
58700         var rv = val;
58701         if(val instanceof Date){
58702             rv = this.renderDate(val);
58703         }else if(typeof val == 'boolean'){
58704             rv = this.renderBool(val);
58705         }
58706         return Roo.util.Format.htmlEncode(rv);
58707     },
58708
58709     getPropertyName : function(name){
58710         var pn = this.grid.propertyNames;
58711         return pn && pn[name] ? pn[name] : name;
58712     },
58713
58714     getCellEditor : function(colIndex, rowIndex){
58715         var p = this.store.getProperty(rowIndex);
58716         var n = p.data['name'], val = p.data['value'];
58717         
58718         if(typeof(this.grid.customEditors[n]) == 'string'){
58719             return this.editors[this.grid.customEditors[n]];
58720         }
58721         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58722             return this.grid.customEditors[n];
58723         }
58724         if(val instanceof Date){
58725             return this.editors['date'];
58726         }else if(typeof val == 'number'){
58727             return this.editors['number'];
58728         }else if(typeof val == 'boolean'){
58729             return this.editors['boolean'];
58730         }else{
58731             return this.editors['string'];
58732         }
58733     }
58734 });
58735
58736 /**
58737  * @class Roo.grid.PropertyGrid
58738  * @extends Roo.grid.EditorGrid
58739  * This class represents the  interface of a component based property grid control.
58740  * <br><br>Usage:<pre><code>
58741  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58742       
58743  });
58744  // set any options
58745  grid.render();
58746  * </code></pre>
58747   
58748  * @constructor
58749  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58750  * The container MUST have some type of size defined for the grid to fill. The container will be
58751  * automatically set to position relative if it isn't already.
58752  * @param {Object} config A config object that sets properties on this grid.
58753  */
58754 Roo.grid.PropertyGrid = function(container, config){
58755     config = config || {};
58756     var store = new Roo.grid.PropertyStore(this);
58757     this.store = store;
58758     var cm = new Roo.grid.PropertyColumnModel(this, store);
58759     store.store.sort('name', 'ASC');
58760     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58761         ds: store.store,
58762         cm: cm,
58763         enableColLock:false,
58764         enableColumnMove:false,
58765         stripeRows:false,
58766         trackMouseOver: false,
58767         clicksToEdit:1
58768     }, config));
58769     this.getGridEl().addClass('x-props-grid');
58770     this.lastEditRow = null;
58771     this.on('columnresize', this.onColumnResize, this);
58772     this.addEvents({
58773          /**
58774              * @event beforepropertychange
58775              * Fires before a property changes (return false to stop?)
58776              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58777              * @param {String} id Record Id
58778              * @param {String} newval New Value
58779          * @param {String} oldval Old Value
58780              */
58781         "beforepropertychange": true,
58782         /**
58783              * @event propertychange
58784              * Fires after a property changes
58785              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58786              * @param {String} id Record Id
58787              * @param {String} newval New Value
58788          * @param {String} oldval Old Value
58789              */
58790         "propertychange": true
58791     });
58792     this.customEditors = this.customEditors || {};
58793 };
58794 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58795     
58796      /**
58797      * @cfg {Object} customEditors map of colnames=> custom editors.
58798      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58799      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58800      * false disables editing of the field.
58801          */
58802     
58803       /**
58804      * @cfg {Object} propertyNames map of property Names to their displayed value
58805          */
58806     
58807     render : function(){
58808         Roo.grid.PropertyGrid.superclass.render.call(this);
58809         this.autoSize.defer(100, this);
58810     },
58811
58812     autoSize : function(){
58813         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58814         if(this.view){
58815             this.view.fitColumns();
58816         }
58817     },
58818
58819     onColumnResize : function(){
58820         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58821         this.autoSize();
58822     },
58823     /**
58824      * Sets the data for the Grid
58825      * accepts a Key => Value object of all the elements avaiable.
58826      * @param {Object} data  to appear in grid.
58827      */
58828     setSource : function(source){
58829         this.store.setSource(source);
58830         //this.autoSize();
58831     },
58832     /**
58833      * Gets all the data from the grid.
58834      * @return {Object} data  data stored in grid
58835      */
58836     getSource : function(){
58837         return this.store.getSource();
58838     }
58839 });/*
58840   
58841  * Licence LGPL
58842  
58843  */
58844  
58845 /**
58846  * @class Roo.grid.Calendar
58847  * @extends Roo.util.Grid
58848  * This class extends the Grid to provide a calendar widget
58849  * <br><br>Usage:<pre><code>
58850  var grid = new Roo.grid.Calendar("my-container-id", {
58851      ds: myDataStore,
58852      cm: myColModel,
58853      selModel: mySelectionModel,
58854      autoSizeColumns: true,
58855      monitorWindowResize: false,
58856      trackMouseOver: true
58857      eventstore : real data store..
58858  });
58859  // set any options
58860  grid.render();
58861   
58862   * @constructor
58863  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58864  * The container MUST have some type of size defined for the grid to fill. The container will be
58865  * automatically set to position relative if it isn't already.
58866  * @param {Object} config A config object that sets properties on this grid.
58867  */
58868 Roo.grid.Calendar = function(container, config){
58869         // initialize the container
58870         this.container = Roo.get(container);
58871         this.container.update("");
58872         this.container.setStyle("overflow", "hidden");
58873     this.container.addClass('x-grid-container');
58874
58875     this.id = this.container.id;
58876
58877     Roo.apply(this, config);
58878     // check and correct shorthanded configs
58879     
58880     var rows = [];
58881     var d =1;
58882     for (var r = 0;r < 6;r++) {
58883         
58884         rows[r]=[];
58885         for (var c =0;c < 7;c++) {
58886             rows[r][c]= '';
58887         }
58888     }
58889     if (this.eventStore) {
58890         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58891         this.eventStore.on('load',this.onLoad, this);
58892         this.eventStore.on('beforeload',this.clearEvents, this);
58893          
58894     }
58895     
58896     this.dataSource = new Roo.data.Store({
58897             proxy: new Roo.data.MemoryProxy(rows),
58898             reader: new Roo.data.ArrayReader({}, [
58899                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58900     });
58901
58902     this.dataSource.load();
58903     this.ds = this.dataSource;
58904     this.ds.xmodule = this.xmodule || false;
58905     
58906     
58907     var cellRender = function(v,x,r)
58908     {
58909         return String.format(
58910             '<div class="fc-day  fc-widget-content"><div>' +
58911                 '<div class="fc-event-container"></div>' +
58912                 '<div class="fc-day-number">{0}</div>'+
58913                 
58914                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58915             '</div></div>', v);
58916     
58917     }
58918     
58919     
58920     this.colModel = new Roo.grid.ColumnModel( [
58921         {
58922             xtype: 'ColumnModel',
58923             xns: Roo.grid,
58924             dataIndex : 'weekday0',
58925             header : 'Sunday',
58926             renderer : cellRender
58927         },
58928         {
58929             xtype: 'ColumnModel',
58930             xns: Roo.grid,
58931             dataIndex : 'weekday1',
58932             header : 'Monday',
58933             renderer : cellRender
58934         },
58935         {
58936             xtype: 'ColumnModel',
58937             xns: Roo.grid,
58938             dataIndex : 'weekday2',
58939             header : 'Tuesday',
58940             renderer : cellRender
58941         },
58942         {
58943             xtype: 'ColumnModel',
58944             xns: Roo.grid,
58945             dataIndex : 'weekday3',
58946             header : 'Wednesday',
58947             renderer : cellRender
58948         },
58949         {
58950             xtype: 'ColumnModel',
58951             xns: Roo.grid,
58952             dataIndex : 'weekday4',
58953             header : 'Thursday',
58954             renderer : cellRender
58955         },
58956         {
58957             xtype: 'ColumnModel',
58958             xns: Roo.grid,
58959             dataIndex : 'weekday5',
58960             header : 'Friday',
58961             renderer : cellRender
58962         },
58963         {
58964             xtype: 'ColumnModel',
58965             xns: Roo.grid,
58966             dataIndex : 'weekday6',
58967             header : 'Saturday',
58968             renderer : cellRender
58969         }
58970     ]);
58971     this.cm = this.colModel;
58972     this.cm.xmodule = this.xmodule || false;
58973  
58974         
58975           
58976     //this.selModel = new Roo.grid.CellSelectionModel();
58977     //this.sm = this.selModel;
58978     //this.selModel.init(this);
58979     
58980     
58981     if(this.width){
58982         this.container.setWidth(this.width);
58983     }
58984
58985     if(this.height){
58986         this.container.setHeight(this.height);
58987     }
58988     /** @private */
58989         this.addEvents({
58990         // raw events
58991         /**
58992          * @event click
58993          * The raw click event for the entire grid.
58994          * @param {Roo.EventObject} e
58995          */
58996         "click" : true,
58997         /**
58998          * @event dblclick
58999          * The raw dblclick event for the entire grid.
59000          * @param {Roo.EventObject} e
59001          */
59002         "dblclick" : true,
59003         /**
59004          * @event contextmenu
59005          * The raw contextmenu event for the entire grid.
59006          * @param {Roo.EventObject} e
59007          */
59008         "contextmenu" : true,
59009         /**
59010          * @event mousedown
59011          * The raw mousedown event for the entire grid.
59012          * @param {Roo.EventObject} e
59013          */
59014         "mousedown" : true,
59015         /**
59016          * @event mouseup
59017          * The raw mouseup event for the entire grid.
59018          * @param {Roo.EventObject} e
59019          */
59020         "mouseup" : true,
59021         /**
59022          * @event mouseover
59023          * The raw mouseover event for the entire grid.
59024          * @param {Roo.EventObject} e
59025          */
59026         "mouseover" : true,
59027         /**
59028          * @event mouseout
59029          * The raw mouseout event for the entire grid.
59030          * @param {Roo.EventObject} e
59031          */
59032         "mouseout" : true,
59033         /**
59034          * @event keypress
59035          * The raw keypress event for the entire grid.
59036          * @param {Roo.EventObject} e
59037          */
59038         "keypress" : true,
59039         /**
59040          * @event keydown
59041          * The raw keydown event for the entire grid.
59042          * @param {Roo.EventObject} e
59043          */
59044         "keydown" : true,
59045
59046         // custom events
59047
59048         /**
59049          * @event cellclick
59050          * Fires when a cell is clicked
59051          * @param {Grid} this
59052          * @param {Number} rowIndex
59053          * @param {Number} columnIndex
59054          * @param {Roo.EventObject} e
59055          */
59056         "cellclick" : true,
59057         /**
59058          * @event celldblclick
59059          * Fires when a cell is double clicked
59060          * @param {Grid} this
59061          * @param {Number} rowIndex
59062          * @param {Number} columnIndex
59063          * @param {Roo.EventObject} e
59064          */
59065         "celldblclick" : true,
59066         /**
59067          * @event rowclick
59068          * Fires when a row is clicked
59069          * @param {Grid} this
59070          * @param {Number} rowIndex
59071          * @param {Roo.EventObject} e
59072          */
59073         "rowclick" : true,
59074         /**
59075          * @event rowdblclick
59076          * Fires when a row is double clicked
59077          * @param {Grid} this
59078          * @param {Number} rowIndex
59079          * @param {Roo.EventObject} e
59080          */
59081         "rowdblclick" : true,
59082         /**
59083          * @event headerclick
59084          * Fires when a header is clicked
59085          * @param {Grid} this
59086          * @param {Number} columnIndex
59087          * @param {Roo.EventObject} e
59088          */
59089         "headerclick" : true,
59090         /**
59091          * @event headerdblclick
59092          * Fires when a header cell is double clicked
59093          * @param {Grid} this
59094          * @param {Number} columnIndex
59095          * @param {Roo.EventObject} e
59096          */
59097         "headerdblclick" : true,
59098         /**
59099          * @event rowcontextmenu
59100          * Fires when a row is right clicked
59101          * @param {Grid} this
59102          * @param {Number} rowIndex
59103          * @param {Roo.EventObject} e
59104          */
59105         "rowcontextmenu" : true,
59106         /**
59107          * @event cellcontextmenu
59108          * Fires when a cell is right clicked
59109          * @param {Grid} this
59110          * @param {Number} rowIndex
59111          * @param {Number} cellIndex
59112          * @param {Roo.EventObject} e
59113          */
59114          "cellcontextmenu" : true,
59115         /**
59116          * @event headercontextmenu
59117          * Fires when a header is right clicked
59118          * @param {Grid} this
59119          * @param {Number} columnIndex
59120          * @param {Roo.EventObject} e
59121          */
59122         "headercontextmenu" : true,
59123         /**
59124          * @event bodyscroll
59125          * Fires when the body element is scrolled
59126          * @param {Number} scrollLeft
59127          * @param {Number} scrollTop
59128          */
59129         "bodyscroll" : true,
59130         /**
59131          * @event columnresize
59132          * Fires when the user resizes a column
59133          * @param {Number} columnIndex
59134          * @param {Number} newSize
59135          */
59136         "columnresize" : true,
59137         /**
59138          * @event columnmove
59139          * Fires when the user moves a column
59140          * @param {Number} oldIndex
59141          * @param {Number} newIndex
59142          */
59143         "columnmove" : true,
59144         /**
59145          * @event startdrag
59146          * Fires when row(s) start being dragged
59147          * @param {Grid} this
59148          * @param {Roo.GridDD} dd The drag drop object
59149          * @param {event} e The raw browser event
59150          */
59151         "startdrag" : true,
59152         /**
59153          * @event enddrag
59154          * Fires when a drag operation is complete
59155          * @param {Grid} this
59156          * @param {Roo.GridDD} dd The drag drop object
59157          * @param {event} e The raw browser event
59158          */
59159         "enddrag" : true,
59160         /**
59161          * @event dragdrop
59162          * Fires when dragged row(s) are dropped on a valid DD target
59163          * @param {Grid} this
59164          * @param {Roo.GridDD} dd The drag drop object
59165          * @param {String} targetId The target drag drop object
59166          * @param {event} e The raw browser event
59167          */
59168         "dragdrop" : true,
59169         /**
59170          * @event dragover
59171          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59172          * @param {Grid} this
59173          * @param {Roo.GridDD} dd The drag drop object
59174          * @param {String} targetId The target drag drop object
59175          * @param {event} e The raw browser event
59176          */
59177         "dragover" : true,
59178         /**
59179          * @event dragenter
59180          *  Fires when the dragged row(s) first cross another DD target while being dragged
59181          * @param {Grid} this
59182          * @param {Roo.GridDD} dd The drag drop object
59183          * @param {String} targetId The target drag drop object
59184          * @param {event} e The raw browser event
59185          */
59186         "dragenter" : true,
59187         /**
59188          * @event dragout
59189          * Fires when the dragged row(s) leave another DD target while being dragged
59190          * @param {Grid} this
59191          * @param {Roo.GridDD} dd The drag drop object
59192          * @param {String} targetId The target drag drop object
59193          * @param {event} e The raw browser event
59194          */
59195         "dragout" : true,
59196         /**
59197          * @event rowclass
59198          * Fires when a row is rendered, so you can change add a style to it.
59199          * @param {GridView} gridview   The grid view
59200          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59201          */
59202         'rowclass' : true,
59203
59204         /**
59205          * @event render
59206          * Fires when the grid is rendered
59207          * @param {Grid} grid
59208          */
59209         'render' : true,
59210             /**
59211              * @event select
59212              * Fires when a date is selected
59213              * @param {DatePicker} this
59214              * @param {Date} date The selected date
59215              */
59216         'select': true,
59217         /**
59218              * @event monthchange
59219              * Fires when the displayed month changes 
59220              * @param {DatePicker} this
59221              * @param {Date} date The selected month
59222              */
59223         'monthchange': true,
59224         /**
59225              * @event evententer
59226              * Fires when mouse over an event
59227              * @param {Calendar} this
59228              * @param {event} Event
59229              */
59230         'evententer': true,
59231         /**
59232              * @event eventleave
59233              * Fires when the mouse leaves an
59234              * @param {Calendar} this
59235              * @param {event}
59236              */
59237         'eventleave': true,
59238         /**
59239              * @event eventclick
59240              * Fires when the mouse click an
59241              * @param {Calendar} this
59242              * @param {event}
59243              */
59244         'eventclick': true,
59245         /**
59246              * @event eventrender
59247              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59248              * @param {Calendar} this
59249              * @param {data} data to be modified
59250              */
59251         'eventrender': true
59252         
59253     });
59254
59255     Roo.grid.Grid.superclass.constructor.call(this);
59256     this.on('render', function() {
59257         this.view.el.addClass('x-grid-cal'); 
59258         
59259         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59260
59261     },this);
59262     
59263     if (!Roo.grid.Calendar.style) {
59264         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59265             
59266             
59267             '.x-grid-cal .x-grid-col' :  {
59268                 height: 'auto !important',
59269                 'vertical-align': 'top'
59270             },
59271             '.x-grid-cal  .fc-event-hori' : {
59272                 height: '14px'
59273             }
59274              
59275             
59276         }, Roo.id());
59277     }
59278
59279     
59280     
59281 };
59282 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59283     /**
59284      * @cfg {Store} eventStore The store that loads events.
59285      */
59286     eventStore : 25,
59287
59288      
59289     activeDate : false,
59290     startDay : 0,
59291     autoWidth : true,
59292     monitorWindowResize : false,
59293
59294     
59295     resizeColumns : function() {
59296         var col = (this.view.el.getWidth() / 7) - 3;
59297         // loop through cols, and setWidth
59298         for(var i =0 ; i < 7 ; i++){
59299             this.cm.setColumnWidth(i, col);
59300         }
59301     },
59302      setDate :function(date) {
59303         
59304         Roo.log('setDate?');
59305         
59306         this.resizeColumns();
59307         var vd = this.activeDate;
59308         this.activeDate = date;
59309 //        if(vd && this.el){
59310 //            var t = date.getTime();
59311 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59312 //                Roo.log('using add remove');
59313 //                
59314 //                this.fireEvent('monthchange', this, date);
59315 //                
59316 //                this.cells.removeClass("fc-state-highlight");
59317 //                this.cells.each(function(c){
59318 //                   if(c.dateValue == t){
59319 //                       c.addClass("fc-state-highlight");
59320 //                       setTimeout(function(){
59321 //                            try{c.dom.firstChild.focus();}catch(e){}
59322 //                       }, 50);
59323 //                       return false;
59324 //                   }
59325 //                   return true;
59326 //                });
59327 //                return;
59328 //            }
59329 //        }
59330         
59331         var days = date.getDaysInMonth();
59332         
59333         var firstOfMonth = date.getFirstDateOfMonth();
59334         var startingPos = firstOfMonth.getDay()-this.startDay;
59335         
59336         if(startingPos < this.startDay){
59337             startingPos += 7;
59338         }
59339         
59340         var pm = date.add(Date.MONTH, -1);
59341         var prevStart = pm.getDaysInMonth()-startingPos;
59342 //        
59343         
59344         
59345         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59346         
59347         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59348         //this.cells.addClassOnOver('fc-state-hover');
59349         
59350         var cells = this.cells.elements;
59351         var textEls = this.textNodes;
59352         
59353         //Roo.each(cells, function(cell){
59354         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59355         //});
59356         
59357         days += startingPos;
59358
59359         // convert everything to numbers so it's fast
59360         var day = 86400000;
59361         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59362         //Roo.log(d);
59363         //Roo.log(pm);
59364         //Roo.log(prevStart);
59365         
59366         var today = new Date().clearTime().getTime();
59367         var sel = date.clearTime().getTime();
59368         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59369         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59370         var ddMatch = this.disabledDatesRE;
59371         var ddText = this.disabledDatesText;
59372         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59373         var ddaysText = this.disabledDaysText;
59374         var format = this.format;
59375         
59376         var setCellClass = function(cal, cell){
59377             
59378             //Roo.log('set Cell Class');
59379             cell.title = "";
59380             var t = d.getTime();
59381             
59382             //Roo.log(d);
59383             
59384             
59385             cell.dateValue = t;
59386             if(t == today){
59387                 cell.className += " fc-today";
59388                 cell.className += " fc-state-highlight";
59389                 cell.title = cal.todayText;
59390             }
59391             if(t == sel){
59392                 // disable highlight in other month..
59393                 cell.className += " fc-state-highlight";
59394                 
59395             }
59396             // disabling
59397             if(t < min) {
59398                 //cell.className = " fc-state-disabled";
59399                 cell.title = cal.minText;
59400                 return;
59401             }
59402             if(t > max) {
59403                 //cell.className = " fc-state-disabled";
59404                 cell.title = cal.maxText;
59405                 return;
59406             }
59407             if(ddays){
59408                 if(ddays.indexOf(d.getDay()) != -1){
59409                     // cell.title = ddaysText;
59410                    // cell.className = " fc-state-disabled";
59411                 }
59412             }
59413             if(ddMatch && format){
59414                 var fvalue = d.dateFormat(format);
59415                 if(ddMatch.test(fvalue)){
59416                     cell.title = ddText.replace("%0", fvalue);
59417                    cell.className = " fc-state-disabled";
59418                 }
59419             }
59420             
59421             if (!cell.initialClassName) {
59422                 cell.initialClassName = cell.dom.className;
59423             }
59424             
59425             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59426         };
59427
59428         var i = 0;
59429         
59430         for(; i < startingPos; i++) {
59431             cells[i].dayName =  (++prevStart);
59432             Roo.log(textEls[i]);
59433             d.setDate(d.getDate()+1);
59434             
59435             //cells[i].className = "fc-past fc-other-month";
59436             setCellClass(this, cells[i]);
59437         }
59438         
59439         var intDay = 0;
59440         
59441         for(; i < days; i++){
59442             intDay = i - startingPos + 1;
59443             cells[i].dayName =  (intDay);
59444             d.setDate(d.getDate()+1);
59445             
59446             cells[i].className = ''; // "x-date-active";
59447             setCellClass(this, cells[i]);
59448         }
59449         var extraDays = 0;
59450         
59451         for(; i < 42; i++) {
59452             //textEls[i].innerHTML = (++extraDays);
59453             
59454             d.setDate(d.getDate()+1);
59455             cells[i].dayName = (++extraDays);
59456             cells[i].className = "fc-future fc-other-month";
59457             setCellClass(this, cells[i]);
59458         }
59459         
59460         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59461         
59462         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59463         
59464         // this will cause all the cells to mis
59465         var rows= [];
59466         var i =0;
59467         for (var r = 0;r < 6;r++) {
59468             for (var c =0;c < 7;c++) {
59469                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59470             }    
59471         }
59472         
59473         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59474         for(i=0;i<cells.length;i++) {
59475             
59476             this.cells.elements[i].dayName = cells[i].dayName ;
59477             this.cells.elements[i].className = cells[i].className;
59478             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59479             this.cells.elements[i].title = cells[i].title ;
59480             this.cells.elements[i].dateValue = cells[i].dateValue ;
59481         }
59482         
59483         
59484         
59485         
59486         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59487         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59488         
59489         ////if(totalRows != 6){
59490             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59491            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59492        // }
59493         
59494         this.fireEvent('monthchange', this, date);
59495         
59496         
59497     },
59498  /**
59499      * Returns the grid's SelectionModel.
59500      * @return {SelectionModel}
59501      */
59502     getSelectionModel : function(){
59503         if(!this.selModel){
59504             this.selModel = new Roo.grid.CellSelectionModel();
59505         }
59506         return this.selModel;
59507     },
59508
59509     load: function() {
59510         this.eventStore.load()
59511         
59512         
59513         
59514     },
59515     
59516     findCell : function(dt) {
59517         dt = dt.clearTime().getTime();
59518         var ret = false;
59519         this.cells.each(function(c){
59520             //Roo.log("check " +c.dateValue + '?=' + dt);
59521             if(c.dateValue == dt){
59522                 ret = c;
59523                 return false;
59524             }
59525             return true;
59526         });
59527         
59528         return ret;
59529     },
59530     
59531     findCells : function(rec) {
59532         var s = rec.data.start_dt.clone().clearTime().getTime();
59533        // Roo.log(s);
59534         var e= rec.data.end_dt.clone().clearTime().getTime();
59535        // Roo.log(e);
59536         var ret = [];
59537         this.cells.each(function(c){
59538              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59539             
59540             if(c.dateValue > e){
59541                 return ;
59542             }
59543             if(c.dateValue < s){
59544                 return ;
59545             }
59546             ret.push(c);
59547         });
59548         
59549         return ret;    
59550     },
59551     
59552     findBestRow: function(cells)
59553     {
59554         var ret = 0;
59555         
59556         for (var i =0 ; i < cells.length;i++) {
59557             ret  = Math.max(cells[i].rows || 0,ret);
59558         }
59559         return ret;
59560         
59561     },
59562     
59563     
59564     addItem : function(rec)
59565     {
59566         // look for vertical location slot in
59567         var cells = this.findCells(rec);
59568         
59569         rec.row = this.findBestRow(cells);
59570         
59571         // work out the location.
59572         
59573         var crow = false;
59574         var rows = [];
59575         for(var i =0; i < cells.length; i++) {
59576             if (!crow) {
59577                 crow = {
59578                     start : cells[i],
59579                     end :  cells[i]
59580                 };
59581                 continue;
59582             }
59583             if (crow.start.getY() == cells[i].getY()) {
59584                 // on same row.
59585                 crow.end = cells[i];
59586                 continue;
59587             }
59588             // different row.
59589             rows.push(crow);
59590             crow = {
59591                 start: cells[i],
59592                 end : cells[i]
59593             };
59594             
59595         }
59596         
59597         rows.push(crow);
59598         rec.els = [];
59599         rec.rows = rows;
59600         rec.cells = cells;
59601         for (var i = 0; i < cells.length;i++) {
59602             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59603             
59604         }
59605         
59606         
59607     },
59608     
59609     clearEvents: function() {
59610         
59611         if (!this.eventStore.getCount()) {
59612             return;
59613         }
59614         // reset number of rows in cells.
59615         Roo.each(this.cells.elements, function(c){
59616             c.rows = 0;
59617         });
59618         
59619         this.eventStore.each(function(e) {
59620             this.clearEvent(e);
59621         },this);
59622         
59623     },
59624     
59625     clearEvent : function(ev)
59626     {
59627         if (ev.els) {
59628             Roo.each(ev.els, function(el) {
59629                 el.un('mouseenter' ,this.onEventEnter, this);
59630                 el.un('mouseleave' ,this.onEventLeave, this);
59631                 el.remove();
59632             },this);
59633             ev.els = [];
59634         }
59635     },
59636     
59637     
59638     renderEvent : function(ev,ctr) {
59639         if (!ctr) {
59640              ctr = this.view.el.select('.fc-event-container',true).first();
59641         }
59642         
59643          
59644         this.clearEvent(ev);
59645             //code
59646        
59647         
59648         
59649         ev.els = [];
59650         var cells = ev.cells;
59651         var rows = ev.rows;
59652         this.fireEvent('eventrender', this, ev);
59653         
59654         for(var i =0; i < rows.length; i++) {
59655             
59656             cls = '';
59657             if (i == 0) {
59658                 cls += ' fc-event-start';
59659             }
59660             if ((i+1) == rows.length) {
59661                 cls += ' fc-event-end';
59662             }
59663             
59664             //Roo.log(ev.data);
59665             // how many rows should it span..
59666             var cg = this.eventTmpl.append(ctr,Roo.apply({
59667                 fccls : cls
59668                 
59669             }, ev.data) , true);
59670             
59671             
59672             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59673             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59674             cg.on('click', this.onEventClick, this, ev);
59675             
59676             ev.els.push(cg);
59677             
59678             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59679             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59680             //Roo.log(cg);
59681              
59682             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59683             cg.setWidth(ebox.right - sbox.x -2);
59684         }
59685     },
59686     
59687     renderEvents: function()
59688     {   
59689         // first make sure there is enough space..
59690         
59691         if (!this.eventTmpl) {
59692             this.eventTmpl = new Roo.Template(
59693                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59694                     '<div class="fc-event-inner">' +
59695                         '<span class="fc-event-time">{time}</span>' +
59696                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59697                     '</div>' +
59698                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59699                 '</div>'
59700             );
59701                 
59702         }
59703                
59704         
59705         
59706         this.cells.each(function(c) {
59707             //Roo.log(c.select('.fc-day-content div',true).first());
59708             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59709         });
59710         
59711         var ctr = this.view.el.select('.fc-event-container',true).first();
59712         
59713         var cls;
59714         this.eventStore.each(function(ev){
59715             
59716             this.renderEvent(ev);
59717              
59718              
59719         }, this);
59720         this.view.layout();
59721         
59722     },
59723     
59724     onEventEnter: function (e, el,event,d) {
59725         this.fireEvent('evententer', this, el, event);
59726     },
59727     
59728     onEventLeave: function (e, el,event,d) {
59729         this.fireEvent('eventleave', this, el, event);
59730     },
59731     
59732     onEventClick: function (e, el,event,d) {
59733         this.fireEvent('eventclick', this, el, event);
59734     },
59735     
59736     onMonthChange: function () {
59737         this.store.load();
59738     },
59739     
59740     onLoad: function () {
59741         
59742         //Roo.log('calendar onload');
59743 //         
59744         if(this.eventStore.getCount() > 0){
59745             
59746            
59747             
59748             this.eventStore.each(function(d){
59749                 
59750                 
59751                 // FIXME..
59752                 var add =   d.data;
59753                 if (typeof(add.end_dt) == 'undefined')  {
59754                     Roo.log("Missing End time in calendar data: ");
59755                     Roo.log(d);
59756                     return;
59757                 }
59758                 if (typeof(add.start_dt) == 'undefined')  {
59759                     Roo.log("Missing Start time in calendar data: ");
59760                     Roo.log(d);
59761                     return;
59762                 }
59763                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59764                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59765                 add.id = add.id || d.id;
59766                 add.title = add.title || '??';
59767                 
59768                 this.addItem(d);
59769                 
59770              
59771             },this);
59772         }
59773         
59774         this.renderEvents();
59775     }
59776     
59777
59778 });
59779 /*
59780  grid : {
59781                 xtype: 'Grid',
59782                 xns: Roo.grid,
59783                 listeners : {
59784                     render : function ()
59785                     {
59786                         _this.grid = this;
59787                         
59788                         if (!this.view.el.hasClass('course-timesheet')) {
59789                             this.view.el.addClass('course-timesheet');
59790                         }
59791                         if (this.tsStyle) {
59792                             this.ds.load({});
59793                             return; 
59794                         }
59795                         Roo.log('width');
59796                         Roo.log(_this.grid.view.el.getWidth());
59797                         
59798                         
59799                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59800                             '.course-timesheet .x-grid-row' : {
59801                                 height: '80px'
59802                             },
59803                             '.x-grid-row td' : {
59804                                 'vertical-align' : 0
59805                             },
59806                             '.course-edit-link' : {
59807                                 'color' : 'blue',
59808                                 'text-overflow' : 'ellipsis',
59809                                 'overflow' : 'hidden',
59810                                 'white-space' : 'nowrap',
59811                                 'cursor' : 'pointer'
59812                             },
59813                             '.sub-link' : {
59814                                 'color' : 'green'
59815                             },
59816                             '.de-act-sup-link' : {
59817                                 'color' : 'purple',
59818                                 'text-decoration' : 'line-through'
59819                             },
59820                             '.de-act-link' : {
59821                                 'color' : 'red',
59822                                 'text-decoration' : 'line-through'
59823                             },
59824                             '.course-timesheet .course-highlight' : {
59825                                 'border-top-style': 'dashed !important',
59826                                 'border-bottom-bottom': 'dashed !important'
59827                             },
59828                             '.course-timesheet .course-item' : {
59829                                 'font-family'   : 'tahoma, arial, helvetica',
59830                                 'font-size'     : '11px',
59831                                 'overflow'      : 'hidden',
59832                                 'padding-left'  : '10px',
59833                                 'padding-right' : '10px',
59834                                 'padding-top' : '10px' 
59835                             }
59836                             
59837                         }, Roo.id());
59838                                 this.ds.load({});
59839                     }
59840                 },
59841                 autoWidth : true,
59842                 monitorWindowResize : false,
59843                 cellrenderer : function(v,x,r)
59844                 {
59845                     return v;
59846                 },
59847                 sm : {
59848                     xtype: 'CellSelectionModel',
59849                     xns: Roo.grid
59850                 },
59851                 dataSource : {
59852                     xtype: 'Store',
59853                     xns: Roo.data,
59854                     listeners : {
59855                         beforeload : function (_self, options)
59856                         {
59857                             options.params = options.params || {};
59858                             options.params._month = _this.monthField.getValue();
59859                             options.params.limit = 9999;
59860                             options.params['sort'] = 'when_dt';    
59861                             options.params['dir'] = 'ASC';    
59862                             this.proxy.loadResponse = this.loadResponse;
59863                             Roo.log("load?");
59864                             //this.addColumns();
59865                         },
59866                         load : function (_self, records, options)
59867                         {
59868                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59869                                 // if you click on the translation.. you can edit it...
59870                                 var el = Roo.get(this);
59871                                 var id = el.dom.getAttribute('data-id');
59872                                 var d = el.dom.getAttribute('data-date');
59873                                 var t = el.dom.getAttribute('data-time');
59874                                 //var id = this.child('span').dom.textContent;
59875                                 
59876                                 //Roo.log(this);
59877                                 Pman.Dialog.CourseCalendar.show({
59878                                     id : id,
59879                                     when_d : d,
59880                                     when_t : t,
59881                                     productitem_active : id ? 1 : 0
59882                                 }, function() {
59883                                     _this.grid.ds.load({});
59884                                 });
59885                            
59886                            });
59887                            
59888                            _this.panel.fireEvent('resize', [ '', '' ]);
59889                         }
59890                     },
59891                     loadResponse : function(o, success, response){
59892                             // this is overridden on before load..
59893                             
59894                             Roo.log("our code?");       
59895                             //Roo.log(success);
59896                             //Roo.log(response)
59897                             delete this.activeRequest;
59898                             if(!success){
59899                                 this.fireEvent("loadexception", this, o, response);
59900                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59901                                 return;
59902                             }
59903                             var result;
59904                             try {
59905                                 result = o.reader.read(response);
59906                             }catch(e){
59907                                 Roo.log("load exception?");
59908                                 this.fireEvent("loadexception", this, o, response, e);
59909                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59910                                 return;
59911                             }
59912                             Roo.log("ready...");        
59913                             // loop through result.records;
59914                             // and set this.tdate[date] = [] << array of records..
59915                             _this.tdata  = {};
59916                             Roo.each(result.records, function(r){
59917                                 //Roo.log(r.data);
59918                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59919                                     _this.tdata[r.data.when_dt.format('j')] = [];
59920                                 }
59921                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59922                             });
59923                             
59924                             //Roo.log(_this.tdata);
59925                             
59926                             result.records = [];
59927                             result.totalRecords = 6;
59928                     
59929                             // let's generate some duumy records for the rows.
59930                             //var st = _this.dateField.getValue();
59931                             
59932                             // work out monday..
59933                             //st = st.add(Date.DAY, -1 * st.format('w'));
59934                             
59935                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59936                             
59937                             var firstOfMonth = date.getFirstDayOfMonth();
59938                             var days = date.getDaysInMonth();
59939                             var d = 1;
59940                             var firstAdded = false;
59941                             for (var i = 0; i < result.totalRecords ; i++) {
59942                                 //var d= st.add(Date.DAY, i);
59943                                 var row = {};
59944                                 var added = 0;
59945                                 for(var w = 0 ; w < 7 ; w++){
59946                                     if(!firstAdded && firstOfMonth != w){
59947                                         continue;
59948                                     }
59949                                     if(d > days){
59950                                         continue;
59951                                     }
59952                                     firstAdded = true;
59953                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59954                                     row['weekday'+w] = String.format(
59955                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59956                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59957                                                     d,
59958                                                     date.format('Y-m-')+dd
59959                                                 );
59960                                     added++;
59961                                     if(typeof(_this.tdata[d]) != 'undefined'){
59962                                         Roo.each(_this.tdata[d], function(r){
59963                                             var is_sub = '';
59964                                             var deactive = '';
59965                                             var id = r.id;
59966                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59967                                             if(r.parent_id*1>0){
59968                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59969                                                 id = r.parent_id;
59970                                             }
59971                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59972                                                 deactive = 'de-act-link';
59973                                             }
59974                                             
59975                                             row['weekday'+w] += String.format(
59976                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59977                                                     id, //0
59978                                                     r.product_id_name, //1
59979                                                     r.when_dt.format('h:ia'), //2
59980                                                     is_sub, //3
59981                                                     deactive, //4
59982                                                     desc // 5
59983                                             );
59984                                         });
59985                                     }
59986                                     d++;
59987                                 }
59988                                 
59989                                 // only do this if something added..
59990                                 if(added > 0){ 
59991                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59992                                 }
59993                                 
59994                                 
59995                                 // push it twice. (second one with an hour..
59996                                 
59997                             }
59998                             //Roo.log(result);
59999                             this.fireEvent("load", this, o, o.request.arg);
60000                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60001                         },
60002                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60003                     proxy : {
60004                         xtype: 'HttpProxy',
60005                         xns: Roo.data,
60006                         method : 'GET',
60007                         url : baseURL + '/Roo/Shop_course.php'
60008                     },
60009                     reader : {
60010                         xtype: 'JsonReader',
60011                         xns: Roo.data,
60012                         id : 'id',
60013                         fields : [
60014                             {
60015                                 'name': 'id',
60016                                 'type': 'int'
60017                             },
60018                             {
60019                                 'name': 'when_dt',
60020                                 'type': 'string'
60021                             },
60022                             {
60023                                 'name': 'end_dt',
60024                                 'type': 'string'
60025                             },
60026                             {
60027                                 'name': 'parent_id',
60028                                 'type': 'int'
60029                             },
60030                             {
60031                                 'name': 'product_id',
60032                                 'type': 'int'
60033                             },
60034                             {
60035                                 'name': 'productitem_id',
60036                                 'type': 'int'
60037                             },
60038                             {
60039                                 'name': 'guid',
60040                                 'type': 'int'
60041                             }
60042                         ]
60043                     }
60044                 },
60045                 toolbar : {
60046                     xtype: 'Toolbar',
60047                     xns: Roo,
60048                     items : [
60049                         {
60050                             xtype: 'Button',
60051                             xns: Roo.Toolbar,
60052                             listeners : {
60053                                 click : function (_self, e)
60054                                 {
60055                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60056                                     sd.setMonth(sd.getMonth()-1);
60057                                     _this.monthField.setValue(sd.format('Y-m-d'));
60058                                     _this.grid.ds.load({});
60059                                 }
60060                             },
60061                             text : "Back"
60062                         },
60063                         {
60064                             xtype: 'Separator',
60065                             xns: Roo.Toolbar
60066                         },
60067                         {
60068                             xtype: 'MonthField',
60069                             xns: Roo.form,
60070                             listeners : {
60071                                 render : function (_self)
60072                                 {
60073                                     _this.monthField = _self;
60074                                    // _this.monthField.set  today
60075                                 },
60076                                 select : function (combo, date)
60077                                 {
60078                                     _this.grid.ds.load({});
60079                                 }
60080                             },
60081                             value : (function() { return new Date(); })()
60082                         },
60083                         {
60084                             xtype: 'Separator',
60085                             xns: Roo.Toolbar
60086                         },
60087                         {
60088                             xtype: 'TextItem',
60089                             xns: Roo.Toolbar,
60090                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60091                         },
60092                         {
60093                             xtype: 'Fill',
60094                             xns: Roo.Toolbar
60095                         },
60096                         {
60097                             xtype: 'Button',
60098                             xns: Roo.Toolbar,
60099                             listeners : {
60100                                 click : function (_self, e)
60101                                 {
60102                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60103                                     sd.setMonth(sd.getMonth()+1);
60104                                     _this.monthField.setValue(sd.format('Y-m-d'));
60105                                     _this.grid.ds.load({});
60106                                 }
60107                             },
60108                             text : "Next"
60109                         }
60110                     ]
60111                 },
60112                  
60113             }
60114         };
60115         
60116         *//*
60117  * Based on:
60118  * Ext JS Library 1.1.1
60119  * Copyright(c) 2006-2007, Ext JS, LLC.
60120  *
60121  * Originally Released Under LGPL - original licence link has changed is not relivant.
60122  *
60123  * Fork - LGPL
60124  * <script type="text/javascript">
60125  */
60126  
60127 /**
60128  * @class Roo.LoadMask
60129  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60130  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60131  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60132  * element's UpdateManager load indicator and will be destroyed after the initial load.
60133  * @constructor
60134  * Create a new LoadMask
60135  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60136  * @param {Object} config The config object
60137  */
60138 Roo.LoadMask = function(el, config){
60139     this.el = Roo.get(el);
60140     Roo.apply(this, config);
60141     if(this.store){
60142         this.store.on('beforeload', this.onBeforeLoad, this);
60143         this.store.on('load', this.onLoad, this);
60144         this.store.on('loadexception', this.onLoadException, this);
60145         this.removeMask = false;
60146     }else{
60147         var um = this.el.getUpdateManager();
60148         um.showLoadIndicator = false; // disable the default indicator
60149         um.on('beforeupdate', this.onBeforeLoad, this);
60150         um.on('update', this.onLoad, this);
60151         um.on('failure', this.onLoad, this);
60152         this.removeMask = true;
60153     }
60154 };
60155
60156 Roo.LoadMask.prototype = {
60157     /**
60158      * @cfg {Boolean} removeMask
60159      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60160      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60161      */
60162     /**
60163      * @cfg {String} msg
60164      * The text to display in a centered loading message box (defaults to 'Loading...')
60165      */
60166     msg : 'Loading...',
60167     /**
60168      * @cfg {String} msgCls
60169      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60170      */
60171     msgCls : 'x-mask-loading',
60172
60173     /**
60174      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60175      * @type Boolean
60176      */
60177     disabled: false,
60178
60179     /**
60180      * Disables the mask to prevent it from being displayed
60181      */
60182     disable : function(){
60183        this.disabled = true;
60184     },
60185
60186     /**
60187      * Enables the mask so that it can be displayed
60188      */
60189     enable : function(){
60190         this.disabled = false;
60191     },
60192     
60193     onLoadException : function()
60194     {
60195         Roo.log(arguments);
60196         
60197         if (typeof(arguments[3]) != 'undefined') {
60198             Roo.MessageBox.alert("Error loading",arguments[3]);
60199         } 
60200         /*
60201         try {
60202             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60203                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60204             }   
60205         } catch(e) {
60206             
60207         }
60208         */
60209     
60210         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60211     },
60212     // private
60213     onLoad : function()
60214     {
60215         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60216     },
60217
60218     // private
60219     onBeforeLoad : function(){
60220         if(!this.disabled){
60221             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60222         }
60223     },
60224
60225     // private
60226     destroy : function(){
60227         if(this.store){
60228             this.store.un('beforeload', this.onBeforeLoad, this);
60229             this.store.un('load', this.onLoad, this);
60230             this.store.un('loadexception', this.onLoadException, this);
60231         }else{
60232             var um = this.el.getUpdateManager();
60233             um.un('beforeupdate', this.onBeforeLoad, this);
60234             um.un('update', this.onLoad, this);
60235             um.un('failure', this.onLoad, this);
60236         }
60237     }
60238 };/*
60239  * Based on:
60240  * Ext JS Library 1.1.1
60241  * Copyright(c) 2006-2007, Ext JS, LLC.
60242  *
60243  * Originally Released Under LGPL - original licence link has changed is not relivant.
60244  *
60245  * Fork - LGPL
60246  * <script type="text/javascript">
60247  */
60248
60249
60250 /**
60251  * @class Roo.XTemplate
60252  * @extends Roo.Template
60253  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60254 <pre><code>
60255 var t = new Roo.XTemplate(
60256         '&lt;select name="{name}"&gt;',
60257                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60258         '&lt;/select&gt;'
60259 );
60260  
60261 // then append, applying the master template values
60262  </code></pre>
60263  *
60264  * Supported features:
60265  *
60266  *  Tags:
60267
60268 <pre><code>
60269       {a_variable} - output encoded.
60270       {a_variable.format:("Y-m-d")} - call a method on the variable
60271       {a_variable:raw} - unencoded output
60272       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60273       {a_variable:this.method_on_template(...)} - call a method on the template object.
60274  
60275 </code></pre>
60276  *  The tpl tag:
60277 <pre><code>
60278         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60279         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60280         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60281         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60282   
60283         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60284         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60285 </code></pre>
60286  *      
60287  */
60288 Roo.XTemplate = function()
60289 {
60290     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60291     if (this.html) {
60292         this.compile();
60293     }
60294 };
60295
60296
60297 Roo.extend(Roo.XTemplate, Roo.Template, {
60298
60299     /**
60300      * The various sub templates
60301      */
60302     tpls : false,
60303     /**
60304      *
60305      * basic tag replacing syntax
60306      * WORD:WORD()
60307      *
60308      * // you can fake an object call by doing this
60309      *  x.t:(test,tesT) 
60310      * 
60311      */
60312     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60313
60314     /**
60315      * compile the template
60316      *
60317      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60318      *
60319      */
60320     compile: function()
60321     {
60322         var s = this.html;
60323      
60324         s = ['<tpl>', s, '</tpl>'].join('');
60325     
60326         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60327             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60328             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60329             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60330             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60331             m,
60332             id     = 0,
60333             tpls   = [];
60334     
60335         while(true == !!(m = s.match(re))){
60336             var forMatch   = m[0].match(nameRe),
60337                 ifMatch   = m[0].match(ifRe),
60338                 execMatch   = m[0].match(execRe),
60339                 namedMatch   = m[0].match(namedRe),
60340                 
60341                 exp  = null, 
60342                 fn   = null,
60343                 exec = null,
60344                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60345                 
60346             if (ifMatch) {
60347                 // if - puts fn into test..
60348                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60349                 if(exp){
60350                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60351                 }
60352             }
60353             
60354             if (execMatch) {
60355                 // exec - calls a function... returns empty if true is  returned.
60356                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60357                 if(exp){
60358                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60359                 }
60360             }
60361             
60362             
60363             if (name) {
60364                 // for = 
60365                 switch(name){
60366                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60367                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60368                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60369                 }
60370             }
60371             var uid = namedMatch ? namedMatch[1] : id;
60372             
60373             
60374             tpls.push({
60375                 id:     namedMatch ? namedMatch[1] : id,
60376                 target: name,
60377                 exec:   exec,
60378                 test:   fn,
60379                 body:   m[1] || ''
60380             });
60381             if (namedMatch) {
60382                 s = s.replace(m[0], '');
60383             } else { 
60384                 s = s.replace(m[0], '{xtpl'+ id + '}');
60385             }
60386             ++id;
60387         }
60388         this.tpls = [];
60389         for(var i = tpls.length-1; i >= 0; --i){
60390             this.compileTpl(tpls[i]);
60391             this.tpls[tpls[i].id] = tpls[i];
60392         }
60393         this.master = tpls[tpls.length-1];
60394         return this;
60395     },
60396     /**
60397      * same as applyTemplate, except it's done to one of the subTemplates
60398      * when using named templates, you can do:
60399      *
60400      * var str = pl.applySubTemplate('your-name', values);
60401      *
60402      * 
60403      * @param {Number} id of the template
60404      * @param {Object} values to apply to template
60405      * @param {Object} parent (normaly the instance of this object)
60406      */
60407     applySubTemplate : function(id, values, parent)
60408     {
60409         
60410         
60411         var t = this.tpls[id];
60412         
60413         
60414         try { 
60415             if(t.test && !t.test.call(this, values, parent)){
60416                 return '';
60417             }
60418         } catch(e) {
60419             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60420             Roo.log(e.toString());
60421             Roo.log(t.test);
60422             return ''
60423         }
60424         try { 
60425             
60426             if(t.exec && t.exec.call(this, values, parent)){
60427                 return '';
60428             }
60429         } catch(e) {
60430             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60431             Roo.log(e.toString());
60432             Roo.log(t.exec);
60433             return ''
60434         }
60435         try {
60436             var vs = t.target ? t.target.call(this, values, parent) : values;
60437             parent = t.target ? values : parent;
60438             if(t.target && vs instanceof Array){
60439                 var buf = [];
60440                 for(var i = 0, len = vs.length; i < len; i++){
60441                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60442                 }
60443                 return buf.join('');
60444             }
60445             return t.compiled.call(this, vs, parent);
60446         } catch (e) {
60447             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60448             Roo.log(e.toString());
60449             Roo.log(t.compiled);
60450             return '';
60451         }
60452     },
60453
60454     compileTpl : function(tpl)
60455     {
60456         var fm = Roo.util.Format;
60457         var useF = this.disableFormats !== true;
60458         var sep = Roo.isGecko ? "+" : ",";
60459         var undef = function(str) {
60460             Roo.log("Property not found :"  + str);
60461             return '';
60462         };
60463         
60464         var fn = function(m, name, format, args)
60465         {
60466             //Roo.log(arguments);
60467             args = args ? args.replace(/\\'/g,"'") : args;
60468             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60469             if (typeof(format) == 'undefined') {
60470                 format= 'htmlEncode';
60471             }
60472             if (format == 'raw' ) {
60473                 format = false;
60474             }
60475             
60476             if(name.substr(0, 4) == 'xtpl'){
60477                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60478             }
60479             
60480             // build an array of options to determine if value is undefined..
60481             
60482             // basically get 'xxxx.yyyy' then do
60483             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60484             //    (function () { Roo.log("Property not found"); return ''; })() :
60485             //    ......
60486             
60487             var udef_ar = [];
60488             var lookfor = '';
60489             Roo.each(name.split('.'), function(st) {
60490                 lookfor += (lookfor.length ? '.': '') + st;
60491                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60492             });
60493             
60494             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60495             
60496             
60497             if(format && useF){
60498                 
60499                 args = args ? ',' + args : "";
60500                  
60501                 if(format.substr(0, 5) != "this."){
60502                     format = "fm." + format + '(';
60503                 }else{
60504                     format = 'this.call("'+ format.substr(5) + '", ';
60505                     args = ", values";
60506                 }
60507                 
60508                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60509             }
60510              
60511             if (args.length) {
60512                 // called with xxyx.yuu:(test,test)
60513                 // change to ()
60514                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60515             }
60516             // raw.. - :raw modifier..
60517             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60518             
60519         };
60520         var body;
60521         // branched to use + in gecko and [].join() in others
60522         if(Roo.isGecko){
60523             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60524                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60525                     "';};};";
60526         }else{
60527             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60528             body.push(tpl.body.replace(/(\r\n|\n)/g,
60529                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60530             body.push("'].join('');};};");
60531             body = body.join('');
60532         }
60533         
60534         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60535        
60536         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60537         eval(body);
60538         
60539         return this;
60540     },
60541
60542     applyTemplate : function(values){
60543         return this.master.compiled.call(this, values, {});
60544         //var s = this.subs;
60545     },
60546
60547     apply : function(){
60548         return this.applyTemplate.apply(this, arguments);
60549     }
60550
60551  });
60552
60553 Roo.XTemplate.from = function(el){
60554     el = Roo.getDom(el);
60555     return new Roo.XTemplate(el.value || el.innerHTML);
60556 };