140c020c02ba595c6028db497ed89c7fd0577bcb
[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         isTouch =  (function() {
68             try {
69                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     window.addEventListener('touchstart', function __set_has_touch__ () {
71                         Roo.isTouch = true;
72                         window.removeEventListener('touchstart', __set_has_touch__);
73                     });
74                     return false; // no touch on chrome!?
75                 }
76                 document.createEvent("TouchEvent");  
77                 return true;  
78             } catch (e) {  
79                 return false;  
80             } 
81             
82         })();
83     // remove css image flicker
84         if(isIE && !isIE7){
85         try{
86             document.execCommand("BackgroundImageCache", false, true);
87         }catch(e){}
88     }
89     
90     Roo.apply(Roo, {
91         /**
92          * True if the browser is in strict mode
93          * @type Boolean
94          */
95         isStrict : isStrict,
96         /**
97          * True if the page is running over SSL
98          * @type Boolean
99          */
100         isSecure : isSecure,
101         /**
102          * True when the document is fully initialized and ready for action
103          * @type Boolean
104          */
105         isReady : false,
106         /**
107          * Turn on debugging output (currently only the factory uses this)
108          * @type Boolean
109          */
110         
111         debug: false,
112
113         /**
114          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
115          * @type Boolean
116          */
117         enableGarbageCollector : true,
118
119         /**
120          * True to automatically purge event listeners after uncaching an element (defaults to false).
121          * Note: this only happens if enableGarbageCollector is true.
122          * @type Boolean
123          */
124         enableListenerCollection:false,
125
126         /**
127          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
128          * the IE insecure content warning (defaults to javascript:false).
129          * @type String
130          */
131         SSL_SECURE_URL : "javascript:false",
132
133         /**
134          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
135          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
136          * @type String
137          */
138         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
139
140         emptyFn : function(){},
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * 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.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * 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]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isFirefox : isFirefox,
624         /** @type Boolean */
625         isIE : isIE,
626         /** @type Boolean */
627         isIE7 : isIE7,
628         /** @type Boolean */
629         isIE11 : isIE11,
630         /** @type Boolean */
631         isGecko : isGecko,
632         /** @type Boolean */
633         isBorderBox : isBorderBox,
634         /** @type Boolean */
635         isWindows : isWindows,
636         /** @type Boolean */
637         isLinux : isLinux,
638         /** @type Boolean */
639         isMac : isMac,
640         /** @type Boolean */
641         isIOS : isIOS,
642         /** @type Boolean */
643         isTouch : isTouch,
644
645         /**
646          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
647          * you may want to set this to true.
648          * @type Boolean
649          */
650         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
651         
652         
653                 
654         /**
655          * Selects a single element as a Roo Element
656          * This is about as close as you can get to jQuery's $('do crazy stuff')
657          * @param {String} selector The selector/xpath query
658          * @param {Node} root (optional) The start of the query (defaults to document).
659          * @return {Roo.Element}
660          */
661         selectNode : function(selector, root) 
662         {
663             var node = Roo.DomQuery.selectNode(selector,root);
664             return node ? Roo.get(node) : new Roo.Element(false);
665         }
666         
667     });
668
669
670 })();
671
672 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
673                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
674                 "Roo.app", "Roo.ux",
675                 "Roo.bootstrap",
676                 "Roo.bootstrap.dash");
677 /*
678  * Based on:
679  * Ext JS Library 1.1.1
680  * Copyright(c) 2006-2007, Ext JS, LLC.
681  *
682  * Originally Released Under LGPL - original licence link has changed is not relivant.
683  *
684  * Fork - LGPL
685  * <script type="text/javascript">
686  */
687
688 (function() {    
689     // wrappedn so fnCleanup is not in global scope...
690     if(Roo.isIE) {
691         function fnCleanUp() {
692             var p = Function.prototype;
693             delete p.createSequence;
694             delete p.defer;
695             delete p.createDelegate;
696             delete p.createCallback;
697             delete p.createInterceptor;
698
699             window.detachEvent("onunload", fnCleanUp);
700         }
701         window.attachEvent("onunload", fnCleanUp);
702     }
703 })();
704
705
706 /**
707  * @class Function
708  * These functions are available on every Function object (any JavaScript function).
709  */
710 Roo.apply(Function.prototype, {
711      /**
712      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
713      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
714      * Will create a function that is bound to those 2 args.
715      * @return {Function} The new function
716     */
717     createCallback : function(/*args...*/){
718         // make args available, in function below
719         var args = arguments;
720         var method = this;
721         return function() {
722             return method.apply(window, args);
723         };
724     },
725
726     /**
727      * Creates a delegate (callback) that sets the scope to obj.
728      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
729      * Will create a function that is automatically scoped to this.
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Function} The new function
735      */
736     createDelegate : function(obj, args, appendArgs){
737         var method = this;
738         return function() {
739             var callArgs = args || arguments;
740             if(appendArgs === true){
741                 callArgs = Array.prototype.slice.call(arguments, 0);
742                 callArgs = callArgs.concat(args);
743             }else if(typeof appendArgs == "number"){
744                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
745                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
746                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
747             }
748             return method.apply(obj || window, callArgs);
749         };
750     },
751
752     /**
753      * Calls this function after the number of millseconds specified.
754      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
755      * @param {Object} obj (optional) The object for which the scope is set
756      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
757      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
758      *                                             if a number the args are inserted at the specified position
759      * @return {Number} The timeout id that can be used with clearTimeout
760      */
761     defer : function(millis, obj, args, appendArgs){
762         var fn = this.createDelegate(obj, args, appendArgs);
763         if(millis){
764             return setTimeout(fn, millis);
765         }
766         fn();
767         return 0;
768     },
769     /**
770      * Create a combined function call sequence of the original function + the passed function.
771      * The resulting function returns the results of the original function.
772      * The passed fcn is called with the parameters of the original function
773      * @param {Function} fcn The function to sequence
774      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
775      * @return {Function} The new function
776      */
777     createSequence : function(fcn, scope){
778         if(typeof fcn != "function"){
779             return this;
780         }
781         var method = this;
782         return function() {
783             var retval = method.apply(this || window, arguments);
784             fcn.apply(scope || this || window, arguments);
785             return retval;
786         };
787     },
788
789     /**
790      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
791      * The resulting function returns the results of the original function.
792      * The passed fcn is called with the parameters of the original function.
793      * @addon
794      * @param {Function} fcn The function to call before the original
795      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
796      * @return {Function} The new function
797      */
798     createInterceptor : function(fcn, scope){
799         if(typeof fcn != "function"){
800             return this;
801         }
802         var method = this;
803         return function() {
804             fcn.target = this;
805             fcn.method = method;
806             if(fcn.apply(scope || this || window, arguments) === false){
807                 return;
808             }
809             return method.apply(this || window, arguments);
810         };
811     }
812 });
813 /*
814  * Based on:
815  * Ext JS Library 1.1.1
816  * Copyright(c) 2006-2007, Ext JS, LLC.
817  *
818  * Originally Released Under LGPL - original licence link has changed is not relivant.
819  *
820  * Fork - LGPL
821  * <script type="text/javascript">
822  */
823
824 Roo.applyIf(String, {
825     
826     /** @scope String */
827     
828     /**
829      * Escapes the passed string for ' and \
830      * @param {String} string The string to escape
831      * @return {String} The escaped string
832      * @static
833      */
834     escape : function(string) {
835         return string.replace(/('|\\)/g, "\\$1");
836     },
837
838     /**
839      * Pads the left side of a string with a specified character.  This is especially useful
840      * for normalizing number and date strings.  Example usage:
841      * <pre><code>
842 var s = String.leftPad('123', 5, '0');
843 // s now contains the string: '00123'
844 </code></pre>
845      * @param {String} string The original string
846      * @param {Number} size The total length of the output string
847      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
848      * @return {String} The padded string
849      * @static
850      */
851     leftPad : function (val, size, ch) {
852         var result = new String(val);
853         if(ch === null || ch === undefined || ch === '') {
854             ch = " ";
855         }
856         while (result.length < size) {
857             result = ch + result;
858         }
859         return result;
860     },
861
862     /**
863      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
864      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
865      * <pre><code>
866 var cls = 'my-class', text = 'Some text';
867 var s = String.format('<div class="{0}">{1}</div>', cls, text);
868 // s now contains the string: '<div class="my-class">Some text</div>'
869 </code></pre>
870      * @param {String} string The tokenized string to be formatted
871      * @param {String} value1 The value to replace token {0}
872      * @param {String} value2 Etc...
873      * @return {String} The formatted string
874      * @static
875      */
876     format : function(format){
877         var args = Array.prototype.slice.call(arguments, 1);
878         return format.replace(/\{(\d+)\}/g, function(m, i){
879             return Roo.util.Format.htmlEncode(args[i]);
880         });
881     }
882 });
883
884 /**
885  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
886  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
887  * they are already different, the first value passed in is returned.  Note that this method returns the new value
888  * but does not change the current string.
889  * <pre><code>
890 // alternate sort directions
891 sort = sort.toggle('ASC', 'DESC');
892
893 // instead of conditional logic:
894 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
895 </code></pre>
896  * @param {String} value The value to compare to the current string
897  * @param {String} other The new value to use if the string already equals the first value passed in
898  * @return {String} The new value
899  */
900  
901 String.prototype.toggle = function(value, other){
902     return this == value ? other : value;
903 };/*
904  * Based on:
905  * Ext JS Library 1.1.1
906  * Copyright(c) 2006-2007, Ext JS, LLC.
907  *
908  * Originally Released Under LGPL - original licence link has changed is not relivant.
909  *
910  * Fork - LGPL
911  * <script type="text/javascript">
912  */
913
914  /**
915  * @class Number
916  */
917 Roo.applyIf(Number.prototype, {
918     /**
919      * Checks whether or not the current number is within a desired range.  If the number is already within the
920      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
921      * exceeded.  Note that this method returns the constrained value but does not change the current number.
922      * @param {Number} min The minimum number in the range
923      * @param {Number} max The maximum number in the range
924      * @return {Number} The constrained value if outside the range, otherwise the current value
925      */
926     constrain : function(min, max){
927         return Math.min(Math.max(this, min), max);
928     }
929 });/*
930  * Based on:
931  * Ext JS Library 1.1.1
932  * Copyright(c) 2006-2007, Ext JS, LLC.
933  *
934  * Originally Released Under LGPL - original licence link has changed is not relivant.
935  *
936  * Fork - LGPL
937  * <script type="text/javascript">
938  */
939  /**
940  * @class Array
941  */
942 Roo.applyIf(Array.prototype, {
943     /**
944      * 
945      * Checks whether or not the specified object exists in the array.
946      * @param {Object} o The object to check for
947      * @return {Number} The index of o in the array (or -1 if it is not found)
948      */
949     indexOf : function(o){
950        for (var i = 0, len = this.length; i < len; i++){
951               if(this[i] == o) { return i; }
952        }
953            return -1;
954     },
955
956     /**
957      * Removes the specified object from the array.  If the object is not found nothing happens.
958      * @param {Object} o The object to remove
959      */
960     remove : function(o){
961        var index = this.indexOf(o);
962        if(index != -1){
963            this.splice(index, 1);
964        }
965     },
966     /**
967      * Map (JS 1.6 compatibility)
968      * @param {Function} function  to call
969      */
970     map : function(fun )
971     {
972         var len = this.length >>> 0;
973         if (typeof fun != "function") {
974             throw new TypeError();
975         }
976         var res = new Array(len);
977         var thisp = arguments[1];
978         for (var i = 0; i < len; i++)
979         {
980             if (i in this) {
981                 res[i] = fun.call(thisp, this[i], i, this);
982             }
983         }
984
985         return res;
986     }
987     
988 });
989
990
991  
992 /*
993  * Based on:
994  * Ext JS Library 1.1.1
995  * Copyright(c) 2006-2007, Ext JS, LLC.
996  *
997  * Originally Released Under LGPL - original licence link has changed is not relivant.
998  *
999  * Fork - LGPL
1000  * <script type="text/javascript">
1001  */
1002
1003 /**
1004  * @class Date
1005  *
1006  * The date parsing and format syntax is a subset of
1007  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1008  * supported will provide results equivalent to their PHP versions.
1009  *
1010  * Following is the list of all currently supported formats:
1011  *<pre>
1012 Sample date:
1013 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1014
1015 Format  Output      Description
1016 ------  ----------  --------------------------------------------------------------
1017   d      10         Day of the month, 2 digits with leading zeros
1018   D      Wed        A textual representation of a day, three letters
1019   j      10         Day of the month without leading zeros
1020   l      Wednesday  A full textual representation of the day of the week
1021   S      th         English ordinal day of month suffix, 2 chars (use with j)
1022   w      3          Numeric representation of the day of the week
1023   z      9          The julian date, or day of the year (0-365)
1024   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1025   F      January    A full textual representation of the month
1026   m      01         Numeric representation of a month, with leading zeros
1027   M      Jan        Month name abbreviation, three letters
1028   n      1          Numeric representation of a month, without leading zeros
1029   t      31         Number of days in the given month
1030   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1031   Y      2007       A full numeric representation of a year, 4 digits
1032   y      07         A two digit representation of a year
1033   a      pm         Lowercase Ante meridiem and Post meridiem
1034   A      PM         Uppercase Ante meridiem and Post meridiem
1035   g      3          12-hour format of an hour without leading zeros
1036   G      15         24-hour format of an hour without leading zeros
1037   h      03         12-hour format of an hour with leading zeros
1038   H      15         24-hour format of an hour with leading zeros
1039   i      05         Minutes with leading zeros
1040   s      01         Seconds, with leading zeros
1041   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1042   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1043   T      CST        Timezone setting of the machine running the code
1044   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1045 </pre>
1046  *
1047  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1048  * <pre><code>
1049 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1050 document.write(dt.format('Y-m-d'));                         //2007-01-10
1051 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1052 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
1053  </code></pre>
1054  *
1055  * Here are some standard date/time patterns that you might find helpful.  They
1056  * are not part of the source of Date.js, but to use them you can simply copy this
1057  * block of code into any script that is included after Date.js and they will also become
1058  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1059  * <pre><code>
1060 Date.patterns = {
1061     ISO8601Long:"Y-m-d H:i:s",
1062     ISO8601Short:"Y-m-d",
1063     ShortDate: "n/j/Y",
1064     LongDate: "l, F d, Y",
1065     FullDateTime: "l, F d, Y g:i:s A",
1066     MonthDay: "F d",
1067     ShortTime: "g:i A",
1068     LongTime: "g:i:s A",
1069     SortableDateTime: "Y-m-d\\TH:i:s",
1070     UniversalSortableDateTime: "Y-m-d H:i:sO",
1071     YearMonth: "F, Y"
1072 };
1073 </code></pre>
1074  *
1075  * Example usage:
1076  * <pre><code>
1077 var dt = new Date();
1078 document.write(dt.format(Date.patterns.ShortDate));
1079  </code></pre>
1080  */
1081
1082 /*
1083  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1084  * They generate precompiled functions from date formats instead of parsing and
1085  * processing the pattern every time you format a date.  These functions are available
1086  * on every Date object (any javascript function).
1087  *
1088  * The original article and download are here:
1089  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1090  *
1091  */
1092  
1093  
1094  // was in core
1095 /**
1096  Returns the number of milliseconds between this date and date
1097  @param {Date} date (optional) Defaults to now
1098  @return {Number} The diff in milliseconds
1099  @member Date getElapsed
1100  */
1101 Date.prototype.getElapsed = function(date) {
1102         return Math.abs((date || new Date()).getTime()-this.getTime());
1103 };
1104 // was in date file..
1105
1106
1107 // private
1108 Date.parseFunctions = {count:0};
1109 // private
1110 Date.parseRegexes = [];
1111 // private
1112 Date.formatFunctions = {count:0};
1113
1114 // private
1115 Date.prototype.dateFormat = function(format) {
1116     if (Date.formatFunctions[format] == null) {
1117         Date.createNewFormat(format);
1118     }
1119     var func = Date.formatFunctions[format];
1120     return this[func]();
1121 };
1122
1123
1124 /**
1125  * Formats a date given the supplied format string
1126  * @param {String} format The format string
1127  * @return {String} The formatted date
1128  * @method
1129  */
1130 Date.prototype.format = Date.prototype.dateFormat;
1131
1132 // private
1133 Date.createNewFormat = function(format) {
1134     var funcName = "format" + Date.formatFunctions.count++;
1135     Date.formatFunctions[format] = funcName;
1136     var code = "Date.prototype." + funcName + " = function(){return ";
1137     var special = false;
1138     var ch = '';
1139     for (var i = 0; i < format.length; ++i) {
1140         ch = format.charAt(i);
1141         if (!special && ch == "\\") {
1142             special = true;
1143         }
1144         else if (special) {
1145             special = false;
1146             code += "'" + String.escape(ch) + "' + ";
1147         }
1148         else {
1149             code += Date.getFormatCode(ch);
1150         }
1151     }
1152     /** eval:var:zzzzzzzzzzzzz */
1153     eval(code.substring(0, code.length - 3) + ";}");
1154 };
1155
1156 // private
1157 Date.getFormatCode = function(character) {
1158     switch (character) {
1159     case "d":
1160         return "String.leftPad(this.getDate(), 2, '0') + ";
1161     case "D":
1162         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1163     case "j":
1164         return "this.getDate() + ";
1165     case "l":
1166         return "Date.dayNames[this.getDay()] + ";
1167     case "S":
1168         return "this.getSuffix() + ";
1169     case "w":
1170         return "this.getDay() + ";
1171     case "z":
1172         return "this.getDayOfYear() + ";
1173     case "W":
1174         return "this.getWeekOfYear() + ";
1175     case "F":
1176         return "Date.monthNames[this.getMonth()] + ";
1177     case "m":
1178         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1179     case "M":
1180         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1181     case "n":
1182         return "(this.getMonth() + 1) + ";
1183     case "t":
1184         return "this.getDaysInMonth() + ";
1185     case "L":
1186         return "(this.isLeapYear() ? 1 : 0) + ";
1187     case "Y":
1188         return "this.getFullYear() + ";
1189     case "y":
1190         return "('' + this.getFullYear()).substring(2, 4) + ";
1191     case "a":
1192         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1193     case "A":
1194         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1195     case "g":
1196         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1197     case "G":
1198         return "this.getHours() + ";
1199     case "h":
1200         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1201     case "H":
1202         return "String.leftPad(this.getHours(), 2, '0') + ";
1203     case "i":
1204         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1205     case "s":
1206         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1207     case "O":
1208         return "this.getGMTOffset() + ";
1209     case "P":
1210         return "this.getGMTColonOffset() + ";
1211     case "T":
1212         return "this.getTimezone() + ";
1213     case "Z":
1214         return "(this.getTimezoneOffset() * -60) + ";
1215     default:
1216         return "'" + String.escape(character) + "' + ";
1217     }
1218 };
1219
1220 /**
1221  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1222  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1223  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1224  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1225  * string or the parse operation will fail.
1226  * Example Usage:
1227 <pre><code>
1228 //dt = Fri May 25 2007 (current date)
1229 var dt = new Date();
1230
1231 //dt = Thu May 25 2006 (today's month/day in 2006)
1232 dt = Date.parseDate("2006", "Y");
1233
1234 //dt = Sun Jan 15 2006 (all date parts specified)
1235 dt = Date.parseDate("2006-1-15", "Y-m-d");
1236
1237 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1238 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1239 </code></pre>
1240  * @param {String} input The unparsed date as a string
1241  * @param {String} format The format the date is in
1242  * @return {Date} The parsed date
1243  * @static
1244  */
1245 Date.parseDate = function(input, format) {
1246     if (Date.parseFunctions[format] == null) {
1247         Date.createParser(format);
1248     }
1249     var func = Date.parseFunctions[format];
1250     return Date[func](input);
1251 };
1252 /**
1253  * @private
1254  */
1255
1256 Date.createParser = function(format) {
1257     var funcName = "parse" + Date.parseFunctions.count++;
1258     var regexNum = Date.parseRegexes.length;
1259     var currentGroup = 1;
1260     Date.parseFunctions[format] = funcName;
1261
1262     var code = "Date." + funcName + " = function(input){\n"
1263         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1264         + "var d = new Date();\n"
1265         + "y = d.getFullYear();\n"
1266         + "m = d.getMonth();\n"
1267         + "d = d.getDate();\n"
1268         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1269         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1270         + "if (results && results.length > 0) {";
1271     var regex = "";
1272
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             regex += String.escape(ch);
1283         }
1284         else {
1285             var obj = Date.formatCodeToRegex(ch, currentGroup);
1286             currentGroup += obj.g;
1287             regex += obj.s;
1288             if (obj.g && obj.c) {
1289                 code += obj.c;
1290             }
1291         }
1292     }
1293
1294     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1295         + "{v = new Date(y, m, d, h, i, s);}\n"
1296         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1297         + "{v = new Date(y, m, d, h, i);}\n"
1298         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1299         + "{v = new Date(y, m, d, h);}\n"
1300         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1301         + "{v = new Date(y, m, d);}\n"
1302         + "else if (y >= 0 && m >= 0)\n"
1303         + "{v = new Date(y, m);}\n"
1304         + "else if (y >= 0)\n"
1305         + "{v = new Date(y);}\n"
1306         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1307         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1308         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1309         + ";}";
1310
1311     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1312     /** eval:var:zzzzzzzzzzzzz */
1313     eval(code);
1314 };
1315
1316 // private
1317 Date.formatCodeToRegex = function(character, currentGroup) {
1318     switch (character) {
1319     case "D":
1320         return {g:0,
1321         c:null,
1322         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1323     case "j":
1324         return {g:1,
1325             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1326             s:"(\\d{1,2})"}; // day of month without leading zeroes
1327     case "d":
1328         return {g:1,
1329             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1330             s:"(\\d{2})"}; // day of month with leading zeroes
1331     case "l":
1332         return {g:0,
1333             c:null,
1334             s:"(?:" + Date.dayNames.join("|") + ")"};
1335     case "S":
1336         return {g:0,
1337             c:null,
1338             s:"(?:st|nd|rd|th)"};
1339     case "w":
1340         return {g:0,
1341             c:null,
1342             s:"\\d"};
1343     case "z":
1344         return {g:0,
1345             c:null,
1346             s:"(?:\\d{1,3})"};
1347     case "W":
1348         return {g:0,
1349             c:null,
1350             s:"(?:\\d{2})"};
1351     case "F":
1352         return {g:1,
1353             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1354             s:"(" + Date.monthNames.join("|") + ")"};
1355     case "M":
1356         return {g:1,
1357             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1358             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1359     case "n":
1360         return {g:1,
1361             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1362             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1363     case "m":
1364         return {g:1,
1365             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1366             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1367     case "t":
1368         return {g:0,
1369             c:null,
1370             s:"\\d{1,2}"};
1371     case "L":
1372         return {g:0,
1373             c:null,
1374             s:"(?:1|0)"};
1375     case "Y":
1376         return {g:1,
1377             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1378             s:"(\\d{4})"};
1379     case "y":
1380         return {g:1,
1381             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1382                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1383             s:"(\\d{1,2})"};
1384     case "a":
1385         return {g:1,
1386             c:"if (results[" + currentGroup + "] == 'am') {\n"
1387                 + "if (h == 12) { h = 0; }\n"
1388                 + "} else { if (h < 12) { h += 12; }}",
1389             s:"(am|pm)"};
1390     case "A":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(AM|PM)"};
1396     case "g":
1397     case "G":
1398         return {g:1,
1399             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1401     case "h":
1402     case "H":
1403         return {g:1,
1404             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1405             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1406     case "i":
1407         return {g:1,
1408             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1409             s:"(\\d{2})"};
1410     case "s":
1411         return {g:1,
1412             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1413             s:"(\\d{2})"};
1414     case "O":
1415         return {g:1,
1416             c:[
1417                 "o = results[", currentGroup, "];\n",
1418                 "var sn = o.substring(0,1);\n", // get + / - sign
1419                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1420                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1421                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1422                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1423             ].join(""),
1424             s:"([+\-]\\d{2,4})"};
1425     
1426     
1427     case "P":
1428         return {g:1,
1429                 c:[
1430                    "o = results[", currentGroup, "];\n",
1431                    "var sn = o.substring(0,1);\n",
1432                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1433                    "var mn = o.substring(4,6) % 60;\n",
1434                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1435                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1436             ].join(""),
1437             s:"([+\-]\\d{4})"};
1438     case "T":
1439         return {g:0,
1440             c:null,
1441             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1442     case "Z":
1443         return {g:1,
1444             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1445                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1446             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1447     default:
1448         return {g:0,
1449             c:null,
1450             s:String.escape(character)};
1451     }
1452 };
1453
1454 /**
1455  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1456  * @return {String} The abbreviated timezone name (e.g. 'CST')
1457  */
1458 Date.prototype.getTimezone = function() {
1459     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1460 };
1461
1462 /**
1463  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1464  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1465  */
1466 Date.prototype.getGMTOffset = function() {
1467     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1468         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1469         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1470 };
1471
1472 /**
1473  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1474  * @return {String} 2-characters representing hours and 2-characters representing minutes
1475  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1476  */
1477 Date.prototype.getGMTColonOffset = function() {
1478         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1479                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1480                 + ":"
1481                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1482 }
1483
1484 /**
1485  * Get the numeric day number of the year, adjusted for leap year.
1486  * @return {Number} 0 through 364 (365 in leap years)
1487  */
1488 Date.prototype.getDayOfYear = function() {
1489     var num = 0;
1490     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1491     for (var i = 0; i < this.getMonth(); ++i) {
1492         num += Date.daysInMonth[i];
1493     }
1494     return num + this.getDate() - 1;
1495 };
1496
1497 /**
1498  * Get the string representation of the numeric week number of the year
1499  * (equivalent to the format specifier 'W').
1500  * @return {String} '00' through '52'
1501  */
1502 Date.prototype.getWeekOfYear = function() {
1503     // Skip to Thursday of this week
1504     var now = this.getDayOfYear() + (4 - this.getDay());
1505     // Find the first Thursday of the year
1506     var jan1 = new Date(this.getFullYear(), 0, 1);
1507     var then = (7 - jan1.getDay() + 4);
1508     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1509 };
1510
1511 /**
1512  * Whether or not the current date is in a leap year.
1513  * @return {Boolean} True if the current date is in a leap year, else false
1514  */
1515 Date.prototype.isLeapYear = function() {
1516     var year = this.getFullYear();
1517     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1518 };
1519
1520 /**
1521  * Get the first day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getFirstDayOfMonth = function() {
1532     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536 /**
1537  * Get the last day of the current month, adjusted for leap year.  The returned value
1538  * is the numeric day index within the week (0-6) which can be used in conjunction with
1539  * the {@link #monthNames} array to retrieve the textual day name.
1540  * Example:
1541  *<pre><code>
1542 var dt = new Date('1/10/2007');
1543 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1544 </code></pre>
1545  * @return {Number} The day number (0-6)
1546  */
1547 Date.prototype.getLastDayOfMonth = function() {
1548     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1549     return (day < 0) ? (day + 7) : day;
1550 };
1551
1552
1553 /**
1554  * Get the first date of this date's month
1555  * @return {Date}
1556  */
1557 Date.prototype.getFirstDateOfMonth = function() {
1558     return new Date(this.getFullYear(), this.getMonth(), 1);
1559 };
1560
1561 /**
1562  * Get the last date of this date's month
1563  * @return {Date}
1564  */
1565 Date.prototype.getLastDateOfMonth = function() {
1566     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1567 };
1568 /**
1569  * Get the number of days in the current month, adjusted for leap year.
1570  * @return {Number} The number of days in the month
1571  */
1572 Date.prototype.getDaysInMonth = function() {
1573     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1574     return Date.daysInMonth[this.getMonth()];
1575 };
1576
1577 /**
1578  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1579  * @return {String} 'st, 'nd', 'rd' or 'th'
1580  */
1581 Date.prototype.getSuffix = function() {
1582     switch (this.getDate()) {
1583         case 1:
1584         case 21:
1585         case 31:
1586             return "st";
1587         case 2:
1588         case 22:
1589             return "nd";
1590         case 3:
1591         case 23:
1592             return "rd";
1593         default:
1594             return "th";
1595     }
1596 };
1597
1598 // private
1599 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1600
1601 /**
1602  * An array of textual month names.
1603  * Override these values for international dates, for example...
1604  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1605  * @type Array
1606  * @static
1607  */
1608 Date.monthNames =
1609    ["January",
1610     "February",
1611     "March",
1612     "April",
1613     "May",
1614     "June",
1615     "July",
1616     "August",
1617     "September",
1618     "October",
1619     "November",
1620     "December"];
1621
1622 /**
1623  * An array of textual day names.
1624  * Override these values for international dates, for example...
1625  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1626  * @type Array
1627  * @static
1628  */
1629 Date.dayNames =
1630    ["Sunday",
1631     "Monday",
1632     "Tuesday",
1633     "Wednesday",
1634     "Thursday",
1635     "Friday",
1636     "Saturday"];
1637
1638 // private
1639 Date.y2kYear = 50;
1640 // private
1641 Date.monthNumbers = {
1642     Jan:0,
1643     Feb:1,
1644     Mar:2,
1645     Apr:3,
1646     May:4,
1647     Jun:5,
1648     Jul:6,
1649     Aug:7,
1650     Sep:8,
1651     Oct:9,
1652     Nov:10,
1653     Dec:11};
1654
1655 /**
1656  * Creates and returns a new Date instance with the exact same date value as the called instance.
1657  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1658  * variable will also be changed.  When the intention is to create a new variable that will not
1659  * modify the original instance, you should create a clone.
1660  *
1661  * Example of correctly cloning a date:
1662  * <pre><code>
1663 //wrong way:
1664 var orig = new Date('10/1/2006');
1665 var copy = orig;
1666 copy.setDate(5);
1667 document.write(orig);  //returns 'Thu Oct 05 2006'!
1668
1669 //correct way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig.clone();
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 01 2006'
1674 </code></pre>
1675  * @return {Date} The new Date instance
1676  */
1677 Date.prototype.clone = function() {
1678         return new Date(this.getTime());
1679 };
1680
1681 /**
1682  * Clears any time information from this date
1683  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1684  @return {Date} this or the clone
1685  */
1686 Date.prototype.clearTime = function(clone){
1687     if(clone){
1688         return this.clone().clearTime();
1689     }
1690     this.setHours(0);
1691     this.setMinutes(0);
1692     this.setSeconds(0);
1693     this.setMilliseconds(0);
1694     return this;
1695 };
1696
1697 // private
1698 // safari setMonth is broken -- check that this is only donw once...
1699 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1700     Date.brokenSetMonth = Date.prototype.setMonth;
1701         Date.prototype.setMonth = function(num){
1702                 if(num <= -1){
1703                         var n = Math.ceil(-num);
1704                         var back_year = Math.ceil(n/12);
1705                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1706                         this.setFullYear(this.getFullYear() - back_year);
1707                         return Date.brokenSetMonth.call(this, month);
1708                 } else {
1709                         return Date.brokenSetMonth.apply(this, arguments);
1710                 }
1711         };
1712 }
1713
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MILLI = "ms";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.SECOND = "s";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MINUTE = "mi";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.HOUR = "h";
1730 /** Date interval constant 
1731 * @static 
1732 * @type String */
1733 Date.DAY = "d";
1734 /** Date interval constant 
1735 * @static 
1736 * @type String */
1737 Date.MONTH = "mo";
1738 /** Date interval constant 
1739 * @static 
1740 * @type String */
1741 Date.YEAR = "y";
1742
1743 /**
1744  * Provides a convenient method of performing basic date arithmetic.  This method
1745  * does not modify the Date instance being called - it creates and returns
1746  * a new Date instance containing the resulting date value.
1747  *
1748  * Examples:
1749  * <pre><code>
1750 //Basic usage:
1751 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1752 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1753
1754 //Negative values will subtract correctly:
1755 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1756 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1757
1758 //You can even chain several calls together in one line!
1759 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1760 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1761  </code></pre>
1762  *
1763  * @param {String} interval   A valid date interval enum value
1764  * @param {Number} value      The amount to add to the current date
1765  * @return {Date} The new Date instance
1766  */
1767 Date.prototype.add = function(interval, value){
1768   var d = this.clone();
1769   if (!interval || value === 0) { return d; }
1770   switch(interval.toLowerCase()){
1771     case Date.MILLI:
1772       d.setMilliseconds(this.getMilliseconds() + value);
1773       break;
1774     case Date.SECOND:
1775       d.setSeconds(this.getSeconds() + value);
1776       break;
1777     case Date.MINUTE:
1778       d.setMinutes(this.getMinutes() + value);
1779       break;
1780     case Date.HOUR:
1781       d.setHours(this.getHours() + value);
1782       break;
1783     case Date.DAY:
1784       d.setDate(this.getDate() + value);
1785       break;
1786     case Date.MONTH:
1787       var day = this.getDate();
1788       if(day > 28){
1789           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1790       }
1791       d.setDate(day);
1792       d.setMonth(this.getMonth() + value);
1793       break;
1794     case Date.YEAR:
1795       d.setFullYear(this.getFullYear() + value);
1796       break;
1797   }
1798   return d;
1799 };
1800 /*
1801  * Based on:
1802  * Ext JS Library 1.1.1
1803  * Copyright(c) 2006-2007, Ext JS, LLC.
1804  *
1805  * Originally Released Under LGPL - original licence link has changed is not relivant.
1806  *
1807  * Fork - LGPL
1808  * <script type="text/javascript">
1809  */
1810
1811 /**
1812  * @class Roo.lib.Dom
1813  * @static
1814  * 
1815  * Dom utils (from YIU afaik)
1816  * 
1817  **/
1818 Roo.lib.Dom = {
1819     /**
1820      * Get the view width
1821      * @param {Boolean} full True will get the full document, otherwise it's the view width
1822      * @return {Number} The width
1823      */
1824      
1825     getViewWidth : function(full) {
1826         return full ? this.getDocumentWidth() : this.getViewportWidth();
1827     },
1828     /**
1829      * Get the view height
1830      * @param {Boolean} full True will get the full document, otherwise it's the view height
1831      * @return {Number} The height
1832      */
1833     getViewHeight : function(full) {
1834         return full ? this.getDocumentHeight() : this.getViewportHeight();
1835     },
1836
1837     getDocumentHeight: function() {
1838         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1839         return Math.max(scrollHeight, this.getViewportHeight());
1840     },
1841
1842     getDocumentWidth: function() {
1843         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1844         return Math.max(scrollWidth, this.getViewportWidth());
1845     },
1846
1847     getViewportHeight: function() {
1848         var height = self.innerHeight;
1849         var mode = document.compatMode;
1850
1851         if ((mode || Roo.isIE) && !Roo.isOpera) {
1852             height = (mode == "CSS1Compat") ?
1853                      document.documentElement.clientHeight :
1854                      document.body.clientHeight;
1855         }
1856
1857         return height;
1858     },
1859
1860     getViewportWidth: function() {
1861         var width = self.innerWidth;
1862         var mode = document.compatMode;
1863
1864         if (mode || Roo.isIE) {
1865             width = (mode == "CSS1Compat") ?
1866                     document.documentElement.clientWidth :
1867                     document.body.clientWidth;
1868         }
1869         return width;
1870     },
1871
1872     isAncestor : function(p, c) {
1873         p = Roo.getDom(p);
1874         c = Roo.getDom(c);
1875         if (!p || !c) {
1876             return false;
1877         }
1878
1879         if (p.contains && !Roo.isSafari) {
1880             return p.contains(c);
1881         } else if (p.compareDocumentPosition) {
1882             return !!(p.compareDocumentPosition(c) & 16);
1883         } else {
1884             var parent = c.parentNode;
1885             while (parent) {
1886                 if (parent == p) {
1887                     return true;
1888                 }
1889                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1890                     return false;
1891                 }
1892                 parent = parent.parentNode;
1893             }
1894             return false;
1895         }
1896     },
1897
1898     getRegion : function(el) {
1899         return Roo.lib.Region.getRegion(el);
1900     },
1901
1902     getY : function(el) {
1903         return this.getXY(el)[1];
1904     },
1905
1906     getX : function(el) {
1907         return this.getXY(el)[0];
1908     },
1909
1910     getXY : function(el) {
1911         var p, pe, b, scroll, bd = document.body;
1912         el = Roo.getDom(el);
1913         var fly = Roo.lib.AnimBase.fly;
1914         if (el.getBoundingClientRect) {
1915             b = el.getBoundingClientRect();
1916             scroll = fly(document).getScroll();
1917             return [b.left + scroll.left, b.top + scroll.top];
1918         }
1919         var x = 0, y = 0;
1920
1921         p = el;
1922
1923         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1924
1925         while (p) {
1926
1927             x += p.offsetLeft;
1928             y += p.offsetTop;
1929
1930             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1931                 hasAbsolute = true;
1932             }
1933
1934             if (Roo.isGecko) {
1935                 pe = fly(p);
1936
1937                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1938                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1939
1940
1941                 x += bl;
1942                 y += bt;
1943
1944
1945                 if (p != el && pe.getStyle('overflow') != 'visible') {
1946                     x += bl;
1947                     y += bt;
1948                 }
1949             }
1950             p = p.offsetParent;
1951         }
1952
1953         if (Roo.isSafari && hasAbsolute) {
1954             x -= bd.offsetLeft;
1955             y -= bd.offsetTop;
1956         }
1957
1958         if (Roo.isGecko && !hasAbsolute) {
1959             var dbd = fly(bd);
1960             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1961             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1962         }
1963
1964         p = el.parentNode;
1965         while (p && p != bd) {
1966             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1967                 x -= p.scrollLeft;
1968                 y -= p.scrollTop;
1969             }
1970             p = p.parentNode;
1971         }
1972         return [x, y];
1973     },
1974  
1975   
1976
1977
1978     setXY : function(el, xy) {
1979         el = Roo.fly(el, '_setXY');
1980         el.position();
1981         var pts = el.translatePoints(xy);
1982         if (xy[0] !== false) {
1983             el.dom.style.left = pts.left + "px";
1984         }
1985         if (xy[1] !== false) {
1986             el.dom.style.top = pts.top + "px";
1987         }
1988     },
1989
1990     setX : function(el, x) {
1991         this.setXY(el, [x, false]);
1992     },
1993
1994     setY : function(el, y) {
1995         this.setXY(el, [false, y]);
1996     }
1997 };
1998 /*
1999  * Portions of this file are based on pieces of Yahoo User Interface Library
2000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2001  * YUI licensed under the BSD License:
2002  * http://developer.yahoo.net/yui/license.txt
2003  * <script type="text/javascript">
2004  *
2005  */
2006
2007 Roo.lib.Event = function() {
2008     var loadComplete = false;
2009     var listeners = [];
2010     var unloadListeners = [];
2011     var retryCount = 0;
2012     var onAvailStack = [];
2013     var counter = 0;
2014     var lastError = null;
2015
2016     return {
2017         POLL_RETRYS: 200,
2018         POLL_INTERVAL: 20,
2019         EL: 0,
2020         TYPE: 1,
2021         FN: 2,
2022         WFN: 3,
2023         OBJ: 3,
2024         ADJ_SCOPE: 4,
2025         _interval: null,
2026
2027         startInterval: function() {
2028             if (!this._interval) {
2029                 var self = this;
2030                 var callback = function() {
2031                     self._tryPreloadAttach();
2032                 };
2033                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2034
2035             }
2036         },
2037
2038         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2039             onAvailStack.push({ id:         p_id,
2040                 fn:         p_fn,
2041                 obj:        p_obj,
2042                 override:   p_override,
2043                 checkReady: false    });
2044
2045             retryCount = this.POLL_RETRYS;
2046             this.startInterval();
2047         },
2048
2049
2050         addListener: function(el, eventName, fn) {
2051             el = Roo.getDom(el);
2052             if (!el || !fn) {
2053                 return false;
2054             }
2055
2056             if ("unload" == eventName) {
2057                 unloadListeners[unloadListeners.length] =
2058                 [el, eventName, fn];
2059                 return true;
2060             }
2061
2062             var wrappedFn = function(e) {
2063                 return fn(Roo.lib.Event.getEvent(e));
2064             };
2065
2066             var li = [el, eventName, fn, wrappedFn];
2067
2068             var index = listeners.length;
2069             listeners[index] = li;
2070
2071             this.doAdd(el, eventName, wrappedFn, false);
2072             return true;
2073
2074         },
2075
2076
2077         removeListener: function(el, eventName, fn) {
2078             var i, len;
2079
2080             el = Roo.getDom(el);
2081
2082             if(!fn) {
2083                 return this.purgeElement(el, false, eventName);
2084             }
2085
2086
2087             if ("unload" == eventName) {
2088
2089                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2090                     var li = unloadListeners[i];
2091                     if (li &&
2092                         li[0] == el &&
2093                         li[1] == eventName &&
2094                         li[2] == fn) {
2095                         unloadListeners.splice(i, 1);
2096                         return true;
2097                     }
2098                 }
2099
2100                 return false;
2101             }
2102
2103             var cacheItem = null;
2104
2105
2106             var index = arguments[3];
2107
2108             if ("undefined" == typeof index) {
2109                 index = this._getCacheIndex(el, eventName, fn);
2110             }
2111
2112             if (index >= 0) {
2113                 cacheItem = listeners[index];
2114             }
2115
2116             if (!el || !cacheItem) {
2117                 return false;
2118             }
2119
2120             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2121
2122             delete listeners[index][this.WFN];
2123             delete listeners[index][this.FN];
2124             listeners.splice(index, 1);
2125
2126             return true;
2127
2128         },
2129
2130
2131         getTarget: function(ev, resolveTextNode) {
2132             ev = ev.browserEvent || ev;
2133             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2134             var t = ev.target || ev.srcElement;
2135             return this.resolveTextNode(t);
2136         },
2137
2138
2139         resolveTextNode: function(node) {
2140             if (Roo.isSafari && node && 3 == node.nodeType) {
2141                 return node.parentNode;
2142             } else {
2143                 return node;
2144             }
2145         },
2146
2147
2148         getPageX: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2151             var x = ev.pageX;
2152             if (!x && 0 !== x) {
2153                 x = ev.clientX || 0;
2154
2155                 if (Roo.isIE) {
2156                     x += this.getScroll()[1];
2157                 }
2158             }
2159
2160             return x;
2161         },
2162
2163
2164         getPageY: function(ev) {
2165             ev = ev.browserEvent || ev;
2166             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2167             var y = ev.pageY;
2168             if (!y && 0 !== y) {
2169                 y = ev.clientY || 0;
2170
2171                 if (Roo.isIE) {
2172                     y += this.getScroll()[0];
2173                 }
2174             }
2175
2176
2177             return y;
2178         },
2179
2180
2181         getXY: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             return [this.getPageX(ev), this.getPageY(ev)];
2185         },
2186
2187
2188         getRelatedTarget: function(ev) {
2189             ev = ev.browserEvent || ev;
2190             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2191             var t = ev.relatedTarget;
2192             if (!t) {
2193                 if (ev.type == "mouseout") {
2194                     t = ev.toElement;
2195                 } else if (ev.type == "mouseover") {
2196                     t = ev.fromElement;
2197                 }
2198             }
2199
2200             return this.resolveTextNode(t);
2201         },
2202
2203
2204         getTime: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2207             if (!ev.time) {
2208                 var t = new Date().getTime();
2209                 try {
2210                     ev.time = t;
2211                 } catch(ex) {
2212                     this.lastError = ex;
2213                     return t;
2214                 }
2215             }
2216
2217             return ev.time;
2218         },
2219
2220
2221         stopEvent: function(ev) {
2222             this.stopPropagation(ev);
2223             this.preventDefault(ev);
2224         },
2225
2226
2227         stopPropagation: function(ev) {
2228             ev = ev.browserEvent || ev;
2229             if (ev.stopPropagation) {
2230                 ev.stopPropagation();
2231             } else {
2232                 ev.cancelBubble = true;
2233             }
2234         },
2235
2236
2237         preventDefault: function(ev) {
2238             ev = ev.browserEvent || ev;
2239             if(ev.preventDefault) {
2240                 ev.preventDefault();
2241             } else {
2242                 ev.returnValue = false;
2243             }
2244         },
2245
2246
2247         getEvent: function(e) {
2248             var ev = e || window.event;
2249             if (!ev) {
2250                 var c = this.getEvent.caller;
2251                 while (c) {
2252                     ev = c.arguments[0];
2253                     if (ev && Event == ev.constructor) {
2254                         break;
2255                     }
2256                     c = c.caller;
2257                 }
2258             }
2259             return ev;
2260         },
2261
2262
2263         getCharCode: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             return ev.charCode || ev.keyCode || 0;
2266         },
2267
2268
2269         _getCacheIndex: function(el, eventName, fn) {
2270             for (var i = 0,len = listeners.length; i < len; ++i) {
2271                 var li = listeners[i];
2272                 if (li &&
2273                     li[this.FN] == fn &&
2274                     li[this.EL] == el &&
2275                     li[this.TYPE] == eventName) {
2276                     return i;
2277                 }
2278             }
2279
2280             return -1;
2281         },
2282
2283
2284         elCache: {},
2285
2286
2287         getEl: function(id) {
2288             return document.getElementById(id);
2289         },
2290
2291
2292         clearCache: function() {
2293         },
2294
2295
2296         _load: function(e) {
2297             loadComplete = true;
2298             var EU = Roo.lib.Event;
2299
2300
2301             if (Roo.isIE) {
2302                 EU.doRemove(window, "load", EU._load);
2303             }
2304         },
2305
2306
2307         _tryPreloadAttach: function() {
2308
2309             if (this.locked) {
2310                 return false;
2311             }
2312
2313             this.locked = true;
2314
2315
2316             var tryAgain = !loadComplete;
2317             if (!tryAgain) {
2318                 tryAgain = (retryCount > 0);
2319             }
2320
2321
2322             var notAvail = [];
2323             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2324                 var item = onAvailStack[i];
2325                 if (item) {
2326                     var el = this.getEl(item.id);
2327
2328                     if (el) {
2329                         if (!item.checkReady ||
2330                             loadComplete ||
2331                             el.nextSibling ||
2332                             (document && document.body)) {
2333
2334                             var scope = el;
2335                             if (item.override) {
2336                                 if (item.override === true) {
2337                                     scope = item.obj;
2338                                 } else {
2339                                     scope = item.override;
2340                                 }
2341                             }
2342                             item.fn.call(scope, item.obj);
2343                             onAvailStack[i] = null;
2344                         }
2345                     } else {
2346                         notAvail.push(item);
2347                     }
2348                 }
2349             }
2350
2351             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2352
2353             if (tryAgain) {
2354
2355                 this.startInterval();
2356             } else {
2357                 clearInterval(this._interval);
2358                 this._interval = null;
2359             }
2360
2361             this.locked = false;
2362
2363             return true;
2364
2365         },
2366
2367
2368         purgeElement: function(el, recurse, eventName) {
2369             var elListeners = this.getListeners(el, eventName);
2370             if (elListeners) {
2371                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2372                     var l = elListeners[i];
2373                     this.removeListener(el, l.type, l.fn);
2374                 }
2375             }
2376
2377             if (recurse && el && el.childNodes) {
2378                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2379                     this.purgeElement(el.childNodes[i], recurse, eventName);
2380                 }
2381             }
2382         },
2383
2384
2385         getListeners: function(el, eventName) {
2386             var results = [], searchLists;
2387             if (!eventName) {
2388                 searchLists = [listeners, unloadListeners];
2389             } else if (eventName == "unload") {
2390                 searchLists = [unloadListeners];
2391             } else {
2392                 searchLists = [listeners];
2393             }
2394
2395             for (var j = 0; j < searchLists.length; ++j) {
2396                 var searchList = searchLists[j];
2397                 if (searchList && searchList.length > 0) {
2398                     for (var i = 0,len = searchList.length; i < len; ++i) {
2399                         var l = searchList[i];
2400                         if (l && l[this.EL] === el &&
2401                             (!eventName || eventName === l[this.TYPE])) {
2402                             results.push({
2403                                 type:   l[this.TYPE],
2404                                 fn:     l[this.FN],
2405                                 obj:    l[this.OBJ],
2406                                 adjust: l[this.ADJ_SCOPE],
2407                                 index:  i
2408                             });
2409                         }
2410                     }
2411                 }
2412             }
2413
2414             return (results.length) ? results : null;
2415         },
2416
2417
2418         _unload: function(e) {
2419
2420             var EU = Roo.lib.Event, i, j, l, len, index;
2421
2422             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2423                 l = unloadListeners[i];
2424                 if (l) {
2425                     var scope = window;
2426                     if (l[EU.ADJ_SCOPE]) {
2427                         if (l[EU.ADJ_SCOPE] === true) {
2428                             scope = l[EU.OBJ];
2429                         } else {
2430                             scope = l[EU.ADJ_SCOPE];
2431                         }
2432                     }
2433                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2434                     unloadListeners[i] = null;
2435                     l = null;
2436                     scope = null;
2437                 }
2438             }
2439
2440             unloadListeners = null;
2441
2442             if (listeners && listeners.length > 0) {
2443                 j = listeners.length;
2444                 while (j) {
2445                     index = j - 1;
2446                     l = listeners[index];
2447                     if (l) {
2448                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2449                                 l[EU.FN], index);
2450                     }
2451                     j = j - 1;
2452                 }
2453                 l = null;
2454
2455                 EU.clearCache();
2456             }
2457
2458             EU.doRemove(window, "unload", EU._unload);
2459
2460         },
2461
2462
2463         getScroll: function() {
2464             var dd = document.documentElement, db = document.body;
2465             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2466                 return [dd.scrollTop, dd.scrollLeft];
2467             } else if (db) {
2468                 return [db.scrollTop, db.scrollLeft];
2469             } else {
2470                 return [0, 0];
2471             }
2472         },
2473
2474
2475         doAdd: function () {
2476             if (window.addEventListener) {
2477                 return function(el, eventName, fn, capture) {
2478                     el.addEventListener(eventName, fn, (capture));
2479                 };
2480             } else if (window.attachEvent) {
2481                 return function(el, eventName, fn, capture) {
2482                     el.attachEvent("on" + eventName, fn);
2483                 };
2484             } else {
2485                 return function() {
2486                 };
2487             }
2488         }(),
2489
2490
2491         doRemove: function() {
2492             if (window.removeEventListener) {
2493                 return function (el, eventName, fn, capture) {
2494                     el.removeEventListener(eventName, fn, (capture));
2495                 };
2496             } else if (window.detachEvent) {
2497                 return function (el, eventName, fn) {
2498                     el.detachEvent("on" + eventName, fn);
2499                 };
2500             } else {
2501                 return function() {
2502                 };
2503             }
2504         }()
2505     };
2506     
2507 }();
2508 (function() {     
2509    
2510     var E = Roo.lib.Event;
2511     E.on = E.addListener;
2512     E.un = E.removeListener;
2513
2514     if (document && document.body) {
2515         E._load();
2516     } else {
2517         E.doAdd(window, "load", E._load);
2518     }
2519     E.doAdd(window, "unload", E._unload);
2520     E._tryPreloadAttach();
2521 })();
2522
2523 /*
2524  * Portions of this file are based on pieces of Yahoo User Interface Library
2525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2526  * YUI licensed under the BSD License:
2527  * http://developer.yahoo.net/yui/license.txt
2528  * <script type="text/javascript">
2529  *
2530  */
2531
2532 (function() {
2533     /**
2534      * @class Roo.lib.Ajax
2535      *
2536      */
2537     Roo.lib.Ajax = {
2538         /**
2539          * @static 
2540          */
2541         request : function(method, uri, cb, data, options) {
2542             if(options){
2543                 var hs = options.headers;
2544                 if(hs){
2545                     for(var h in hs){
2546                         if(hs.hasOwnProperty(h)){
2547                             this.initHeader(h, hs[h], false);
2548                         }
2549                     }
2550                 }
2551                 if(options.xmlData){
2552                     this.initHeader('Content-Type', 'text/xml', false);
2553                     method = 'POST';
2554                     data = options.xmlData;
2555                 }
2556             }
2557
2558             return this.asyncRequest(method, uri, cb, data);
2559         },
2560
2561         serializeForm : function(form) {
2562             if(typeof form == 'string') {
2563                 form = (document.getElementById(form) || document.forms[form]);
2564             }
2565
2566             var el, name, val, disabled, data = '', hasSubmit = false;
2567             for (var i = 0; i < form.elements.length; i++) {
2568                 el = form.elements[i];
2569                 disabled = form.elements[i].disabled;
2570                 name = form.elements[i].name;
2571                 val = form.elements[i].value;
2572
2573                 if (!disabled && name){
2574                     switch (el.type)
2575                             {
2576                         case 'select-one':
2577                         case 'select-multiple':
2578                             for (var j = 0; j < el.options.length; j++) {
2579                                 if (el.options[j].selected) {
2580                                     if (Roo.isIE) {
2581                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2582                                     }
2583                                     else {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                 }
2587                             }
2588                             break;
2589                         case 'radio':
2590                         case 'checkbox':
2591                             if (el.checked) {
2592                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2593                             }
2594                             break;
2595                         case 'file':
2596
2597                         case undefined:
2598
2599                         case 'reset':
2600
2601                         case 'button':
2602
2603                             break;
2604                         case 'submit':
2605                             if(hasSubmit == false) {
2606                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2607                                 hasSubmit = true;
2608                             }
2609                             break;
2610                         default:
2611                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2612                             break;
2613                     }
2614                 }
2615             }
2616             data = data.substr(0, data.length - 1);
2617             return data;
2618         },
2619
2620         headers:{},
2621
2622         hasHeaders:false,
2623
2624         useDefaultHeader:true,
2625
2626         defaultPostHeader:'application/x-www-form-urlencoded',
2627
2628         useDefaultXhrHeader:true,
2629
2630         defaultXhrHeader:'XMLHttpRequest',
2631
2632         hasDefaultHeaders:true,
2633
2634         defaultHeaders:{},
2635
2636         poll:{},
2637
2638         timeout:{},
2639
2640         pollInterval:50,
2641
2642         transactionId:0,
2643
2644         setProgId:function(id)
2645         {
2646             this.activeX.unshift(id);
2647         },
2648
2649         setDefaultPostHeader:function(b)
2650         {
2651             this.useDefaultHeader = b;
2652         },
2653
2654         setDefaultXhrHeader:function(b)
2655         {
2656             this.useDefaultXhrHeader = b;
2657         },
2658
2659         setPollingInterval:function(i)
2660         {
2661             if (typeof i == 'number' && isFinite(i)) {
2662                 this.pollInterval = i;
2663             }
2664         },
2665
2666         createXhrObject:function(transactionId)
2667         {
2668             var obj,http;
2669             try
2670             {
2671
2672                 http = new XMLHttpRequest();
2673
2674                 obj = { conn:http, tId:transactionId };
2675             }
2676             catch(e)
2677             {
2678                 for (var i = 0; i < this.activeX.length; ++i) {
2679                     try
2680                     {
2681
2682                         http = new ActiveXObject(this.activeX[i]);
2683
2684                         obj = { conn:http, tId:transactionId };
2685                         break;
2686                     }
2687                     catch(e) {
2688                     }
2689                 }
2690             }
2691             finally
2692             {
2693                 return obj;
2694             }
2695         },
2696
2697         getConnectionObject:function()
2698         {
2699             var o;
2700             var tId = this.transactionId;
2701
2702             try
2703             {
2704                 o = this.createXhrObject(tId);
2705                 if (o) {
2706                     this.transactionId++;
2707                 }
2708             }
2709             catch(e) {
2710             }
2711             finally
2712             {
2713                 return o;
2714             }
2715         },
2716
2717         asyncRequest:function(method, uri, callback, postData)
2718         {
2719             var o = this.getConnectionObject();
2720
2721             if (!o) {
2722                 return null;
2723             }
2724             else {
2725                 o.conn.open(method, uri, true);
2726
2727                 if (this.useDefaultXhrHeader) {
2728                     if (!this.defaultHeaders['X-Requested-With']) {
2729                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2730                     }
2731                 }
2732
2733                 if(postData && this.useDefaultHeader){
2734                     this.initHeader('Content-Type', this.defaultPostHeader);
2735                 }
2736
2737                  if (this.hasDefaultHeaders || this.hasHeaders) {
2738                     this.setHeader(o);
2739                 }
2740
2741                 this.handleReadyState(o, callback);
2742                 o.conn.send(postData || null);
2743
2744                 return o;
2745             }
2746         },
2747
2748         handleReadyState:function(o, callback)
2749         {
2750             var oConn = this;
2751
2752             if (callback && callback.timeout) {
2753                 
2754                 this.timeout[o.tId] = window.setTimeout(function() {
2755                     oConn.abort(o, callback, true);
2756                 }, callback.timeout);
2757             }
2758
2759             this.poll[o.tId] = window.setInterval(
2760                     function() {
2761                         if (o.conn && o.conn.readyState == 4) {
2762                             window.clearInterval(oConn.poll[o.tId]);
2763                             delete oConn.poll[o.tId];
2764
2765                             if(callback && callback.timeout) {
2766                                 window.clearTimeout(oConn.timeout[o.tId]);
2767                                 delete oConn.timeout[o.tId];
2768                             }
2769
2770                             oConn.handleTransactionResponse(o, callback);
2771                         }
2772                     }
2773                     , this.pollInterval);
2774         },
2775
2776         handleTransactionResponse:function(o, callback, isAbort)
2777         {
2778
2779             if (!callback) {
2780                 this.releaseObject(o);
2781                 return;
2782             }
2783
2784             var httpStatus, responseObject;
2785
2786             try
2787             {
2788                 if (o.conn.status !== undefined && o.conn.status != 0) {
2789                     httpStatus = o.conn.status;
2790                 }
2791                 else {
2792                     httpStatus = 13030;
2793                 }
2794             }
2795             catch(e) {
2796
2797
2798                 httpStatus = 13030;
2799             }
2800
2801             if (httpStatus >= 200 && httpStatus < 300) {
2802                 responseObject = this.createResponseObject(o, callback.argument);
2803                 if (callback.success) {
2804                     if (!callback.scope) {
2805                         callback.success(responseObject);
2806                     }
2807                     else {
2808
2809
2810                         callback.success.apply(callback.scope, [responseObject]);
2811                     }
2812                 }
2813             }
2814             else {
2815                 switch (httpStatus) {
2816
2817                     case 12002:
2818                     case 12029:
2819                     case 12030:
2820                     case 12031:
2821                     case 12152:
2822                     case 13030:
2823                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2824                         if (callback.failure) {
2825                             if (!callback.scope) {
2826                                 callback.failure(responseObject);
2827                             }
2828                             else {
2829                                 callback.failure.apply(callback.scope, [responseObject]);
2830                             }
2831                         }
2832                         break;
2833                     default:
2834                         responseObject = this.createResponseObject(o, callback.argument);
2835                         if (callback.failure) {
2836                             if (!callback.scope) {
2837                                 callback.failure(responseObject);
2838                             }
2839                             else {
2840                                 callback.failure.apply(callback.scope, [responseObject]);
2841                             }
2842                         }
2843                 }
2844             }
2845
2846             this.releaseObject(o);
2847             responseObject = null;
2848         },
2849
2850         createResponseObject:function(o, callbackArg)
2851         {
2852             var obj = {};
2853             var headerObj = {};
2854
2855             try
2856             {
2857                 var headerStr = o.conn.getAllResponseHeaders();
2858                 var header = headerStr.split('\n');
2859                 for (var i = 0; i < header.length; i++) {
2860                     var delimitPos = header[i].indexOf(':');
2861                     if (delimitPos != -1) {
2862                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2863                     }
2864                 }
2865             }
2866             catch(e) {
2867             }
2868
2869             obj.tId = o.tId;
2870             obj.status = o.conn.status;
2871             obj.statusText = o.conn.statusText;
2872             obj.getResponseHeader = headerObj;
2873             obj.getAllResponseHeaders = headerStr;
2874             obj.responseText = o.conn.responseText;
2875             obj.responseXML = o.conn.responseXML;
2876
2877             if (typeof callbackArg !== undefined) {
2878                 obj.argument = callbackArg;
2879             }
2880
2881             return obj;
2882         },
2883
2884         createExceptionObject:function(tId, callbackArg, isAbort)
2885         {
2886             var COMM_CODE = 0;
2887             var COMM_ERROR = 'communication failure';
2888             var ABORT_CODE = -1;
2889             var ABORT_ERROR = 'transaction aborted';
2890
2891             var obj = {};
2892
2893             obj.tId = tId;
2894             if (isAbort) {
2895                 obj.status = ABORT_CODE;
2896                 obj.statusText = ABORT_ERROR;
2897             }
2898             else {
2899                 obj.status = COMM_CODE;
2900                 obj.statusText = COMM_ERROR;
2901             }
2902
2903             if (callbackArg) {
2904                 obj.argument = callbackArg;
2905             }
2906
2907             return obj;
2908         },
2909
2910         initHeader:function(label, value, isDefault)
2911         {
2912             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2913
2914             if (headerObj[label] === undefined) {
2915                 headerObj[label] = value;
2916             }
2917             else {
2918
2919
2920                 headerObj[label] = value + "," + headerObj[label];
2921             }
2922
2923             if (isDefault) {
2924                 this.hasDefaultHeaders = true;
2925             }
2926             else {
2927                 this.hasHeaders = true;
2928             }
2929         },
2930
2931
2932         setHeader:function(o)
2933         {
2934             if (this.hasDefaultHeaders) {
2935                 for (var prop in this.defaultHeaders) {
2936                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2937                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2938                     }
2939                 }
2940             }
2941
2942             if (this.hasHeaders) {
2943                 for (var prop in this.headers) {
2944                     if (this.headers.hasOwnProperty(prop)) {
2945                         o.conn.setRequestHeader(prop, this.headers[prop]);
2946                     }
2947                 }
2948                 this.headers = {};
2949                 this.hasHeaders = false;
2950             }
2951         },
2952
2953         resetDefaultHeaders:function() {
2954             delete this.defaultHeaders;
2955             this.defaultHeaders = {};
2956             this.hasDefaultHeaders = false;
2957         },
2958
2959         abort:function(o, callback, isTimeout)
2960         {
2961             if(this.isCallInProgress(o)) {
2962                 o.conn.abort();
2963                 window.clearInterval(this.poll[o.tId]);
2964                 delete this.poll[o.tId];
2965                 if (isTimeout) {
2966                     delete this.timeout[o.tId];
2967                 }
2968
2969                 this.handleTransactionResponse(o, callback, true);
2970
2971                 return true;
2972             }
2973             else {
2974                 return false;
2975             }
2976         },
2977
2978
2979         isCallInProgress:function(o)
2980         {
2981             if (o && o.conn) {
2982                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2983             }
2984             else {
2985
2986                 return false;
2987             }
2988         },
2989
2990
2991         releaseObject:function(o)
2992         {
2993
2994             o.conn = null;
2995
2996             o = null;
2997         },
2998
2999         activeX:[
3000         'MSXML2.XMLHTTP.3.0',
3001         'MSXML2.XMLHTTP',
3002         'Microsoft.XMLHTTP'
3003         ]
3004
3005
3006     };
3007 })();/*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015
3016 Roo.lib.Region = function(t, r, b, l) {
3017     this.top = t;
3018     this[1] = t;
3019     this.right = r;
3020     this.bottom = b;
3021     this.left = l;
3022     this[0] = l;
3023 };
3024
3025
3026 Roo.lib.Region.prototype = {
3027     contains : function(region) {
3028         return ( region.left >= this.left &&
3029                  region.right <= this.right &&
3030                  region.top >= this.top &&
3031                  region.bottom <= this.bottom    );
3032
3033     },
3034
3035     getArea : function() {
3036         return ( (this.bottom - this.top) * (this.right - this.left) );
3037     },
3038
3039     intersect : function(region) {
3040         var t = Math.max(this.top, region.top);
3041         var r = Math.min(this.right, region.right);
3042         var b = Math.min(this.bottom, region.bottom);
3043         var l = Math.max(this.left, region.left);
3044
3045         if (b >= t && r >= l) {
3046             return new Roo.lib.Region(t, r, b, l);
3047         } else {
3048             return null;
3049         }
3050     },
3051     union : function(region) {
3052         var t = Math.min(this.top, region.top);
3053         var r = Math.max(this.right, region.right);
3054         var b = Math.max(this.bottom, region.bottom);
3055         var l = Math.min(this.left, region.left);
3056
3057         return new Roo.lib.Region(t, r, b, l);
3058     },
3059
3060     adjust : function(t, l, b, r) {
3061         this.top += t;
3062         this.left += l;
3063         this.right += r;
3064         this.bottom += b;
3065         return this;
3066     }
3067 };
3068
3069 Roo.lib.Region.getRegion = function(el) {
3070     var p = Roo.lib.Dom.getXY(el);
3071
3072     var t = p[1];
3073     var r = p[0] + el.offsetWidth;
3074     var b = p[1] + el.offsetHeight;
3075     var l = p[0];
3076
3077     return new Roo.lib.Region(t, r, b, l);
3078 };
3079 /*
3080  * Portions of this file are based on pieces of Yahoo User Interface Library
3081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3082  * YUI licensed under the BSD License:
3083  * http://developer.yahoo.net/yui/license.txt
3084  * <script type="text/javascript">
3085  *
3086  */
3087 //@@dep Roo.lib.Region
3088
3089
3090 Roo.lib.Point = function(x, y) {
3091     if (x instanceof Array) {
3092         y = x[1];
3093         x = x[0];
3094     }
3095     this.x = this.right = this.left = this[0] = x;
3096     this.y = this.top = this.bottom = this[1] = y;
3097 };
3098
3099 Roo.lib.Point.prototype = new Roo.lib.Region();
3100 /*
3101  * Portions of this file are based on pieces of Yahoo User Interface Library
3102  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3103  * YUI licensed under the BSD License:
3104  * http://developer.yahoo.net/yui/license.txt
3105  * <script type="text/javascript">
3106  *
3107  */
3108  
3109 (function() {   
3110
3111     Roo.lib.Anim = {
3112         scroll : function(el, args, duration, easing, cb, scope) {
3113             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3114         },
3115
3116         motion : function(el, args, duration, easing, cb, scope) {
3117             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3118         },
3119
3120         color : function(el, args, duration, easing, cb, scope) {
3121             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3122         },
3123
3124         run : function(el, args, duration, easing, cb, scope, type) {
3125             type = type || Roo.lib.AnimBase;
3126             if (typeof easing == "string") {
3127                 easing = Roo.lib.Easing[easing];
3128             }
3129             var anim = new type(el, args, duration, easing);
3130             anim.animateX(function() {
3131                 Roo.callback(cb, scope);
3132             });
3133             return anim;
3134         }
3135     };
3136 })();/*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144
3145 (function() {    
3146     var libFlyweight;
3147     
3148     function fly(el) {
3149         if (!libFlyweight) {
3150             libFlyweight = new Roo.Element.Flyweight();
3151         }
3152         libFlyweight.dom = el;
3153         return libFlyweight;
3154     }
3155
3156     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3157     
3158    
3159     
3160     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3161         if (el) {
3162             this.init(el, attributes, duration, method);
3163         }
3164     };
3165
3166     Roo.lib.AnimBase.fly = fly;
3167     
3168     
3169     
3170     Roo.lib.AnimBase.prototype = {
3171
3172         toString: function() {
3173             var el = this.getEl();
3174             var id = el.id || el.tagName;
3175             return ("Anim " + id);
3176         },
3177
3178         patterns: {
3179             noNegatives:        /width|height|opacity|padding/i,
3180             offsetAttribute:  /^((width|height)|(top|left))$/,
3181             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3182             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3183         },
3184
3185
3186         doMethod: function(attr, start, end) {
3187             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3188         },
3189
3190
3191         setAttribute: function(attr, val, unit) {
3192             if (this.patterns.noNegatives.test(attr)) {
3193                 val = (val > 0) ? val : 0;
3194             }
3195
3196             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3197         },
3198
3199
3200         getAttribute: function(attr) {
3201             var el = this.getEl();
3202             var val = fly(el).getStyle(attr);
3203
3204             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3205                 return parseFloat(val);
3206             }
3207
3208             var a = this.patterns.offsetAttribute.exec(attr) || [];
3209             var pos = !!( a[3] );
3210             var box = !!( a[2] );
3211
3212
3213             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3214                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3215             } else {
3216                 val = 0;
3217             }
3218
3219             return val;
3220         },
3221
3222
3223         getDefaultUnit: function(attr) {
3224             if (this.patterns.defaultUnit.test(attr)) {
3225                 return 'px';
3226             }
3227
3228             return '';
3229         },
3230
3231         animateX : function(callback, scope) {
3232             var f = function() {
3233                 this.onComplete.removeListener(f);
3234                 if (typeof callback == "function") {
3235                     callback.call(scope || this, this);
3236                 }
3237             };
3238             this.onComplete.addListener(f, this);
3239             this.animate();
3240         },
3241
3242
3243         setRuntimeAttribute: function(attr) {
3244             var start;
3245             var end;
3246             var attributes = this.attributes;
3247
3248             this.runtimeAttributes[attr] = {};
3249
3250             var isset = function(prop) {
3251                 return (typeof prop !== 'undefined');
3252             };
3253
3254             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3255                 return false;
3256             }
3257
3258             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3259
3260
3261             if (isset(attributes[attr]['to'])) {
3262                 end = attributes[attr]['to'];
3263             } else if (isset(attributes[attr]['by'])) {
3264                 if (start.constructor == Array) {
3265                     end = [];
3266                     for (var i = 0, len = start.length; i < len; ++i) {
3267                         end[i] = start[i] + attributes[attr]['by'][i];
3268                     }
3269                 } else {
3270                     end = start + attributes[attr]['by'];
3271                 }
3272             }
3273
3274             this.runtimeAttributes[attr].start = start;
3275             this.runtimeAttributes[attr].end = end;
3276
3277
3278             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3279         },
3280
3281
3282         init: function(el, attributes, duration, method) {
3283
3284             var isAnimated = false;
3285
3286
3287             var startTime = null;
3288
3289
3290             var actualFrames = 0;
3291
3292
3293             el = Roo.getDom(el);
3294
3295
3296             this.attributes = attributes || {};
3297
3298
3299             this.duration = duration || 1;
3300
3301
3302             this.method = method || Roo.lib.Easing.easeNone;
3303
3304
3305             this.useSeconds = true;
3306
3307
3308             this.currentFrame = 0;
3309
3310
3311             this.totalFrames = Roo.lib.AnimMgr.fps;
3312
3313
3314             this.getEl = function() {
3315                 return el;
3316             };
3317
3318
3319             this.isAnimated = function() {
3320                 return isAnimated;
3321             };
3322
3323
3324             this.getStartTime = function() {
3325                 return startTime;
3326             };
3327
3328             this.runtimeAttributes = {};
3329
3330
3331             this.animate = function() {
3332                 if (this.isAnimated()) {
3333                     return false;
3334                 }
3335
3336                 this.currentFrame = 0;
3337
3338                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3339
3340                 Roo.lib.AnimMgr.registerElement(this);
3341             };
3342
3343
3344             this.stop = function(finish) {
3345                 if (finish) {
3346                     this.currentFrame = this.totalFrames;
3347                     this._onTween.fire();
3348                 }
3349                 Roo.lib.AnimMgr.stop(this);
3350             };
3351
3352             var onStart = function() {
3353                 this.onStart.fire();
3354
3355                 this.runtimeAttributes = {};
3356                 for (var attr in this.attributes) {
3357                     this.setRuntimeAttribute(attr);
3358                 }
3359
3360                 isAnimated = true;
3361                 actualFrames = 0;
3362                 startTime = new Date();
3363             };
3364
3365
3366             var onTween = function() {
3367                 var data = {
3368                     duration: new Date() - this.getStartTime(),
3369                     currentFrame: this.currentFrame
3370                 };
3371
3372                 data.toString = function() {
3373                     return (
3374                             'duration: ' + data.duration +
3375                             ', currentFrame: ' + data.currentFrame
3376                             );
3377                 };
3378
3379                 this.onTween.fire(data);
3380
3381                 var runtimeAttributes = this.runtimeAttributes;
3382
3383                 for (var attr in runtimeAttributes) {
3384                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3385                 }
3386
3387                 actualFrames += 1;
3388             };
3389
3390             var onComplete = function() {
3391                 var actual_duration = (new Date() - startTime) / 1000 ;
3392
3393                 var data = {
3394                     duration: actual_duration,
3395                     frames: actualFrames,
3396                     fps: actualFrames / actual_duration
3397                 };
3398
3399                 data.toString = function() {
3400                     return (
3401                             'duration: ' + data.duration +
3402                             ', frames: ' + data.frames +
3403                             ', fps: ' + data.fps
3404                             );
3405                 };
3406
3407                 isAnimated = false;
3408                 actualFrames = 0;
3409                 this.onComplete.fire(data);
3410             };
3411
3412
3413             this._onStart = new Roo.util.Event(this);
3414             this.onStart = new Roo.util.Event(this);
3415             this.onTween = new Roo.util.Event(this);
3416             this._onTween = new Roo.util.Event(this);
3417             this.onComplete = new Roo.util.Event(this);
3418             this._onComplete = new Roo.util.Event(this);
3419             this._onStart.addListener(onStart);
3420             this._onTween.addListener(onTween);
3421             this._onComplete.addListener(onComplete);
3422         }
3423     };
3424 })();
3425 /*
3426  * Portions of this file are based on pieces of Yahoo User Interface Library
3427  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3428  * YUI licensed under the BSD License:
3429  * http://developer.yahoo.net/yui/license.txt
3430  * <script type="text/javascript">
3431  *
3432  */
3433
3434 Roo.lib.AnimMgr = new function() {
3435
3436     var thread = null;
3437
3438
3439     var queue = [];
3440
3441
3442     var tweenCount = 0;
3443
3444
3445     this.fps = 1000;
3446
3447
3448     this.delay = 1;
3449
3450
3451     this.registerElement = function(tween) {
3452         queue[queue.length] = tween;
3453         tweenCount += 1;
3454         tween._onStart.fire();
3455         this.start();
3456     };
3457
3458
3459     this.unRegister = function(tween, index) {
3460         tween._onComplete.fire();
3461         index = index || getIndex(tween);
3462         if (index != -1) {
3463             queue.splice(index, 1);
3464         }
3465
3466         tweenCount -= 1;
3467         if (tweenCount <= 0) {
3468             this.stop();
3469         }
3470     };
3471
3472
3473     this.start = function() {
3474         if (thread === null) {
3475             thread = setInterval(this.run, this.delay);
3476         }
3477     };
3478
3479
3480     this.stop = function(tween) {
3481         if (!tween) {
3482             clearInterval(thread);
3483
3484             for (var i = 0, len = queue.length; i < len; ++i) {
3485                 if (queue[0].isAnimated()) {
3486                     this.unRegister(queue[0], 0);
3487                 }
3488             }
3489
3490             queue = [];
3491             thread = null;
3492             tweenCount = 0;
3493         }
3494         else {
3495             this.unRegister(tween);
3496         }
3497     };
3498
3499
3500     this.run = function() {
3501         for (var i = 0, len = queue.length; i < len; ++i) {
3502             var tween = queue[i];
3503             if (!tween || !tween.isAnimated()) {
3504                 continue;
3505             }
3506
3507             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3508             {
3509                 tween.currentFrame += 1;
3510
3511                 if (tween.useSeconds) {
3512                     correctFrame(tween);
3513                 }
3514                 tween._onTween.fire();
3515             }
3516             else {
3517                 Roo.lib.AnimMgr.stop(tween, i);
3518             }
3519         }
3520     };
3521
3522     var getIndex = function(anim) {
3523         for (var i = 0, len = queue.length; i < len; ++i) {
3524             if (queue[i] == anim) {
3525                 return i;
3526             }
3527         }
3528         return -1;
3529     };
3530
3531
3532     var correctFrame = function(tween) {
3533         var frames = tween.totalFrames;
3534         var frame = tween.currentFrame;
3535         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3536         var elapsed = (new Date() - tween.getStartTime());
3537         var tweak = 0;
3538
3539         if (elapsed < tween.duration * 1000) {
3540             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3541         } else {
3542             tweak = frames - (frame + 1);
3543         }
3544         if (tweak > 0 && isFinite(tweak)) {
3545             if (tween.currentFrame + tweak >= frames) {
3546                 tweak = frames - (frame + 1);
3547             }
3548
3549             tween.currentFrame += tweak;
3550         }
3551     };
3552 };
3553
3554     /*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 Roo.lib.Bezier = new function() {
3563
3564         this.getPosition = function(points, t) {
3565             var n = points.length;
3566             var tmp = [];
3567
3568             for (var i = 0; i < n; ++i) {
3569                 tmp[i] = [points[i][0], points[i][1]];
3570             }
3571
3572             for (var j = 1; j < n; ++j) {
3573                 for (i = 0; i < n - j; ++i) {
3574                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3575                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3576                 }
3577             }
3578
3579             return [ tmp[0][0], tmp[0][1] ];
3580
3581         };
3582     };/*
3583  * Portions of this file are based on pieces of Yahoo User Interface Library
3584  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3585  * YUI licensed under the BSD License:
3586  * http://developer.yahoo.net/yui/license.txt
3587  * <script type="text/javascript">
3588  *
3589  */
3590 (function() {
3591
3592     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3593         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3594     };
3595
3596     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3597
3598     var fly = Roo.lib.AnimBase.fly;
3599     var Y = Roo.lib;
3600     var superclass = Y.ColorAnim.superclass;
3601     var proto = Y.ColorAnim.prototype;
3602
3603     proto.toString = function() {
3604         var el = this.getEl();
3605         var id = el.id || el.tagName;
3606         return ("ColorAnim " + id);
3607     };
3608
3609     proto.patterns.color = /color$/i;
3610     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3611     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3612     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3613     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3614
3615
3616     proto.parseColor = function(s) {
3617         if (s.length == 3) {
3618             return s;
3619         }
3620
3621         var c = this.patterns.hex.exec(s);
3622         if (c && c.length == 4) {
3623             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3624         }
3625
3626         c = this.patterns.rgb.exec(s);
3627         if (c && c.length == 4) {
3628             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3629         }
3630
3631         c = this.patterns.hex3.exec(s);
3632         if (c && c.length == 4) {
3633             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3634         }
3635
3636         return null;
3637     };
3638     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3639     proto.getAttribute = function(attr) {
3640         var el = this.getEl();
3641         if (this.patterns.color.test(attr)) {
3642             var val = fly(el).getStyle(attr);
3643
3644             if (this.patterns.transparent.test(val)) {
3645                 var parent = el.parentNode;
3646                 val = fly(parent).getStyle(attr);
3647
3648                 while (parent && this.patterns.transparent.test(val)) {
3649                     parent = parent.parentNode;
3650                     val = fly(parent).getStyle(attr);
3651                     if (parent.tagName.toUpperCase() == 'HTML') {
3652                         val = '#fff';
3653                     }
3654                 }
3655             }
3656         } else {
3657             val = superclass.getAttribute.call(this, attr);
3658         }
3659
3660         return val;
3661     };
3662     proto.getAttribute = function(attr) {
3663         var el = this.getEl();
3664         if (this.patterns.color.test(attr)) {
3665             var val = fly(el).getStyle(attr);
3666
3667             if (this.patterns.transparent.test(val)) {
3668                 var parent = el.parentNode;
3669                 val = fly(parent).getStyle(attr);
3670
3671                 while (parent && this.patterns.transparent.test(val)) {
3672                     parent = parent.parentNode;
3673                     val = fly(parent).getStyle(attr);
3674                     if (parent.tagName.toUpperCase() == 'HTML') {
3675                         val = '#fff';
3676                     }
3677                 }
3678             }
3679         } else {
3680             val = superclass.getAttribute.call(this, attr);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.doMethod = function(attr, start, end) {
3687         var val;
3688
3689         if (this.patterns.color.test(attr)) {
3690             val = [];
3691             for (var i = 0, len = start.length; i < len; ++i) {
3692                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3693             }
3694
3695             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3696         }
3697         else {
3698             val = superclass.doMethod.call(this, attr, start, end);
3699         }
3700
3701         return val;
3702     };
3703
3704     proto.setRuntimeAttribute = function(attr) {
3705         superclass.setRuntimeAttribute.call(this, attr);
3706
3707         if (this.patterns.color.test(attr)) {
3708             var attributes = this.attributes;
3709             var start = this.parseColor(this.runtimeAttributes[attr].start);
3710             var end = this.parseColor(this.runtimeAttributes[attr].end);
3711
3712             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3713                 end = this.parseColor(attributes[attr].by);
3714
3715                 for (var i = 0, len = start.length; i < len; ++i) {
3716                     end[i] = start[i] + end[i];
3717                 }
3718             }
3719
3720             this.runtimeAttributes[attr].start = start;
3721             this.runtimeAttributes[attr].end = end;
3722         }
3723     };
3724 })();
3725
3726 /*
3727  * Portions of this file are based on pieces of Yahoo User Interface Library
3728  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3729  * YUI licensed under the BSD License:
3730  * http://developer.yahoo.net/yui/license.txt
3731  * <script type="text/javascript">
3732  *
3733  */
3734 Roo.lib.Easing = {
3735
3736
3737     easeNone: function (t, b, c, d) {
3738         return c * t / d + b;
3739     },
3740
3741
3742     easeIn: function (t, b, c, d) {
3743         return c * (t /= d) * t + b;
3744     },
3745
3746
3747     easeOut: function (t, b, c, d) {
3748         return -c * (t /= d) * (t - 2) + b;
3749     },
3750
3751
3752     easeBoth: function (t, b, c, d) {
3753         if ((t /= d / 2) < 1) {
3754             return c / 2 * t * t + b;
3755         }
3756
3757         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3758     },
3759
3760
3761     easeInStrong: function (t, b, c, d) {
3762         return c * (t /= d) * t * t * t + b;
3763     },
3764
3765
3766     easeOutStrong: function (t, b, c, d) {
3767         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3768     },
3769
3770
3771     easeBothStrong: function (t, b, c, d) {
3772         if ((t /= d / 2) < 1) {
3773             return c / 2 * t * t * t * t + b;
3774         }
3775
3776         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3777     },
3778
3779
3780
3781     elasticIn: function (t, b, c, d, a, p) {
3782         if (t == 0) {
3783             return b;
3784         }
3785         if ((t /= d) == 1) {
3786             return b + c;
3787         }
3788         if (!p) {
3789             p = d * .3;
3790         }
3791
3792         if (!a || a < Math.abs(c)) {
3793             a = c;
3794             var s = p / 4;
3795         }
3796         else {
3797             var s = p / (2 * Math.PI) * Math.asin(c / a);
3798         }
3799
3800         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3801     },
3802
3803
3804     elasticOut: function (t, b, c, d, a, p) {
3805         if (t == 0) {
3806             return b;
3807         }
3808         if ((t /= d) == 1) {
3809             return b + c;
3810         }
3811         if (!p) {
3812             p = d * .3;
3813         }
3814
3815         if (!a || a < Math.abs(c)) {
3816             a = c;
3817             var s = p / 4;
3818         }
3819         else {
3820             var s = p / (2 * Math.PI) * Math.asin(c / a);
3821         }
3822
3823         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3824     },
3825
3826
3827     elasticBoth: function (t, b, c, d, a, p) {
3828         if (t == 0) {
3829             return b;
3830         }
3831
3832         if ((t /= d / 2) == 2) {
3833             return b + c;
3834         }
3835
3836         if (!p) {
3837             p = d * (.3 * 1.5);
3838         }
3839
3840         if (!a || a < Math.abs(c)) {
3841             a = c;
3842             var s = p / 4;
3843         }
3844         else {
3845             var s = p / (2 * Math.PI) * Math.asin(c / a);
3846         }
3847
3848         if (t < 1) {
3849             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3850                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3851         }
3852         return a * Math.pow(2, -10 * (t -= 1)) *
3853                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3854     },
3855
3856
3857
3858     backIn: function (t, b, c, d, s) {
3859         if (typeof s == 'undefined') {
3860             s = 1.70158;
3861         }
3862         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3863     },
3864
3865
3866     backOut: function (t, b, c, d, s) {
3867         if (typeof s == 'undefined') {
3868             s = 1.70158;
3869         }
3870         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3871     },
3872
3873
3874     backBoth: function (t, b, c, d, s) {
3875         if (typeof s == 'undefined') {
3876             s = 1.70158;
3877         }
3878
3879         if ((t /= d / 2 ) < 1) {
3880             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3881         }
3882         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3883     },
3884
3885
3886     bounceIn: function (t, b, c, d) {
3887         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3888     },
3889
3890
3891     bounceOut: function (t, b, c, d) {
3892         if ((t /= d) < (1 / 2.75)) {
3893             return c * (7.5625 * t * t) + b;
3894         } else if (t < (2 / 2.75)) {
3895             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3896         } else if (t < (2.5 / 2.75)) {
3897             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3898         }
3899         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3900     },
3901
3902
3903     bounceBoth: function (t, b, c, d) {
3904         if (t < d / 2) {
3905             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3906         }
3907         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3908     }
3909 };/*
3910  * Portions of this file are based on pieces of Yahoo User Interface Library
3911  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3912  * YUI licensed under the BSD License:
3913  * http://developer.yahoo.net/yui/license.txt
3914  * <script type="text/javascript">
3915  *
3916  */
3917     (function() {
3918         Roo.lib.Motion = function(el, attributes, duration, method) {
3919             if (el) {
3920                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3921             }
3922         };
3923
3924         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3925
3926
3927         var Y = Roo.lib;
3928         var superclass = Y.Motion.superclass;
3929         var proto = Y.Motion.prototype;
3930
3931         proto.toString = function() {
3932             var el = this.getEl();
3933             var id = el.id || el.tagName;
3934             return ("Motion " + id);
3935         };
3936
3937         proto.patterns.points = /^points$/i;
3938
3939         proto.setAttribute = function(attr, val, unit) {
3940             if (this.patterns.points.test(attr)) {
3941                 unit = unit || 'px';
3942                 superclass.setAttribute.call(this, 'left', val[0], unit);
3943                 superclass.setAttribute.call(this, 'top', val[1], unit);
3944             } else {
3945                 superclass.setAttribute.call(this, attr, val, unit);
3946             }
3947         };
3948
3949         proto.getAttribute = function(attr) {
3950             if (this.patterns.points.test(attr)) {
3951                 var val = [
3952                         superclass.getAttribute.call(this, 'left'),
3953                         superclass.getAttribute.call(this, 'top')
3954                         ];
3955             } else {
3956                 val = superclass.getAttribute.call(this, attr);
3957             }
3958
3959             return val;
3960         };
3961
3962         proto.doMethod = function(attr, start, end) {
3963             var val = null;
3964
3965             if (this.patterns.points.test(attr)) {
3966                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3967                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3968             } else {
3969                 val = superclass.doMethod.call(this, attr, start, end);
3970             }
3971             return val;
3972         };
3973
3974         proto.setRuntimeAttribute = function(attr) {
3975             if (this.patterns.points.test(attr)) {
3976                 var el = this.getEl();
3977                 var attributes = this.attributes;
3978                 var start;
3979                 var control = attributes['points']['control'] || [];
3980                 var end;
3981                 var i, len;
3982
3983                 if (control.length > 0 && !(control[0] instanceof Array)) {
3984                     control = [control];
3985                 } else {
3986                     var tmp = [];
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         tmp[i] = control[i];
3989                     }
3990                     control = tmp;
3991                 }
3992
3993                 Roo.fly(el).position();
3994
3995                 if (isset(attributes['points']['from'])) {
3996                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3997                 }
3998                 else {
3999                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4000                 }
4001
4002                 start = this.getAttribute('points');
4003
4004
4005                 if (isset(attributes['points']['to'])) {
4006                     end = translateValues.call(this, attributes['points']['to'], start);
4007
4008                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009                     for (i = 0,len = control.length; i < len; ++i) {
4010                         control[i] = translateValues.call(this, control[i], start);
4011                     }
4012
4013
4014                 } else if (isset(attributes['points']['by'])) {
4015                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4016
4017                     for (i = 0,len = control.length; i < len; ++i) {
4018                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4019                     }
4020                 }
4021
4022                 this.runtimeAttributes[attr] = [start];
4023
4024                 if (control.length > 0) {
4025                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4026                 }
4027
4028                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4029             }
4030             else {
4031                 superclass.setRuntimeAttribute.call(this, attr);
4032             }
4033         };
4034
4035         var translateValues = function(val, start) {
4036             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4037             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4038
4039             return val;
4040         };
4041
4042         var isset = function(prop) {
4043             return (typeof prop !== 'undefined');
4044         };
4045     })();
4046 /*
4047  * Portions of this file are based on pieces of Yahoo User Interface Library
4048  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4049  * YUI licensed under the BSD License:
4050  * http://developer.yahoo.net/yui/license.txt
4051  * <script type="text/javascript">
4052  *
4053  */
4054     (function() {
4055         Roo.lib.Scroll = function(el, attributes, duration, method) {
4056             if (el) {
4057                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4058             }
4059         };
4060
4061         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4062
4063
4064         var Y = Roo.lib;
4065         var superclass = Y.Scroll.superclass;
4066         var proto = Y.Scroll.prototype;
4067
4068         proto.toString = function() {
4069             var el = this.getEl();
4070             var id = el.id || el.tagName;
4071             return ("Scroll " + id);
4072         };
4073
4074         proto.doMethod = function(attr, start, end) {
4075             var val = null;
4076
4077             if (attr == 'scroll') {
4078                 val = [
4079                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4080                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4081                         ];
4082
4083             } else {
4084                 val = superclass.doMethod.call(this, attr, start, end);
4085             }
4086             return val;
4087         };
4088
4089         proto.getAttribute = function(attr) {
4090             var val = null;
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 val = [ el.scrollLeft, el.scrollTop ];
4095             } else {
4096                 val = superclass.getAttribute.call(this, attr);
4097             }
4098
4099             return val;
4100         };
4101
4102         proto.setAttribute = function(attr, val, unit) {
4103             var el = this.getEl();
4104
4105             if (attr == 'scroll') {
4106                 el.scrollLeft = val[0];
4107                 el.scrollTop = val[1];
4108             } else {
4109                 superclass.setAttribute.call(this, attr, val, unit);
4110             }
4111         };
4112     })();
4113 /*
4114  * Based on:
4115  * Ext JS Library 1.1.1
4116  * Copyright(c) 2006-2007, Ext JS, LLC.
4117  *
4118  * Originally Released Under LGPL - original licence link has changed is not relivant.
4119  *
4120  * Fork - LGPL
4121  * <script type="text/javascript">
4122  */
4123
4124
4125 // nasty IE9 hack - what a pile of crap that is..
4126
4127  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4128     Range.prototype.createContextualFragment = function (html) {
4129         var doc = window.document;
4130         var container = doc.createElement("div");
4131         container.innerHTML = html;
4132         var frag = doc.createDocumentFragment(), n;
4133         while ((n = container.firstChild)) {
4134             frag.appendChild(n);
4135         }
4136         return frag;
4137     };
4138 }
4139
4140 /**
4141  * @class Roo.DomHelper
4142  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4143  * 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>.
4144  * @singleton
4145  */
4146 Roo.DomHelper = function(){
4147     var tempTableEl = null;
4148     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4149     var tableRe = /^table|tbody|tr|td$/i;
4150     var xmlns = {};
4151     // build as innerHTML where available
4152     /** @ignore */
4153     var createHtml = function(o){
4154         if(typeof o == 'string'){
4155             return o;
4156         }
4157         var b = "";
4158         if(!o.tag){
4159             o.tag = "div";
4160         }
4161         b += "<" + o.tag;
4162         for(var attr in o){
4163             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4164             if(attr == "style"){
4165                 var s = o["style"];
4166                 if(typeof s == "function"){
4167                     s = s.call();
4168                 }
4169                 if(typeof s == "string"){
4170                     b += ' style="' + s + '"';
4171                 }else if(typeof s == "object"){
4172                     b += ' style="';
4173                     for(var key in s){
4174                         if(typeof s[key] != "function"){
4175                             b += key + ":" + s[key] + ";";
4176                         }
4177                     }
4178                     b += '"';
4179                 }
4180             }else{
4181                 if(attr == "cls"){
4182                     b += ' class="' + o["cls"] + '"';
4183                 }else if(attr == "htmlFor"){
4184                     b += ' for="' + o["htmlFor"] + '"';
4185                 }else{
4186                     b += " " + attr + '="' + o[attr] + '"';
4187                 }
4188             }
4189         }
4190         if(emptyTags.test(o.tag)){
4191             b += "/>";
4192         }else{
4193             b += ">";
4194             var cn = o.children || o.cn;
4195             if(cn){
4196                 //http://bugs.kde.org/show_bug.cgi?id=71506
4197                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                     for(var i = 0, len = cn.length; i < len; i++) {
4199                         b += createHtml(cn[i], b);
4200                     }
4201                 }else{
4202                     b += createHtml(cn, b);
4203                 }
4204             }
4205             if(o.html){
4206                 b += o.html;
4207             }
4208             b += "</" + o.tag + ">";
4209         }
4210         return b;
4211     };
4212
4213     // build as dom
4214     /** @ignore */
4215     var createDom = function(o, parentNode){
4216          
4217         // defininition craeted..
4218         var ns = false;
4219         if (o.ns && o.ns != 'html') {
4220                
4221             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4222                 xmlns[o.ns] = o.xmlns;
4223                 ns = o.xmlns;
4224             }
4225             if (typeof(xmlns[o.ns]) == 'undefined') {
4226                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4227             }
4228             ns = xmlns[o.ns];
4229         }
4230         
4231         
4232         if (typeof(o) == 'string') {
4233             return parentNode.appendChild(document.createTextNode(o));
4234         }
4235         o.tag = o.tag || div;
4236         if (o.ns && Roo.isIE) {
4237             ns = false;
4238             o.tag = o.ns + ':' + o.tag;
4239             
4240         }
4241         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4242         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4243         for(var attr in o){
4244             
4245             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4246                     attr == "style" || typeof o[attr] == "function") { continue; }
4247                     
4248             if(attr=="cls" && Roo.isIE){
4249                 el.className = o["cls"];
4250             }else{
4251                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4252                 else { 
4253                     el[attr] = o[attr];
4254                 }
4255             }
4256         }
4257         Roo.DomHelper.applyStyles(el, o.style);
4258         var cn = o.children || o.cn;
4259         if(cn){
4260             //http://bugs.kde.org/show_bug.cgi?id=71506
4261              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4262                 for(var i = 0, len = cn.length; i < len; i++) {
4263                     createDom(cn[i], el);
4264                 }
4265             }else{
4266                 createDom(cn, el);
4267             }
4268         }
4269         if(o.html){
4270             el.innerHTML = o.html;
4271         }
4272         if(parentNode){
4273            parentNode.appendChild(el);
4274         }
4275         return el;
4276     };
4277
4278     var ieTable = function(depth, s, h, e){
4279         tempTableEl.innerHTML = [s, h, e].join('');
4280         var i = -1, el = tempTableEl;
4281         while(++i < depth){
4282             el = el.firstChild;
4283         }
4284         return el;
4285     };
4286
4287     // kill repeat to save bytes
4288     var ts = '<table>',
4289         te = '</table>',
4290         tbs = ts+'<tbody>',
4291         tbe = '</tbody>'+te,
4292         trs = tbs + '<tr>',
4293         tre = '</tr>'+tbe;
4294
4295     /**
4296      * @ignore
4297      * Nasty code for IE's broken table implementation
4298      */
4299     var insertIntoTable = function(tag, where, el, html){
4300         if(!tempTableEl){
4301             tempTableEl = document.createElement('div');
4302         }
4303         var node;
4304         var before = null;
4305         if(tag == 'td'){
4306             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4307                 return;
4308             }
4309             if(where == 'beforebegin'){
4310                 before = el;
4311                 el = el.parentNode;
4312             } else{
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315             }
4316             node = ieTable(4, trs, html, tre);
4317         }
4318         else if(tag == 'tr'){
4319             if(where == 'beforebegin'){
4320                 before = el;
4321                 el = el.parentNode;
4322                 node = ieTable(3, tbs, html, tbe);
4323             } else if(where == 'afterend'){
4324                 before = el.nextSibling;
4325                 el = el.parentNode;
4326                 node = ieTable(3, tbs, html, tbe);
4327             } else{ // INTO a TR
4328                 if(where == 'afterbegin'){
4329                     before = el.firstChild;
4330                 }
4331                 node = ieTable(4, trs, html, tre);
4332             }
4333         } else if(tag == 'tbody'){
4334             if(where == 'beforebegin'){
4335                 before = el;
4336                 el = el.parentNode;
4337                 node = ieTable(2, ts, html, te);
4338             } else if(where == 'afterend'){
4339                 before = el.nextSibling;
4340                 el = el.parentNode;
4341                 node = ieTable(2, ts, html, te);
4342             } else{
4343                 if(where == 'afterbegin'){
4344                     before = el.firstChild;
4345                 }
4346                 node = ieTable(3, tbs, html, tbe);
4347             }
4348         } else{ // TABLE
4349             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4350                 return;
4351             }
4352             if(where == 'afterbegin'){
4353                 before = el.firstChild;
4354             }
4355             node = ieTable(2, ts, html, te);
4356         }
4357         el.insertBefore(node, before);
4358         return node;
4359     };
4360
4361     return {
4362     /** True to force the use of DOM instead of html fragments @type Boolean */
4363     useDom : false,
4364
4365     /**
4366      * Returns the markup for the passed Element(s) config
4367      * @param {Object} o The Dom object spec (and children)
4368      * @return {String}
4369      */
4370     markup : function(o){
4371         return createHtml(o);
4372     },
4373
4374     /**
4375      * Applies a style specification to an element
4376      * @param {String/HTMLElement} el The element to apply styles to
4377      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4378      * a function which returns such a specification.
4379      */
4380     applyStyles : function(el, styles){
4381         if(styles){
4382            el = Roo.fly(el);
4383            if(typeof styles == "string"){
4384                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4385                var matches;
4386                while ((matches = re.exec(styles)) != null){
4387                    el.setStyle(matches[1], matches[2]);
4388                }
4389            }else if (typeof styles == "object"){
4390                for (var style in styles){
4391                   el.setStyle(style, styles[style]);
4392                }
4393            }else if (typeof styles == "function"){
4394                 Roo.DomHelper.applyStyles(el, styles.call());
4395            }
4396         }
4397     },
4398
4399     /**
4400      * Inserts an HTML fragment into the Dom
4401      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4402      * @param {HTMLElement} el The context element
4403      * @param {String} html The HTML fragmenet
4404      * @return {HTMLElement} The new node
4405      */
4406     insertHtml : function(where, el, html){
4407         where = where.toLowerCase();
4408         if(el.insertAdjacentHTML){
4409             if(tableRe.test(el.tagName)){
4410                 var rs;
4411                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4412                     return rs;
4413                 }
4414             }
4415             switch(where){
4416                 case "beforebegin":
4417                     el.insertAdjacentHTML('BeforeBegin', html);
4418                     return el.previousSibling;
4419                 case "afterbegin":
4420                     el.insertAdjacentHTML('AfterBegin', html);
4421                     return el.firstChild;
4422                 case "beforeend":
4423                     el.insertAdjacentHTML('BeforeEnd', html);
4424                     return el.lastChild;
4425                 case "afterend":
4426                     el.insertAdjacentHTML('AfterEnd', html);
4427                     return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430         }
4431         var range = el.ownerDocument.createRange();
4432         var frag;
4433         switch(where){
4434              case "beforebegin":
4435                 range.setStartBefore(el);
4436                 frag = range.createContextualFragment(html);
4437                 el.parentNode.insertBefore(frag, el);
4438                 return el.previousSibling;
4439              case "afterbegin":
4440                 if(el.firstChild){
4441                     range.setStartBefore(el.firstChild);
4442                     frag = range.createContextualFragment(html);
4443                     el.insertBefore(frag, el.firstChild);
4444                     return el.firstChild;
4445                 }else{
4446                     el.innerHTML = html;
4447                     return el.firstChild;
4448                 }
4449             case "beforeend":
4450                 if(el.lastChild){
4451                     range.setStartAfter(el.lastChild);
4452                     frag = range.createContextualFragment(html);
4453                     el.appendChild(frag);
4454                     return el.lastChild;
4455                 }else{
4456                     el.innerHTML = html;
4457                     return el.lastChild;
4458                 }
4459             case "afterend":
4460                 range.setStartAfter(el);
4461                 frag = range.createContextualFragment(html);
4462                 el.parentNode.insertBefore(frag, el.nextSibling);
4463                 return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them before el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertBefore : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "beforeBegin");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them after el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object} o The Dom object spec (and children)
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertAfter : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and inserts them as the first child of el
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     insertFirst : function(el, o, returnElement){
4498         return this.doInsert(el, o, returnElement, "afterBegin");
4499     },
4500
4501     // private
4502     doInsert : function(el, o, returnElement, pos, sibling){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml(pos, el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and appends them to el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     append : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         var newNode;
4525         if(this.useDom || o.ns){
4526             newNode = createDom(o, null);
4527             el.appendChild(newNode);
4528         }else{
4529             var html = createHtml(o);
4530             newNode = this.insertHtml("beforeEnd", el, html);
4531         }
4532         return returnElement ? Roo.get(newNode, true) : newNode;
4533     },
4534
4535     /**
4536      * Creates new Dom element(s) and overwrites the contents of el with them
4537      * @param {String/HTMLElement/Element} el The context element
4538      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4539      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4540      * @return {HTMLElement/Roo.Element} The new node
4541      */
4542     overwrite : function(el, o, returnElement){
4543         el = Roo.getDom(el);
4544         if (o.ns) {
4545           
4546             while (el.childNodes.length) {
4547                 el.removeChild(el.firstChild);
4548             }
4549             createDom(o, el);
4550         } else {
4551             el.innerHTML = createHtml(o);   
4552         }
4553         
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     },
4556
4557     /**
4558      * Creates a new Roo.DomHelper.Template from the Dom object spec
4559      * @param {Object} o The Dom object spec (and children)
4560      * @return {Roo.DomHelper.Template} The new template
4561      */
4562     createTemplate : function(o){
4563         var html = createHtml(o);
4564         return new Roo.Template(html);
4565     }
4566     };
4567 }();
4568 /*
4569  * Based on:
4570  * Ext JS Library 1.1.1
4571  * Copyright(c) 2006-2007, Ext JS, LLC.
4572  *
4573  * Originally Released Under LGPL - original licence link has changed is not relivant.
4574  *
4575  * Fork - LGPL
4576  * <script type="text/javascript">
4577  */
4578  
4579 /**
4580 * @class Roo.Template
4581 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4582 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4583 * Usage:
4584 <pre><code>
4585 var t = new Roo.Template({
4586     html :  '&lt;div name="{id}"&gt;' + 
4587         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4588         '&lt;/div&gt;',
4589     myformat: function (value, allValues) {
4590         return 'XX' + value;
4591     }
4592 });
4593 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4594 </code></pre>
4595 * For more information see this blog post with examples:
4596 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4597      - Create Elements using DOM, HTML fragments and Templates</a>. 
4598 * @constructor
4599 * @param {Object} cfg - Configuration object.
4600 */
4601 Roo.Template = function(cfg){
4602     // BC!
4603     if(cfg instanceof Array){
4604         cfg = cfg.join("");
4605     }else if(arguments.length > 1){
4606         cfg = Array.prototype.join.call(arguments, "");
4607     }
4608     
4609     
4610     if (typeof(cfg) == 'object') {
4611         Roo.apply(this,cfg)
4612     } else {
4613         // bc
4614         this.html = cfg;
4615     }
4616     if (this.url) {
4617         this.load();
4618     }
4619     
4620 };
4621 Roo.Template.prototype = {
4622     
4623     /**
4624      * @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..
4625      *                    it should be fixed so that template is observable...
4626      */
4627     url : false,
4628     /**
4629      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4630      */
4631     html : '',
4632     /**
4633      * Returns an HTML fragment of this template with the specified values applied.
4634      * @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'})
4635      * @return {String} The HTML fragment
4636      */
4637     applyTemplate : function(values){
4638         try {
4639            
4640             if(this.compiled){
4641                 return this.compiled(values);
4642             }
4643             var useF = this.disableFormats !== true;
4644             var fm = Roo.util.Format, tpl = this;
4645             var fn = function(m, name, format, args){
4646                 if(format && useF){
4647                     if(format.substr(0, 5) == "this."){
4648                         return tpl.call(format.substr(5), values[name], values);
4649                     }else{
4650                         if(args){
4651                             // quoted values are required for strings in compiled templates, 
4652                             // but for non compiled we need to strip them
4653                             // quoted reversed for jsmin
4654                             var re = /^\s*['"](.*)["']\s*$/;
4655                             args = args.split(',');
4656                             for(var i = 0, len = args.length; i < len; i++){
4657                                 args[i] = args[i].replace(re, "$1");
4658                             }
4659                             args = [values[name]].concat(args);
4660                         }else{
4661                             args = [values[name]];
4662                         }
4663                         return fm[format].apply(fm, args);
4664                     }
4665                 }else{
4666                     return values[name] !== undefined ? values[name] : "";
4667                 }
4668             };
4669             return this.html.replace(this.re, fn);
4670         } catch (e) {
4671             Roo.log(e);
4672             throw e;
4673         }
4674          
4675     },
4676     
4677     loading : false,
4678       
4679     load : function ()
4680     {
4681          
4682         if (this.loading) {
4683             return;
4684         }
4685         var _t = this;
4686         
4687         this.loading = true;
4688         this.compiled = false;
4689         
4690         var cx = new Roo.data.Connection();
4691         cx.request({
4692             url : this.url,
4693             method : 'GET',
4694             success : function (response) {
4695                 _t.loading = false;
4696                 _t.html = response.responseText;
4697                 _t.url = false;
4698                 _t.compile();
4699              },
4700             failure : function(response) {
4701                 Roo.log("Template failed to load from " + _t.url);
4702                 _t.loading = false;
4703             }
4704         });
4705     },
4706
4707     /**
4708      * Sets the HTML used as the template and optionally compiles it.
4709      * @param {String} html
4710      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4711      * @return {Roo.Template} this
4712      */
4713     set : function(html, compile){
4714         this.html = html;
4715         this.compiled = null;
4716         if(compile){
4717             this.compile();
4718         }
4719         return this;
4720     },
4721     
4722     /**
4723      * True to disable format functions (defaults to false)
4724      * @type Boolean
4725      */
4726     disableFormats : false,
4727     
4728     /**
4729     * The regular expression used to match template variables 
4730     * @type RegExp
4731     * @property 
4732     */
4733     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4734     
4735     /**
4736      * Compiles the template into an internal function, eliminating the RegEx overhead.
4737      * @return {Roo.Template} this
4738      */
4739     compile : function(){
4740         var fm = Roo.util.Format;
4741         var useF = this.disableFormats !== true;
4742         var sep = Roo.isGecko ? "+" : ",";
4743         var fn = function(m, name, format, args){
4744             if(format && useF){
4745                 args = args ? ',' + args : "";
4746                 if(format.substr(0, 5) != "this."){
4747                     format = "fm." + format + '(';
4748                 }else{
4749                     format = 'this.call("'+ format.substr(5) + '", ';
4750                     args = ", values";
4751                 }
4752             }else{
4753                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4754             }
4755             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4756         };
4757         var body;
4758         // branched to use + in gecko and [].join() in others
4759         if(Roo.isGecko){
4760             body = "this.compiled = function(values){ return '" +
4761                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4762                     "';};";
4763         }else{
4764             body = ["this.compiled = function(values){ return ['"];
4765             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4766             body.push("'].join('');};");
4767             body = body.join('');
4768         }
4769         /**
4770          * eval:var:values
4771          * eval:var:fm
4772          */
4773         eval(body);
4774         return this;
4775     },
4776     
4777     // private function used to call members
4778     call : function(fnName, value, allValues){
4779         return this[fnName](value, allValues);
4780     },
4781     
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @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'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertFirst: function(el, values, returnElement){
4790         return this.doInsert('afterBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) before el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @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'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertBefore: function(el, values, returnElement){
4801         return this.doInsert('beforeBegin', el, values, returnElement);
4802     },
4803
4804     /**
4805      * Applies the supplied values to the template and inserts the new node(s) after el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @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'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     insertAfter : function(el, values, returnElement){
4812         return this.doInsert('afterEnd', el, values, returnElement);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and appends the new node(s) to el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @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'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     append : function(el, values, returnElement){
4823         return this.doInsert('beforeEnd', el, values, returnElement);
4824     },
4825
4826     doInsert : function(where, el, values, returnEl){
4827         el = Roo.getDom(el);
4828         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4829         return returnEl ? Roo.get(newNode, true) : newNode;
4830     },
4831
4832     /**
4833      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4834      * @param {String/HTMLElement/Roo.Element} el The context element
4835      * @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'})
4836      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4837      * @return {HTMLElement/Roo.Element} The new node or Element
4838      */
4839     overwrite : function(el, values, returnElement){
4840         el = Roo.getDom(el);
4841         el.innerHTML = this.applyTemplate(values);
4842         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4843     }
4844 };
4845 /**
4846  * Alias for {@link #applyTemplate}
4847  * @method
4848  */
4849 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4850
4851 // backwards compat
4852 Roo.DomHelper.Template = Roo.Template;
4853
4854 /**
4855  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4856  * @param {String/HTMLElement} el A DOM element or its id
4857  * @returns {Roo.Template} The created template
4858  * @static
4859  */
4860 Roo.Template.from = function(el){
4861     el = Roo.getDom(el);
4862     return new Roo.Template(el.value || el.innerHTML);
4863 };/*
4864  * Based on:
4865  * Ext JS Library 1.1.1
4866  * Copyright(c) 2006-2007, Ext JS, LLC.
4867  *
4868  * Originally Released Under LGPL - original licence link has changed is not relivant.
4869  *
4870  * Fork - LGPL
4871  * <script type="text/javascript">
4872  */
4873  
4874
4875 /*
4876  * This is code is also distributed under MIT license for use
4877  * with jQuery and prototype JavaScript libraries.
4878  */
4879 /**
4880  * @class Roo.DomQuery
4881 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).
4882 <p>
4883 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>
4884
4885 <p>
4886 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.
4887 </p>
4888 <h4>Element Selectors:</h4>
4889 <ul class="list">
4890     <li> <b>*</b> any element</li>
4891     <li> <b>E</b> an element with the tag E</li>
4892     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4893     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4894     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4895     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4896 </ul>
4897 <h4>Attribute Selectors:</h4>
4898 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4899 <ul class="list">
4900     <li> <b>E[foo]</b> has an attribute "foo"</li>
4901     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4902     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4903     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4904     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4905     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4906     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4907 </ul>
4908 <h4>Pseudo Classes:</h4>
4909 <ul class="list">
4910     <li> <b>E:first-child</b> E is the first child of its parent</li>
4911     <li> <b>E:last-child</b> E is the last child of its parent</li>
4912     <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>
4913     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4914     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4915     <li> <b>E:only-child</b> E is the only child of its parent</li>
4916     <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>
4917     <li> <b>E:first</b> the first E in the resultset</li>
4918     <li> <b>E:last</b> the last E in the resultset</li>
4919     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4920     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4921     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4922     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4923     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4924     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4925     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4926     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4927     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4928 </ul>
4929 <h4>CSS Value Selectors:</h4>
4930 <ul class="list">
4931     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4932     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4933     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4934     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4935     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4936     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4937 </ul>
4938  * @singleton
4939  */
4940 Roo.DomQuery = function(){
4941     var cache = {}, simpleCache = {}, valueCache = {};
4942     var nonSpace = /\S/;
4943     var trimRe = /^\s+|\s+$/g;
4944     var tplRe = /\{(\d+)\}/g;
4945     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4946     var tagTokenRe = /^(#)?([\w-\*]+)/;
4947     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4948
4949     function child(p, index){
4950         var i = 0;
4951         var n = p.firstChild;
4952         while(n){
4953             if(n.nodeType == 1){
4954                if(++i == index){
4955                    return n;
4956                }
4957             }
4958             n = n.nextSibling;
4959         }
4960         return null;
4961     };
4962
4963     function next(n){
4964         while((n = n.nextSibling) && n.nodeType != 1);
4965         return n;
4966     };
4967
4968     function prev(n){
4969         while((n = n.previousSibling) && n.nodeType != 1);
4970         return n;
4971     };
4972
4973     function children(d){
4974         var n = d.firstChild, ni = -1;
4975             while(n){
4976                 var nx = n.nextSibling;
4977                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4978                     d.removeChild(n);
4979                 }else{
4980                     n.nodeIndex = ++ni;
4981                 }
4982                 n = nx;
4983             }
4984             return this;
4985         };
4986
4987     function byClassName(c, a, v){
4988         if(!v){
4989             return c;
4990         }
4991         var r = [], ri = -1, cn;
4992         for(var i = 0, ci; ci = c[i]; i++){
4993             if((' '+ci.className+' ').indexOf(v) != -1){
4994                 r[++ri] = ci;
4995             }
4996         }
4997         return r;
4998     };
4999
5000     function attrValue(n, attr){
5001         if(!n.tagName && typeof n.length != "undefined"){
5002             n = n[0];
5003         }
5004         if(!n){
5005             return null;
5006         }
5007         if(attr == "for"){
5008             return n.htmlFor;
5009         }
5010         if(attr == "class" || attr == "className"){
5011             return n.className;
5012         }
5013         return n.getAttribute(attr) || n[attr];
5014
5015     };
5016
5017     function getNodes(ns, mode, tagName){
5018         var result = [], ri = -1, cs;
5019         if(!ns){
5020             return result;
5021         }
5022         tagName = tagName || "*";
5023         if(typeof ns.getElementsByTagName != "undefined"){
5024             ns = [ns];
5025         }
5026         if(!mode){
5027             for(var i = 0, ni; ni = ns[i]; i++){
5028                 cs = ni.getElementsByTagName(tagName);
5029                 for(var j = 0, ci; ci = cs[j]; j++){
5030                     result[++ri] = ci;
5031                 }
5032             }
5033         }else if(mode == "/" || mode == ">"){
5034             var utag = tagName.toUpperCase();
5035             for(var i = 0, ni, cn; ni = ns[i]; i++){
5036                 cn = ni.children || ni.childNodes;
5037                 for(var j = 0, cj; cj = cn[j]; j++){
5038                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5039                         result[++ri] = cj;
5040                     }
5041                 }
5042             }
5043         }else if(mode == "+"){
5044             var utag = tagName.toUpperCase();
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && n.nodeType != 1);
5047                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }else if(mode == "~"){
5052             for(var i = 0, n; n = ns[i]; i++){
5053                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5054                 if(n){
5055                     result[++ri] = n;
5056                 }
5057             }
5058         }
5059         return result;
5060     };
5061
5062     function concat(a, b){
5063         if(b.slice){
5064             return a.concat(b);
5065         }
5066         for(var i = 0, l = b.length; i < l; i++){
5067             a[a.length] = b[i];
5068         }
5069         return a;
5070     }
5071
5072     function byTag(cs, tagName){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!tagName){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         tagName = tagName.toLowerCase();
5081         for(var i = 0, ci; ci = cs[i]; i++){
5082             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5083                 r[++ri] = ci;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byId(cs, attr, id){
5090         if(cs.tagName || cs == document){
5091             cs = [cs];
5092         }
5093         if(!id){
5094             return cs;
5095         }
5096         var r = [], ri = -1;
5097         for(var i = 0,ci; ci = cs[i]; i++){
5098             if(ci && ci.id == id){
5099                 r[++ri] = ci;
5100                 return r;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function byAttribute(cs, attr, value, op, custom){
5107         var r = [], ri = -1, st = custom=="{";
5108         var f = Roo.DomQuery.operators[op];
5109         for(var i = 0, ci; ci = cs[i]; i++){
5110             var a;
5111             if(st){
5112                 a = Roo.DomQuery.getStyle(ci, attr);
5113             }
5114             else if(attr == "class" || attr == "className"){
5115                 a = ci.className;
5116             }else if(attr == "for"){
5117                 a = ci.htmlFor;
5118             }else if(attr == "href"){
5119                 a = ci.getAttribute("href", 2);
5120             }else{
5121                 a = ci.getAttribute(attr);
5122             }
5123             if((f && f(a, value)) || (!f && a)){
5124                 r[++ri] = ci;
5125             }
5126         }
5127         return r;
5128     };
5129
5130     function byPseudo(cs, name, value){
5131         return Roo.DomQuery.pseudos[name](cs, value);
5132     };
5133
5134     // This is for IE MSXML which does not support expandos.
5135     // IE runs the same speed using setAttribute, however FF slows way down
5136     // and Safari completely fails so they need to continue to use expandos.
5137     var isIE = window.ActiveXObject ? true : false;
5138
5139     // this eval is stop the compressor from
5140     // renaming the variable to something shorter
5141     
5142     /** eval:var:batch */
5143     var batch = 30803; 
5144
5145     var key = 30803;
5146
5147     function nodupIEXml(cs){
5148         var d = ++key;
5149         cs[0].setAttribute("_nodup", d);
5150         var r = [cs[0]];
5151         for(var i = 1, len = cs.length; i < len; i++){
5152             var c = cs[i];
5153             if(!c.getAttribute("_nodup") != d){
5154                 c.setAttribute("_nodup", d);
5155                 r[r.length] = c;
5156             }
5157         }
5158         for(var i = 0, len = cs.length; i < len; i++){
5159             cs[i].removeAttribute("_nodup");
5160         }
5161         return r;
5162     }
5163
5164     function nodup(cs){
5165         if(!cs){
5166             return [];
5167         }
5168         var len = cs.length, c, i, r = cs, cj, ri = -1;
5169         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5170             return cs;
5171         }
5172         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5173             return nodupIEXml(cs);
5174         }
5175         var d = ++key;
5176         cs[0]._nodup = d;
5177         for(i = 1; c = cs[i]; i++){
5178             if(c._nodup != d){
5179                 c._nodup = d;
5180             }else{
5181                 r = [];
5182                 for(var j = 0; j < i; j++){
5183                     r[++ri] = cs[j];
5184                 }
5185                 for(j = i+1; cj = cs[j]; j++){
5186                     if(cj._nodup != d){
5187                         cj._nodup = d;
5188                         r[++ri] = cj;
5189                     }
5190                 }
5191                 return r;
5192             }
5193         }
5194         return r;
5195     }
5196
5197     function quickDiffIEXml(c1, c2){
5198         var d = ++key;
5199         for(var i = 0, len = c1.length; i < len; i++){
5200             c1[i].setAttribute("_qdiff", d);
5201         }
5202         var r = [];
5203         for(var i = 0, len = c2.length; i < len; i++){
5204             if(c2[i].getAttribute("_qdiff") != d){
5205                 r[r.length] = c2[i];
5206             }
5207         }
5208         for(var i = 0, len = c1.length; i < len; i++){
5209            c1[i].removeAttribute("_qdiff");
5210         }
5211         return r;
5212     }
5213
5214     function quickDiff(c1, c2){
5215         var len1 = c1.length;
5216         if(!len1){
5217             return c2;
5218         }
5219         if(isIE && c1[0].selectSingleNode){
5220             return quickDiffIEXml(c1, c2);
5221         }
5222         var d = ++key;
5223         for(var i = 0; i < len1; i++){
5224             c1[i]._qdiff = d;
5225         }
5226         var r = [];
5227         for(var i = 0, len = c2.length; i < len; i++){
5228             if(c2[i]._qdiff != d){
5229                 r[r.length] = c2[i];
5230             }
5231         }
5232         return r;
5233     }
5234
5235     function quickId(ns, mode, root, id){
5236         if(ns == root){
5237            var d = root.ownerDocument || root;
5238            return d.getElementById(id);
5239         }
5240         ns = getNodes(ns, mode, "*");
5241         return byId(ns, null, id);
5242     }
5243
5244     return {
5245         getStyle : function(el, name){
5246             return Roo.fly(el).getStyle(name);
5247         },
5248         /**
5249          * Compiles a selector/xpath query into a reusable function. The returned function
5250          * takes one parameter "root" (optional), which is the context node from where the query should start.
5251          * @param {String} selector The selector/xpath query
5252          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5253          * @return {Function}
5254          */
5255         compile : function(path, type){
5256             type = type || "select";
5257             
5258             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5259             var q = path, mode, lq;
5260             var tk = Roo.DomQuery.matchers;
5261             var tklen = tk.length;
5262             var mm;
5263
5264             // accept leading mode switch
5265             var lmode = q.match(modeRe);
5266             if(lmode && lmode[1]){
5267                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5268                 q = q.replace(lmode[1], "");
5269             }
5270             // strip leading slashes
5271             while(path.substr(0, 1)=="/"){
5272                 path = path.substr(1);
5273             }
5274
5275             while(q && lq != q){
5276                 lq = q;
5277                 var tm = q.match(tagTokenRe);
5278                 if(type == "select"){
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }else if(q.substr(0, 1) != '@'){
5287                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5288                     }
5289                 }else{
5290                     if(tm){
5291                         if(tm[1] == "#"){
5292                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5293                         }else{
5294                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5295                         }
5296                         q = q.replace(tm[0], "");
5297                     }
5298                 }
5299                 while(!(mm = q.match(modeRe))){
5300                     var matched = false;
5301                     for(var j = 0; j < tklen; j++){
5302                         var t = tk[j];
5303                         var m = q.match(t.re);
5304                         if(m){
5305                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5306                                                     return m[i];
5307                                                 });
5308                             q = q.replace(m[0], "");
5309                             matched = true;
5310                             break;
5311                         }
5312                     }
5313                     // prevent infinite loop on bad selector
5314                     if(!matched){
5315                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5316                     }
5317                 }
5318                 if(mm[1]){
5319                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5320                     q = q.replace(mm[1], "");
5321                 }
5322             }
5323             fn[fn.length] = "return nodup(n);\n}";
5324             
5325              /** 
5326               * list of variables that need from compression as they are used by eval.
5327              *  eval:var:batch 
5328              *  eval:var:nodup
5329              *  eval:var:byTag
5330              *  eval:var:ById
5331              *  eval:var:getNodes
5332              *  eval:var:quickId
5333              *  eval:var:mode
5334              *  eval:var:root
5335              *  eval:var:n
5336              *  eval:var:byClassName
5337              *  eval:var:byPseudo
5338              *  eval:var:byAttribute
5339              *  eval:var:attrValue
5340              * 
5341              **/ 
5342             eval(fn.join(""));
5343             return f;
5344         },
5345
5346         /**
5347          * Selects a group of elements.
5348          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Array}
5351          */
5352         select : function(path, root, type){
5353             if(!root || root == document){
5354                 root = document;
5355             }
5356             if(typeof root == "string"){
5357                 root = document.getElementById(root);
5358             }
5359             var paths = path.split(",");
5360             var results = [];
5361             for(var i = 0, len = paths.length; i < len; i++){
5362                 var p = paths[i].replace(trimRe, "");
5363                 if(!cache[p]){
5364                     cache[p] = Roo.DomQuery.compile(p);
5365                     if(!cache[p]){
5366                         throw p + " is not a valid selector";
5367                     }
5368                 }
5369                 var result = cache[p](root);
5370                 if(result && result != document){
5371                     results = results.concat(result);
5372                 }
5373             }
5374             if(paths.length > 1){
5375                 return nodup(results);
5376             }
5377             return results;
5378         },
5379
5380         /**
5381          * Selects a single element.
5382          * @param {String} selector The selector/xpath query
5383          * @param {Node} root (optional) The start of the query (defaults to document).
5384          * @return {Element}
5385          */
5386         selectNode : function(path, root){
5387             return Roo.DomQuery.select(path, root)[0];
5388         },
5389
5390         /**
5391          * Selects the value of a node, optionally replacing null with the defaultValue.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {String} defaultValue
5395          */
5396         selectValue : function(path, root, defaultValue){
5397             path = path.replace(trimRe, "");
5398             if(!valueCache[path]){
5399                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5400             }
5401             var n = valueCache[path](root);
5402             n = n[0] ? n[0] : n;
5403             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5404             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5405         },
5406
5407         /**
5408          * Selects the value of a node, parsing integers and floats.
5409          * @param {String} selector The selector/xpath query
5410          * @param {Node} root (optional) The start of the query (defaults to document).
5411          * @param {Number} defaultValue
5412          * @return {Number}
5413          */
5414         selectNumber : function(path, root, defaultValue){
5415             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5416             return parseFloat(v);
5417         },
5418
5419         /**
5420          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5421          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5422          * @param {String} selector The simple selector to test
5423          * @return {Boolean}
5424          */
5425         is : function(el, ss){
5426             if(typeof el == "string"){
5427                 el = document.getElementById(el);
5428             }
5429             var isArray = (el instanceof Array);
5430             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5431             return isArray ? (result.length == el.length) : (result.length > 0);
5432         },
5433
5434         /**
5435          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5436          * @param {Array} el An array of elements to filter
5437          * @param {String} selector The simple selector to test
5438          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5439          * the selector instead of the ones that match
5440          * @return {Array}
5441          */
5442         filter : function(els, ss, nonMatches){
5443             ss = ss.replace(trimRe, "");
5444             if(!simpleCache[ss]){
5445                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5446             }
5447             var result = simpleCache[ss](els);
5448             return nonMatches ? quickDiff(result, els) : result;
5449         },
5450
5451         /**
5452          * Collection of matching regular expressions and code snippets.
5453          */
5454         matchers : [{
5455                 re: /^\.([\w-]+)/,
5456                 select: 'n = byClassName(n, null, " {1} ");'
5457             }, {
5458                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5459                 select: 'n = byPseudo(n, "{1}", "{2}");'
5460             },{
5461                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5462                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5463             }, {
5464                 re: /^#([\w-]+)/,
5465                 select: 'n = byId(n, null, "{1}");'
5466             },{
5467                 re: /^@([\w-]+)/,
5468                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5469             }
5470         ],
5471
5472         /**
5473          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5474          * 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;.
5475          */
5476         operators : {
5477             "=" : function(a, v){
5478                 return a == v;
5479             },
5480             "!=" : function(a, v){
5481                 return a != v;
5482             },
5483             "^=" : function(a, v){
5484                 return a && a.substr(0, v.length) == v;
5485             },
5486             "$=" : function(a, v){
5487                 return a && a.substr(a.length-v.length) == v;
5488             },
5489             "*=" : function(a, v){
5490                 return a && a.indexOf(v) !== -1;
5491             },
5492             "%=" : function(a, v){
5493                 return (a % v) == 0;
5494             },
5495             "|=" : function(a, v){
5496                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5497             },
5498             "~=" : function(a, v){
5499                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5500             }
5501         },
5502
5503         /**
5504          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5505          * and the argument (if any) supplied in the selector.
5506          */
5507         pseudos : {
5508             "first-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.previousSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "last-child" : function(c){
5520                 var r = [], ri = -1, n;
5521                 for(var i = 0, ci; ci = n = c[i]; i++){
5522                     while((n = n.nextSibling) && n.nodeType != 1);
5523                     if(!n){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "nth-child" : function(c, a) {
5531                 var r = [], ri = -1;
5532                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5533                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5534                 for(var i = 0, n; n = c[i]; i++){
5535                     var pn = n.parentNode;
5536                     if (batch != pn._batch) {
5537                         var j = 0;
5538                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5539                             if(cn.nodeType == 1){
5540                                cn.nodeIndex = ++j;
5541                             }
5542                         }
5543                         pn._batch = batch;
5544                     }
5545                     if (f == 1) {
5546                         if (l == 0 || n.nodeIndex == l){
5547                             r[++ri] = n;
5548                         }
5549                     } else if ((n.nodeIndex + l) % f == 0){
5550                         r[++ri] = n;
5551                     }
5552                 }
5553
5554                 return r;
5555             },
5556
5557             "only-child" : function(c){
5558                 var r = [], ri = -1;;
5559                 for(var i = 0, ci; ci = c[i]; i++){
5560                     if(!prev(ci) && !next(ci)){
5561                         r[++ri] = ci;
5562                     }
5563                 }
5564                 return r;
5565             },
5566
5567             "empty" : function(c){
5568                 var r = [], ri = -1;
5569                 for(var i = 0, ci; ci = c[i]; i++){
5570                     var cns = ci.childNodes, j = 0, cn, empty = true;
5571                     while(cn = cns[j]){
5572                         ++j;
5573                         if(cn.nodeType == 1 || cn.nodeType == 3){
5574                             empty = false;
5575                             break;
5576                         }
5577                     }
5578                     if(empty){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "contains" : function(c, v){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "nodeValue" : function(c, v){
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "checked" : function(c){
5606                 var r = [], ri = -1;
5607                 for(var i = 0, ci; ci = c[i]; i++){
5608                     if(ci.checked == true){
5609                         r[++ri] = ci;
5610                     }
5611                 }
5612                 return r;
5613             },
5614
5615             "not" : function(c, ss){
5616                 return Roo.DomQuery.filter(c, ss, true);
5617             },
5618
5619             "odd" : function(c){
5620                 return this["nth-child"](c, "odd");
5621             },
5622
5623             "even" : function(c){
5624                 return this["nth-child"](c, "even");
5625             },
5626
5627             "nth" : function(c, a){
5628                 return c[a-1] || [];
5629             },
5630
5631             "first" : function(c){
5632                 return c[0] || [];
5633             },
5634
5635             "last" : function(c){
5636                 return c[c.length-1] || [];
5637             },
5638
5639             "has" : function(c, ss){
5640                 var s = Roo.DomQuery.select;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     if(s(ss, ci).length > 0){
5644                         r[++ri] = ci;
5645                     }
5646                 }
5647                 return r;
5648             },
5649
5650             "next" : function(c, ss){
5651                 var is = Roo.DomQuery.is;
5652                 var r = [], ri = -1;
5653                 for(var i = 0, ci; ci = c[i]; i++){
5654                     var n = next(ci);
5655                     if(n && is(n, ss)){
5656                         r[++ri] = ci;
5657                     }
5658                 }
5659                 return r;
5660             },
5661
5662             "prev" : function(c, ss){
5663                 var is = Roo.DomQuery.is;
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     var n = prev(ci);
5667                     if(n && is(n, ss)){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             }
5673         }
5674     };
5675 }();
5676
5677 /**
5678  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5679  * @param {String} path The selector/xpath query
5680  * @param {Node} root (optional) The start of the query (defaults to document).
5681  * @return {Array}
5682  * @member Roo
5683  * @method query
5684  */
5685 Roo.query = Roo.DomQuery.select;
5686 /*
5687  * Based on:
5688  * Ext JS Library 1.1.1
5689  * Copyright(c) 2006-2007, Ext JS, LLC.
5690  *
5691  * Originally Released Under LGPL - original licence link has changed is not relivant.
5692  *
5693  * Fork - LGPL
5694  * <script type="text/javascript">
5695  */
5696
5697 /**
5698  * @class Roo.util.Observable
5699  * Base class that provides a common interface for publishing events. Subclasses are expected to
5700  * to have a property "events" with all the events defined.<br>
5701  * For example:
5702  * <pre><code>
5703  Employee = function(name){
5704     this.name = name;
5705     this.addEvents({
5706         "fired" : true,
5707         "quit" : true
5708     });
5709  }
5710  Roo.extend(Employee, Roo.util.Observable);
5711 </code></pre>
5712  * @param {Object} config properties to use (incuding events / listeners)
5713  */
5714
5715 Roo.util.Observable = function(cfg){
5716     
5717     cfg = cfg|| {};
5718     this.addEvents(cfg.events || {});
5719     if (cfg.events) {
5720         delete cfg.events; // make sure
5721     }
5722      
5723     Roo.apply(this, cfg);
5724     
5725     if(this.listeners){
5726         this.on(this.listeners);
5727         delete this.listeners;
5728     }
5729 };
5730 Roo.util.Observable.prototype = {
5731     /** 
5732  * @cfg {Object} listeners  list of events and functions to call for this object, 
5733  * For example :
5734  * <pre><code>
5735     listeners :  { 
5736        'click' : function(e) {
5737            ..... 
5738         } ,
5739         .... 
5740     } 
5741   </code></pre>
5742  */
5743     
5744     
5745     /**
5746      * Fires the specified event with the passed parameters (minus the event name).
5747      * @param {String} eventName
5748      * @param {Object...} args Variable number of parameters are passed to handlers
5749      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5750      */
5751     fireEvent : function(){
5752         var ce = this.events[arguments[0].toLowerCase()];
5753         if(typeof ce == "object"){
5754             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5755         }else{
5756             return true;
5757         }
5758     },
5759
5760     // private
5761     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5762
5763     /**
5764      * Appends an event handler to this component
5765      * @param {String}   eventName The type of event to listen for
5766      * @param {Function} handler The method the event invokes
5767      * @param {Object}   scope (optional) The scope in which to execute the handler
5768      * function. The handler function's "this" context.
5769      * @param {Object}   options (optional) An object containing handler configuration
5770      * properties. This may contain any of the following properties:<ul>
5771      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5772      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5773      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5774      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5775      * by the specified number of milliseconds. If the event fires again within that time, the original
5776      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5777      * </ul><br>
5778      * <p>
5779      * <b>Combining Options</b><br>
5780      * Using the options argument, it is possible to combine different types of listeners:<br>
5781      * <br>
5782      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5783                 <pre><code>
5784                 el.on('click', this.onClick, this, {
5785                         single: true,
5786                 delay: 100,
5787                 forumId: 4
5788                 });
5789                 </code></pre>
5790      * <p>
5791      * <b>Attaching multiple handlers in 1 call</b><br>
5792      * The method also allows for a single argument to be passed which is a config object containing properties
5793      * which specify multiple handlers.
5794      * <pre><code>
5795                 el.on({
5796                         'click': {
5797                         fn: this.onClick,
5798                         scope: this,
5799                         delay: 100
5800                 }, 
5801                 'mouseover': {
5802                         fn: this.onMouseOver,
5803                         scope: this
5804                 },
5805                 'mouseout': {
5806                         fn: this.onMouseOut,
5807                         scope: this
5808                 }
5809                 });
5810                 </code></pre>
5811      * <p>
5812      * Or a shorthand syntax which passes the same scope object to all handlers:
5813         <pre><code>
5814                 el.on({
5815                         'click': this.onClick,
5816                 'mouseover': this.onMouseOver,
5817                 'mouseout': this.onMouseOut,
5818                 scope: this
5819                 });
5820                 </code></pre>
5821      */
5822     addListener : function(eventName, fn, scope, o){
5823         if(typeof eventName == "object"){
5824             o = eventName;
5825             for(var e in o){
5826                 if(this.filterOptRe.test(e)){
5827                     continue;
5828                 }
5829                 if(typeof o[e] == "function"){
5830                     // shared options
5831                     this.addListener(e, o[e], o.scope,  o);
5832                 }else{
5833                     // individual options
5834                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5835                 }
5836             }
5837             return;
5838         }
5839         o = (!o || typeof o == "boolean") ? {} : o;
5840         eventName = eventName.toLowerCase();
5841         var ce = this.events[eventName] || true;
5842         if(typeof ce == "boolean"){
5843             ce = new Roo.util.Event(this, eventName);
5844             this.events[eventName] = ce;
5845         }
5846         ce.addListener(fn, scope, o);
5847     },
5848
5849     /**
5850      * Removes a listener
5851      * @param {String}   eventName     The type of event to listen for
5852      * @param {Function} handler        The handler to remove
5853      * @param {Object}   scope  (optional) The scope (this object) for the handler
5854      */
5855     removeListener : function(eventName, fn, scope){
5856         var ce = this.events[eventName.toLowerCase()];
5857         if(typeof ce == "object"){
5858             ce.removeListener(fn, scope);
5859         }
5860     },
5861
5862     /**
5863      * Removes all listeners for this object
5864      */
5865     purgeListeners : function(){
5866         for(var evt in this.events){
5867             if(typeof this.events[evt] == "object"){
5868                  this.events[evt].clearListeners();
5869             }
5870         }
5871     },
5872
5873     relayEvents : function(o, events){
5874         var createHandler = function(ename){
5875             return function(){
5876                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5877             };
5878         };
5879         for(var i = 0, len = events.length; i < len; i++){
5880             var ename = events[i];
5881             if(!this.events[ename]){ this.events[ename] = true; };
5882             o.on(ename, createHandler(ename), this);
5883         }
5884     },
5885
5886     /**
5887      * Used to define events on this Observable
5888      * @param {Object} object The object with the events defined
5889      */
5890     addEvents : function(o){
5891         if(!this.events){
5892             this.events = {};
5893         }
5894         Roo.applyIf(this.events, o);
5895     },
5896
5897     /**
5898      * Checks to see if this object has any listeners for a specified event
5899      * @param {String} eventName The name of the event to check for
5900      * @return {Boolean} True if the event is being listened for, else false
5901      */
5902     hasListener : function(eventName){
5903         var e = this.events[eventName];
5904         return typeof e == "object" && e.listeners.length > 0;
5905     }
5906 };
5907 /**
5908  * Appends an event handler to this element (shorthand for addListener)
5909  * @param {String}   eventName     The type of event to listen for
5910  * @param {Function} handler        The method the event invokes
5911  * @param {Object}   scope (optional) The scope in which to execute the handler
5912  * function. The handler function's "this" context.
5913  * @param {Object}   options  (optional)
5914  * @method
5915  */
5916 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5917 /**
5918  * Removes a listener (shorthand for removeListener)
5919  * @param {String}   eventName     The type of event to listen for
5920  * @param {Function} handler        The handler to remove
5921  * @param {Object}   scope  (optional) The scope (this object) for the handler
5922  * @method
5923  */
5924 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5925
5926 /**
5927  * Starts capture on the specified Observable. All events will be passed
5928  * to the supplied function with the event name + standard signature of the event
5929  * <b>before</b> the event is fired. If the supplied function returns false,
5930  * the event will not fire.
5931  * @param {Observable} o The Observable to capture
5932  * @param {Function} fn The function to call
5933  * @param {Object} scope (optional) The scope (this object) for the fn
5934  * @static
5935  */
5936 Roo.util.Observable.capture = function(o, fn, scope){
5937     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5938 };
5939
5940 /**
5941  * Removes <b>all</b> added captures from the Observable.
5942  * @param {Observable} o The Observable to release
5943  * @static
5944  */
5945 Roo.util.Observable.releaseCapture = function(o){
5946     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5947 };
5948
5949 (function(){
5950
5951     var createBuffered = function(h, o, scope){
5952         var task = new Roo.util.DelayedTask();
5953         return function(){
5954             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5955         };
5956     };
5957
5958     var createSingle = function(h, e, fn, scope){
5959         return function(){
5960             e.removeListener(fn, scope);
5961             return h.apply(scope, arguments);
5962         };
5963     };
5964
5965     var createDelayed = function(h, o, scope){
5966         return function(){
5967             var args = Array.prototype.slice.call(arguments, 0);
5968             setTimeout(function(){
5969                 h.apply(scope, args);
5970             }, o.delay || 10);
5971         };
5972     };
5973
5974     Roo.util.Event = function(obj, name){
5975         this.name = name;
5976         this.obj = obj;
5977         this.listeners = [];
5978     };
5979
5980     Roo.util.Event.prototype = {
5981         addListener : function(fn, scope, options){
5982             var o = options || {};
5983             scope = scope || this.obj;
5984             if(!this.isListening(fn, scope)){
5985                 var l = {fn: fn, scope: scope, options: o};
5986                 var h = fn;
5987                 if(o.delay){
5988                     h = createDelayed(h, o, scope);
5989                 }
5990                 if(o.single){
5991                     h = createSingle(h, this, fn, scope);
5992                 }
5993                 if(o.buffer){
5994                     h = createBuffered(h, o, scope);
5995                 }
5996                 l.fireFn = h;
5997                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5998                     this.listeners.push(l);
5999                 }else{
6000                     this.listeners = this.listeners.slice(0);
6001                     this.listeners.push(l);
6002                 }
6003             }
6004         },
6005
6006         findListener : function(fn, scope){
6007             scope = scope || this.obj;
6008             var ls = this.listeners;
6009             for(var i = 0, len = ls.length; i < len; i++){
6010                 var l = ls[i];
6011                 if(l.fn == fn && l.scope == scope){
6012                     return i;
6013                 }
6014             }
6015             return -1;
6016         },
6017
6018         isListening : function(fn, scope){
6019             return this.findListener(fn, scope) != -1;
6020         },
6021
6022         removeListener : function(fn, scope){
6023             var index;
6024             if((index = this.findListener(fn, scope)) != -1){
6025                 if(!this.firing){
6026                     this.listeners.splice(index, 1);
6027                 }else{
6028                     this.listeners = this.listeners.slice(0);
6029                     this.listeners.splice(index, 1);
6030                 }
6031                 return true;
6032             }
6033             return false;
6034         },
6035
6036         clearListeners : function(){
6037             this.listeners = [];
6038         },
6039
6040         fire : function(){
6041             var ls = this.listeners, scope, len = ls.length;
6042             if(len > 0){
6043                 this.firing = true;
6044                 var args = Array.prototype.slice.call(arguments, 0);
6045                 for(var i = 0; i < len; i++){
6046                     var l = ls[i];
6047                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6048                         this.firing = false;
6049                         return false;
6050                     }
6051                 }
6052                 this.firing = false;
6053             }
6054             return true;
6055         }
6056     };
6057 })();/*
6058  * RooJS Library 
6059  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6060  *
6061  * Licence LGPL 
6062  *
6063  */
6064  
6065 /**
6066  * @class Roo.Document
6067  * @extends Roo.util.Observable
6068  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6069  * 
6070  * @param {Object} config the methods and properties of the 'base' class for the application.
6071  * 
6072  *  Generic Page handler - implement this to start your app..
6073  * 
6074  * eg.
6075  *  MyProject = new Roo.Document({
6076         events : {
6077             'load' : true // your events..
6078         },
6079         listeners : {
6080             'ready' : function() {
6081                 // fired on Roo.onReady()
6082             }
6083         }
6084  * 
6085  */
6086 Roo.Document = function(cfg) {
6087      
6088     this.addEvents({ 
6089         'ready' : true
6090     });
6091     Roo.util.Observable.call(this,cfg);
6092     
6093     var _this = this;
6094     
6095     Roo.onReady(function() {
6096         _this.fireEvent('ready');
6097     },null,false);
6098     
6099     
6100 }
6101
6102 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6103  * Based on:
6104  * Ext JS Library 1.1.1
6105  * Copyright(c) 2006-2007, Ext JS, LLC.
6106  *
6107  * Originally Released Under LGPL - original licence link has changed is not relivant.
6108  *
6109  * Fork - LGPL
6110  * <script type="text/javascript">
6111  */
6112
6113 /**
6114  * @class Roo.EventManager
6115  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6116  * several useful events directly.
6117  * See {@link Roo.EventObject} for more details on normalized event objects.
6118  * @singleton
6119  */
6120 Roo.EventManager = function(){
6121     var docReadyEvent, docReadyProcId, docReadyState = false;
6122     var resizeEvent, resizeTask, textEvent, textSize;
6123     var E = Roo.lib.Event;
6124     var D = Roo.lib.Dom;
6125
6126     
6127     
6128
6129     var fireDocReady = function(){
6130         if(!docReadyState){
6131             docReadyState = true;
6132             Roo.isReady = true;
6133             if(docReadyProcId){
6134                 clearInterval(docReadyProcId);
6135             }
6136             if(Roo.isGecko || Roo.isOpera) {
6137                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6138             }
6139             if(Roo.isIE){
6140                 var defer = document.getElementById("ie-deferred-loader");
6141                 if(defer){
6142                     defer.onreadystatechange = null;
6143                     defer.parentNode.removeChild(defer);
6144                 }
6145             }
6146             if(docReadyEvent){
6147                 docReadyEvent.fire();
6148                 docReadyEvent.clearListeners();
6149             }
6150         }
6151     };
6152     
6153     var initDocReady = function(){
6154         docReadyEvent = new Roo.util.Event();
6155         if(Roo.isGecko || Roo.isOpera) {
6156             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6157         }else if(Roo.isIE){
6158             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6159             var defer = document.getElementById("ie-deferred-loader");
6160             defer.onreadystatechange = function(){
6161                 if(this.readyState == "complete"){
6162                     fireDocReady();
6163                 }
6164             };
6165         }else if(Roo.isSafari){ 
6166             docReadyProcId = setInterval(function(){
6167                 var rs = document.readyState;
6168                 if(rs == "complete") {
6169                     fireDocReady();     
6170                  }
6171             }, 10);
6172         }
6173         // no matter what, make sure it fires on load
6174         E.on(window, "load", fireDocReady);
6175     };
6176
6177     var createBuffered = function(h, o){
6178         var task = new Roo.util.DelayedTask(h);
6179         return function(e){
6180             // create new event object impl so new events don't wipe out properties
6181             e = new Roo.EventObjectImpl(e);
6182             task.delay(o.buffer, h, null, [e]);
6183         };
6184     };
6185
6186     var createSingle = function(h, el, ename, fn){
6187         return function(e){
6188             Roo.EventManager.removeListener(el, ename, fn);
6189             h(e);
6190         };
6191     };
6192
6193     var createDelayed = function(h, o){
6194         return function(e){
6195             // create new event object impl so new events don't wipe out properties
6196             e = new Roo.EventObjectImpl(e);
6197             setTimeout(function(){
6198                 h(e);
6199             }, o.delay || 10);
6200         };
6201     };
6202     var transitionEndVal = false;
6203     
6204     var transitionEnd = function()
6205     {
6206         if (transitionEndVal) {
6207             return transitionEndVal;
6208         }
6209         var el = document.createElement('div');
6210
6211         var transEndEventNames = {
6212             WebkitTransition : 'webkitTransitionEnd',
6213             MozTransition    : 'transitionend',
6214             OTransition      : 'oTransitionEnd otransitionend',
6215             transition       : 'transitionend'
6216         };
6217     
6218         for (var name in transEndEventNames) {
6219             if (el.style[name] !== undefined) {
6220                 transitionEndVal = transEndEventNames[name];
6221                 return  transitionEndVal ;
6222             }
6223         }
6224     }
6225     
6226
6227     var listen = function(element, ename, opt, fn, scope){
6228         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6229         fn = fn || o.fn; scope = scope || o.scope;
6230         var el = Roo.getDom(element);
6231         
6232         
6233         if(!el){
6234             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6235         }
6236         
6237         if (ename == 'transitionend') {
6238             ename = transitionEnd();
6239         }
6240         var h = function(e){
6241             e = Roo.EventObject.setEvent(e);
6242             var t;
6243             if(o.delegate){
6244                 t = e.getTarget(o.delegate, el);
6245                 if(!t){
6246                     return;
6247                 }
6248             }else{
6249                 t = e.target;
6250             }
6251             if(o.stopEvent === true){
6252                 e.stopEvent();
6253             }
6254             if(o.preventDefault === true){
6255                e.preventDefault();
6256             }
6257             if(o.stopPropagation === true){
6258                 e.stopPropagation();
6259             }
6260
6261             if(o.normalized === false){
6262                 e = e.browserEvent;
6263             }
6264
6265             fn.call(scope || el, e, t, o);
6266         };
6267         if(o.delay){
6268             h = createDelayed(h, o);
6269         }
6270         if(o.single){
6271             h = createSingle(h, el, ename, fn);
6272         }
6273         if(o.buffer){
6274             h = createBuffered(h, o);
6275         }
6276         fn._handlers = fn._handlers || [];
6277         
6278         
6279         fn._handlers.push([Roo.id(el), ename, h]);
6280         
6281         
6282          
6283         E.on(el, ename, h);
6284         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6285             el.addEventListener("DOMMouseScroll", h, false);
6286             E.on(window, 'unload', function(){
6287                 el.removeEventListener("DOMMouseScroll", h, false);
6288             });
6289         }
6290         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6291             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6292         }
6293         return h;
6294     };
6295
6296     var stopListening = function(el, ename, fn){
6297         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6298         if(hds){
6299             for(var i = 0, len = hds.length; i < len; i++){
6300                 var h = hds[i];
6301                 if(h[0] == id && h[1] == ename){
6302                     hd = h[2];
6303                     hds.splice(i, 1);
6304                     break;
6305                 }
6306             }
6307         }
6308         E.un(el, ename, hd);
6309         el = Roo.getDom(el);
6310         if(ename == "mousewheel" && el.addEventListener){
6311             el.removeEventListener("DOMMouseScroll", hd, false);
6312         }
6313         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6314             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6315         }
6316     };
6317
6318     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6319     
6320     var pub = {
6321         
6322         
6323         /** 
6324          * Fix for doc tools
6325          * @scope Roo.EventManager
6326          */
6327         
6328         
6329         /** 
6330          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6331          * object with a Roo.EventObject
6332          * @param {Function} fn        The method the event invokes
6333          * @param {Object}   scope    An object that becomes the scope of the handler
6334          * @param {boolean}  override If true, the obj passed in becomes
6335          *                             the execution scope of the listener
6336          * @return {Function} The wrapped function
6337          * @deprecated
6338          */
6339         wrap : function(fn, scope, override){
6340             return function(e){
6341                 Roo.EventObject.setEvent(e);
6342                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6343             };
6344         },
6345         
6346         /**
6347      * Appends an event handler to an element (shorthand for addListener)
6348      * @param {String/HTMLElement}   element        The html element or id to assign the
6349      * @param {String}   eventName The type of event to listen for
6350      * @param {Function} handler The method the event invokes
6351      * @param {Object}   scope (optional) The scope in which to execute the handler
6352      * function. The handler function's "this" context.
6353      * @param {Object}   options (optional) An object containing handler configuration
6354      * properties. This may contain any of the following properties:<ul>
6355      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6356      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6357      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6358      * <li>preventDefault {Boolean} True to prevent the default action</li>
6359      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6360      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6361      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6362      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6363      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6364      * by the specified number of milliseconds. If the event fires again within that time, the original
6365      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6366      * </ul><br>
6367      * <p>
6368      * <b>Combining Options</b><br>
6369      * Using the options argument, it is possible to combine different types of listeners:<br>
6370      * <br>
6371      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6372      * Code:<pre><code>
6373 el.on('click', this.onClick, this, {
6374     single: true,
6375     delay: 100,
6376     stopEvent : true,
6377     forumId: 4
6378 });</code></pre>
6379      * <p>
6380      * <b>Attaching multiple handlers in 1 call</b><br>
6381       * The method also allows for a single argument to be passed which is a config object containing properties
6382      * which specify multiple handlers.
6383      * <p>
6384      * Code:<pre><code>
6385 el.on({
6386     'click' : {
6387         fn: this.onClick
6388         scope: this,
6389         delay: 100
6390     },
6391     'mouseover' : {
6392         fn: this.onMouseOver
6393         scope: this
6394     },
6395     'mouseout' : {
6396         fn: this.onMouseOut
6397         scope: this
6398     }
6399 });</code></pre>
6400      * <p>
6401      * Or a shorthand syntax:<br>
6402      * Code:<pre><code>
6403 el.on({
6404     'click' : this.onClick,
6405     'mouseover' : this.onMouseOver,
6406     'mouseout' : this.onMouseOut
6407     scope: this
6408 });</code></pre>
6409      */
6410         addListener : function(element, eventName, fn, scope, options){
6411             if(typeof eventName == "object"){
6412                 var o = eventName;
6413                 for(var e in o){
6414                     if(propRe.test(e)){
6415                         continue;
6416                     }
6417                     if(typeof o[e] == "function"){
6418                         // shared options
6419                         listen(element, e, o, o[e], o.scope);
6420                     }else{
6421                         // individual options
6422                         listen(element, e, o[e]);
6423                     }
6424                 }
6425                 return;
6426             }
6427             return listen(element, eventName, options, fn, scope);
6428         },
6429         
6430         /**
6431          * Removes an event handler
6432          *
6433          * @param {String/HTMLElement}   element        The id or html element to remove the 
6434          *                             event from
6435          * @param {String}   eventName     The type of event
6436          * @param {Function} fn
6437          * @return {Boolean} True if a listener was actually removed
6438          */
6439         removeListener : function(element, eventName, fn){
6440             return stopListening(element, eventName, fn);
6441         },
6442         
6443         /**
6444          * Fires when the document is ready (before onload and before images are loaded). Can be 
6445          * accessed shorthanded Roo.onReady().
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An  object that becomes the scope of the handler
6448          * @param {boolean}  options
6449          */
6450         onDocumentReady : function(fn, scope, options){
6451             if(docReadyState){ // if it already fired
6452                 docReadyEvent.addListener(fn, scope, options);
6453                 docReadyEvent.fire();
6454                 docReadyEvent.clearListeners();
6455                 return;
6456             }
6457             if(!docReadyEvent){
6458                 initDocReady();
6459             }
6460             docReadyEvent.addListener(fn, scope, options);
6461         },
6462         
6463         /**
6464          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6465          * @param {Function} fn        The method the event invokes
6466          * @param {Object}   scope    An object that becomes the scope of the handler
6467          * @param {boolean}  options
6468          */
6469         onWindowResize : function(fn, scope, options){
6470             if(!resizeEvent){
6471                 resizeEvent = new Roo.util.Event();
6472                 resizeTask = new Roo.util.DelayedTask(function(){
6473                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6474                 });
6475                 E.on(window, "resize", function(){
6476                     if(Roo.isIE){
6477                         resizeTask.delay(50);
6478                     }else{
6479                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6480                     }
6481                 });
6482             }
6483             resizeEvent.addListener(fn, scope, options);
6484         },
6485
6486         /**
6487          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6488          * @param {Function} fn        The method the event invokes
6489          * @param {Object}   scope    An object that becomes the scope of the handler
6490          * @param {boolean}  options
6491          */
6492         onTextResize : function(fn, scope, options){
6493             if(!textEvent){
6494                 textEvent = new Roo.util.Event();
6495                 var textEl = new Roo.Element(document.createElement('div'));
6496                 textEl.dom.className = 'x-text-resize';
6497                 textEl.dom.innerHTML = 'X';
6498                 textEl.appendTo(document.body);
6499                 textSize = textEl.dom.offsetHeight;
6500                 setInterval(function(){
6501                     if(textEl.dom.offsetHeight != textSize){
6502                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6503                     }
6504                 }, this.textResizeInterval);
6505             }
6506             textEvent.addListener(fn, scope, options);
6507         },
6508
6509         /**
6510          * Removes the passed window resize listener.
6511          * @param {Function} fn        The method the event invokes
6512          * @param {Object}   scope    The scope of handler
6513          */
6514         removeResizeListener : function(fn, scope){
6515             if(resizeEvent){
6516                 resizeEvent.removeListener(fn, scope);
6517             }
6518         },
6519
6520         // private
6521         fireResize : function(){
6522             if(resizeEvent){
6523                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6524             }   
6525         },
6526         /**
6527          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6528          */
6529         ieDeferSrc : false,
6530         /**
6531          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6532          */
6533         textResizeInterval : 50
6534     };
6535     
6536     /**
6537      * Fix for doc tools
6538      * @scopeAlias pub=Roo.EventManager
6539      */
6540     
6541      /**
6542      * Appends an event handler to an element (shorthand for addListener)
6543      * @param {String/HTMLElement}   element        The html element or id to assign the
6544      * @param {String}   eventName The type of event to listen for
6545      * @param {Function} handler The method the event invokes
6546      * @param {Object}   scope (optional) The scope in which to execute the handler
6547      * function. The handler function's "this" context.
6548      * @param {Object}   options (optional) An object containing handler configuration
6549      * properties. This may contain any of the following properties:<ul>
6550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6551      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6552      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6553      * <li>preventDefault {Boolean} True to prevent the default action</li>
6554      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6555      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6556      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6557      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6558      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6559      * by the specified number of milliseconds. If the event fires again within that time, the original
6560      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6561      * </ul><br>
6562      * <p>
6563      * <b>Combining Options</b><br>
6564      * Using the options argument, it is possible to combine different types of listeners:<br>
6565      * <br>
6566      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6567      * Code:<pre><code>
6568 el.on('click', this.onClick, this, {
6569     single: true,
6570     delay: 100,
6571     stopEvent : true,
6572     forumId: 4
6573 });</code></pre>
6574      * <p>
6575      * <b>Attaching multiple handlers in 1 call</b><br>
6576       * The method also allows for a single argument to be passed which is a config object containing properties
6577      * which specify multiple handlers.
6578      * <p>
6579      * Code:<pre><code>
6580 el.on({
6581     'click' : {
6582         fn: this.onClick
6583         scope: this,
6584         delay: 100
6585     },
6586     'mouseover' : {
6587         fn: this.onMouseOver
6588         scope: this
6589     },
6590     'mouseout' : {
6591         fn: this.onMouseOut
6592         scope: this
6593     }
6594 });</code></pre>
6595      * <p>
6596      * Or a shorthand syntax:<br>
6597      * Code:<pre><code>
6598 el.on({
6599     'click' : this.onClick,
6600     'mouseover' : this.onMouseOver,
6601     'mouseout' : this.onMouseOut
6602     scope: this
6603 });</code></pre>
6604      */
6605     pub.on = pub.addListener;
6606     pub.un = pub.removeListener;
6607
6608     pub.stoppedMouseDownEvent = new Roo.util.Event();
6609     return pub;
6610 }();
6611 /**
6612   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6613   * @param {Function} fn        The method the event invokes
6614   * @param {Object}   scope    An  object that becomes the scope of the handler
6615   * @param {boolean}  override If true, the obj passed in becomes
6616   *                             the execution scope of the listener
6617   * @member Roo
6618   * @method onReady
6619  */
6620 Roo.onReady = Roo.EventManager.onDocumentReady;
6621
6622 Roo.onReady(function(){
6623     var bd = Roo.get(document.body);
6624     if(!bd){ return; }
6625
6626     var cls = [
6627             Roo.isIE ? "roo-ie"
6628             : Roo.isGecko ? "roo-gecko"
6629             : Roo.isOpera ? "roo-opera"
6630             : Roo.isSafari ? "roo-safari" : ""];
6631
6632     if(Roo.isMac){
6633         cls.push("roo-mac");
6634     }
6635     if(Roo.isLinux){
6636         cls.push("roo-linux");
6637     }
6638     if(Roo.isIOS){
6639         cls.push("roo-ios");
6640     }
6641     if(Roo.isTouch){
6642         cls.push("roo-touch");
6643     }
6644     if(Roo.isBorderBox){
6645         cls.push('roo-border-box');
6646     }
6647     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6648         var p = bd.dom.parentNode;
6649         if(p){
6650             p.className += ' roo-strict';
6651         }
6652     }
6653     bd.addClass(cls.join(' '));
6654 });
6655
6656 /**
6657  * @class Roo.EventObject
6658  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6659  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6660  * Example:
6661  * <pre><code>
6662  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6663     e.preventDefault();
6664     var target = e.getTarget();
6665     ...
6666  }
6667  var myDiv = Roo.get("myDiv");
6668  myDiv.on("click", handleClick);
6669  //or
6670  Roo.EventManager.on("myDiv", 'click', handleClick);
6671  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6672  </code></pre>
6673  * @singleton
6674  */
6675 Roo.EventObject = function(){
6676     
6677     var E = Roo.lib.Event;
6678     
6679     // safari keypress events for special keys return bad keycodes
6680     var safariKeys = {
6681         63234 : 37, // left
6682         63235 : 39, // right
6683         63232 : 38, // up
6684         63233 : 40, // down
6685         63276 : 33, // page up
6686         63277 : 34, // page down
6687         63272 : 46, // delete
6688         63273 : 36, // home
6689         63275 : 35  // end
6690     };
6691
6692     // normalize button clicks
6693     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6694                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6695
6696     Roo.EventObjectImpl = function(e){
6697         if(e){
6698             this.setEvent(e.browserEvent || e);
6699         }
6700     };
6701     Roo.EventObjectImpl.prototype = {
6702         /**
6703          * Used to fix doc tools.
6704          * @scope Roo.EventObject.prototype
6705          */
6706             
6707
6708         
6709         
6710         /** The normal browser event */
6711         browserEvent : null,
6712         /** The button pressed in a mouse event */
6713         button : -1,
6714         /** True if the shift key was down during the event */
6715         shiftKey : false,
6716         /** True if the control key was down during the event */
6717         ctrlKey : false,
6718         /** True if the alt key was down during the event */
6719         altKey : false,
6720
6721         /** Key constant 
6722         * @type Number */
6723         BACKSPACE : 8,
6724         /** Key constant 
6725         * @type Number */
6726         TAB : 9,
6727         /** Key constant 
6728         * @type Number */
6729         RETURN : 13,
6730         /** Key constant 
6731         * @type Number */
6732         ENTER : 13,
6733         /** Key constant 
6734         * @type Number */
6735         SHIFT : 16,
6736         /** Key constant 
6737         * @type Number */
6738         CONTROL : 17,
6739         /** Key constant 
6740         * @type Number */
6741         ESC : 27,
6742         /** Key constant 
6743         * @type Number */
6744         SPACE : 32,
6745         /** Key constant 
6746         * @type Number */
6747         PAGEUP : 33,
6748         /** Key constant 
6749         * @type Number */
6750         PAGEDOWN : 34,
6751         /** Key constant 
6752         * @type Number */
6753         END : 35,
6754         /** Key constant 
6755         * @type Number */
6756         HOME : 36,
6757         /** Key constant 
6758         * @type Number */
6759         LEFT : 37,
6760         /** Key constant 
6761         * @type Number */
6762         UP : 38,
6763         /** Key constant 
6764         * @type Number */
6765         RIGHT : 39,
6766         /** Key constant 
6767         * @type Number */
6768         DOWN : 40,
6769         /** Key constant 
6770         * @type Number */
6771         DELETE : 46,
6772         /** Key constant 
6773         * @type Number */
6774         F5 : 116,
6775
6776            /** @private */
6777         setEvent : function(e){
6778             if(e == this || (e && e.browserEvent)){ // already wrapped
6779                 return e;
6780             }
6781             this.browserEvent = e;
6782             if(e){
6783                 // normalize buttons
6784                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6785                 if(e.type == 'click' && this.button == -1){
6786                     this.button = 0;
6787                 }
6788                 this.type = e.type;
6789                 this.shiftKey = e.shiftKey;
6790                 // mac metaKey behaves like ctrlKey
6791                 this.ctrlKey = e.ctrlKey || e.metaKey;
6792                 this.altKey = e.altKey;
6793                 // in getKey these will be normalized for the mac
6794                 this.keyCode = e.keyCode;
6795                 // keyup warnings on firefox.
6796                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6797                 // cache the target for the delayed and or buffered events
6798                 this.target = E.getTarget(e);
6799                 // same for XY
6800                 this.xy = E.getXY(e);
6801             }else{
6802                 this.button = -1;
6803                 this.shiftKey = false;
6804                 this.ctrlKey = false;
6805                 this.altKey = false;
6806                 this.keyCode = 0;
6807                 this.charCode =0;
6808                 this.target = null;
6809                 this.xy = [0, 0];
6810             }
6811             return this;
6812         },
6813
6814         /**
6815          * Stop the event (preventDefault and stopPropagation)
6816          */
6817         stopEvent : function(){
6818             if(this.browserEvent){
6819                 if(this.browserEvent.type == 'mousedown'){
6820                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6821                 }
6822                 E.stopEvent(this.browserEvent);
6823             }
6824         },
6825
6826         /**
6827          * Prevents the browsers default handling of the event.
6828          */
6829         preventDefault : function(){
6830             if(this.browserEvent){
6831                 E.preventDefault(this.browserEvent);
6832             }
6833         },
6834
6835         /** @private */
6836         isNavKeyPress : function(){
6837             var k = this.keyCode;
6838             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6839             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6840         },
6841
6842         isSpecialKey : function(){
6843             var k = this.keyCode;
6844             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6845             (k == 16) || (k == 17) ||
6846             (k >= 18 && k <= 20) ||
6847             (k >= 33 && k <= 35) ||
6848             (k >= 36 && k <= 39) ||
6849             (k >= 44 && k <= 45);
6850         },
6851         /**
6852          * Cancels bubbling of the event.
6853          */
6854         stopPropagation : function(){
6855             if(this.browserEvent){
6856                 if(this.type == 'mousedown'){
6857                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6858                 }
6859                 E.stopPropagation(this.browserEvent);
6860             }
6861         },
6862
6863         /**
6864          * Gets the key code for the event.
6865          * @return {Number}
6866          */
6867         getCharCode : function(){
6868             return this.charCode || this.keyCode;
6869         },
6870
6871         /**
6872          * Returns a normalized keyCode for the event.
6873          * @return {Number} The key code
6874          */
6875         getKey : function(){
6876             var k = this.keyCode || this.charCode;
6877             return Roo.isSafari ? (safariKeys[k] || k) : k;
6878         },
6879
6880         /**
6881          * Gets the x coordinate of the event.
6882          * @return {Number}
6883          */
6884         getPageX : function(){
6885             return this.xy[0];
6886         },
6887
6888         /**
6889          * Gets the y coordinate of the event.
6890          * @return {Number}
6891          */
6892         getPageY : function(){
6893             return this.xy[1];
6894         },
6895
6896         /**
6897          * Gets the time of the event.
6898          * @return {Number}
6899          */
6900         getTime : function(){
6901             if(this.browserEvent){
6902                 return E.getTime(this.browserEvent);
6903             }
6904             return null;
6905         },
6906
6907         /**
6908          * Gets the page coordinates of the event.
6909          * @return {Array} The xy values like [x, y]
6910          */
6911         getXY : function(){
6912             return this.xy;
6913         },
6914
6915         /**
6916          * Gets the target for the event.
6917          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6918          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6919                 search as a number or element (defaults to 10 || document.body)
6920          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6921          * @return {HTMLelement}
6922          */
6923         getTarget : function(selector, maxDepth, returnEl){
6924             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6925         },
6926         /**
6927          * Gets the related target.
6928          * @return {HTMLElement}
6929          */
6930         getRelatedTarget : function(){
6931             if(this.browserEvent){
6932                 return E.getRelatedTarget(this.browserEvent);
6933             }
6934             return null;
6935         },
6936
6937         /**
6938          * Normalizes mouse wheel delta across browsers
6939          * @return {Number} The delta
6940          */
6941         getWheelDelta : function(){
6942             var e = this.browserEvent;
6943             var delta = 0;
6944             if(e.wheelDelta){ /* IE/Opera. */
6945                 delta = e.wheelDelta/120;
6946             }else if(e.detail){ /* Mozilla case. */
6947                 delta = -e.detail/3;
6948             }
6949             return delta;
6950         },
6951
6952         /**
6953          * Returns true if the control, meta, shift or alt key was pressed during this event.
6954          * @return {Boolean}
6955          */
6956         hasModifier : function(){
6957             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6958         },
6959
6960         /**
6961          * Returns true if the target of this event equals el or is a child of el
6962          * @param {String/HTMLElement/Element} el
6963          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6964          * @return {Boolean}
6965          */
6966         within : function(el, related){
6967             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6968             return t && Roo.fly(el).contains(t);
6969         },
6970
6971         getPoint : function(){
6972             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6973         }
6974     };
6975
6976     return new Roo.EventObjectImpl();
6977 }();
6978             
6979     /*
6980  * Based on:
6981  * Ext JS Library 1.1.1
6982  * Copyright(c) 2006-2007, Ext JS, LLC.
6983  *
6984  * Originally Released Under LGPL - original licence link has changed is not relivant.
6985  *
6986  * Fork - LGPL
6987  * <script type="text/javascript">
6988  */
6989
6990  
6991 // was in Composite Element!??!?!
6992  
6993 (function(){
6994     var D = Roo.lib.Dom;
6995     var E = Roo.lib.Event;
6996     var A = Roo.lib.Anim;
6997
6998     // local style camelizing for speed
6999     var propCache = {};
7000     var camelRe = /(-[a-z])/gi;
7001     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7002     var view = document.defaultView;
7003
7004 /**
7005  * @class Roo.Element
7006  * Represents an Element in the DOM.<br><br>
7007  * Usage:<br>
7008 <pre><code>
7009 var el = Roo.get("my-div");
7010
7011 // or with getEl
7012 var el = getEl("my-div");
7013
7014 // or with a DOM element
7015 var el = Roo.get(myDivElement);
7016 </code></pre>
7017  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7018  * each call instead of constructing a new one.<br><br>
7019  * <b>Animations</b><br />
7020  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7021  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7022 <pre>
7023 Option    Default   Description
7024 --------- --------  ---------------------------------------------
7025 duration  .35       The duration of the animation in seconds
7026 easing    easeOut   The YUI easing method
7027 callback  none      A function to execute when the anim completes
7028 scope     this      The scope (this) of the callback function
7029 </pre>
7030 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7031 * manipulate the animation. Here's an example:
7032 <pre><code>
7033 var el = Roo.get("my-div");
7034
7035 // no animation
7036 el.setWidth(100);
7037
7038 // default animation
7039 el.setWidth(100, true);
7040
7041 // animation with some options set
7042 el.setWidth(100, {
7043     duration: 1,
7044     callback: this.foo,
7045     scope: this
7046 });
7047
7048 // using the "anim" property to get the Anim object
7049 var opt = {
7050     duration: 1,
7051     callback: this.foo,
7052     scope: this
7053 };
7054 el.setWidth(100, opt);
7055 ...
7056 if(opt.anim.isAnimated()){
7057     opt.anim.stop();
7058 }
7059 </code></pre>
7060 * <b> Composite (Collections of) Elements</b><br />
7061  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7062  * @constructor Create a new Element directly.
7063  * @param {String/HTMLElement} element
7064  * @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).
7065  */
7066     Roo.Element = function(element, forceNew){
7067         var dom = typeof element == "string" ?
7068                 document.getElementById(element) : element;
7069         if(!dom){ // invalid id/element
7070             return null;
7071         }
7072         var id = dom.id;
7073         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7074             return Roo.Element.cache[id];
7075         }
7076
7077         /**
7078          * The DOM element
7079          * @type HTMLElement
7080          */
7081         this.dom = dom;
7082
7083         /**
7084          * The DOM element ID
7085          * @type String
7086          */
7087         this.id = id || Roo.id(dom);
7088     };
7089
7090     var El = Roo.Element;
7091
7092     El.prototype = {
7093         /**
7094          * The element's default display mode  (defaults to "")
7095          * @type String
7096          */
7097         originalDisplay : "",
7098
7099         visibilityMode : 1,
7100         /**
7101          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7102          * @type String
7103          */
7104         defaultUnit : "px",
7105         
7106         /**
7107          * Sets the element's visibility mode. When setVisible() is called it
7108          * will use this to determine whether to set the visibility or the display property.
7109          * @param visMode Element.VISIBILITY or Element.DISPLAY
7110          * @return {Roo.Element} this
7111          */
7112         setVisibilityMode : function(visMode){
7113             this.visibilityMode = visMode;
7114             return this;
7115         },
7116         /**
7117          * Convenience method for setVisibilityMode(Element.DISPLAY)
7118          * @param {String} display (optional) What to set display to when visible
7119          * @return {Roo.Element} this
7120          */
7121         enableDisplayMode : function(display){
7122             this.setVisibilityMode(El.DISPLAY);
7123             if(typeof display != "undefined") { this.originalDisplay = display; }
7124             return this;
7125         },
7126
7127         /**
7128          * 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)
7129          * @param {String} selector The simple selector to test
7130          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7131                 search as a number or element (defaults to 10 || document.body)
7132          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7133          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7134          */
7135         findParent : function(simpleSelector, maxDepth, returnEl){
7136             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7137             maxDepth = maxDepth || 50;
7138             if(typeof maxDepth != "number"){
7139                 stopEl = Roo.getDom(maxDepth);
7140                 maxDepth = 10;
7141             }
7142             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7143                 if(dq.is(p, simpleSelector)){
7144                     return returnEl ? Roo.get(p) : p;
7145                 }
7146                 depth++;
7147                 p = p.parentNode;
7148             }
7149             return null;
7150         },
7151
7152
7153         /**
7154          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7155          * @param {String} selector The simple selector to test
7156          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7157                 search as a number or element (defaults to 10 || document.body)
7158          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7159          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7160          */
7161         findParentNode : function(simpleSelector, maxDepth, returnEl){
7162             var p = Roo.fly(this.dom.parentNode, '_internal');
7163             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7164         },
7165         
7166         /**
7167          * Looks at  the scrollable parent element
7168          */
7169         findScrollableParent : function(){
7170             
7171             var overflowRegex = /(auto|scroll)/;
7172             
7173             if(this.getStyle('position') === 'fixed'){
7174                 return Roo.get(document.body);
7175             }
7176             
7177             var excludeStaticParent = this.getStyle('position') === "absolute";
7178             
7179             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7180                 
7181                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7182                     continue;
7183                 }
7184                 
7185                 if (
7186                         parent.dom.nodeName.toLowerCase() == 'body' ||
7187                         overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))
7188                 ){
7189                     return parent;
7190                 }
7191             }
7192             
7193             return Roo.get(document.body);
7194         },
7195
7196         /**
7197          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7198          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7199          * @param {String} selector The simple selector to test
7200          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7201                 search as a number or element (defaults to 10 || document.body)
7202          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7203          */
7204         up : function(simpleSelector, maxDepth){
7205             return this.findParentNode(simpleSelector, maxDepth, true);
7206         },
7207
7208
7209
7210         /**
7211          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7212          * @param {String} selector The simple selector to test
7213          * @return {Boolean} True if this element matches the selector, else false
7214          */
7215         is : function(simpleSelector){
7216             return Roo.DomQuery.is(this.dom, simpleSelector);
7217         },
7218
7219         /**
7220          * Perform animation on this element.
7221          * @param {Object} args The YUI animation control args
7222          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7223          * @param {Function} onComplete (optional) Function to call when animation completes
7224          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7225          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7226          * @return {Roo.Element} this
7227          */
7228         animate : function(args, duration, onComplete, easing, animType){
7229             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7230             return this;
7231         },
7232
7233         /*
7234          * @private Internal animation call
7235          */
7236         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7237             animType = animType || 'run';
7238             opt = opt || {};
7239             var anim = Roo.lib.Anim[animType](
7240                 this.dom, args,
7241                 (opt.duration || defaultDur) || .35,
7242                 (opt.easing || defaultEase) || 'easeOut',
7243                 function(){
7244                     Roo.callback(cb, this);
7245                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7246                 },
7247                 this
7248             );
7249             opt.anim = anim;
7250             return anim;
7251         },
7252
7253         // private legacy anim prep
7254         preanim : function(a, i){
7255             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7256         },
7257
7258         /**
7259          * Removes worthless text nodes
7260          * @param {Boolean} forceReclean (optional) By default the element
7261          * keeps track if it has been cleaned already so
7262          * you can call this over and over. However, if you update the element and
7263          * need to force a reclean, you can pass true.
7264          */
7265         clean : function(forceReclean){
7266             if(this.isCleaned && forceReclean !== true){
7267                 return this;
7268             }
7269             var ns = /\S/;
7270             var d = this.dom, n = d.firstChild, ni = -1;
7271             while(n){
7272                 var nx = n.nextSibling;
7273                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7274                     d.removeChild(n);
7275                 }else{
7276                     n.nodeIndex = ++ni;
7277                 }
7278                 n = nx;
7279             }
7280             this.isCleaned = true;
7281             return this;
7282         },
7283
7284         // private
7285         calcOffsetsTo : function(el){
7286             el = Roo.get(el);
7287             var d = el.dom;
7288             var restorePos = false;
7289             if(el.getStyle('position') == 'static'){
7290                 el.position('relative');
7291                 restorePos = true;
7292             }
7293             var x = 0, y =0;
7294             var op = this.dom;
7295             while(op && op != d && op.tagName != 'HTML'){
7296                 x+= op.offsetLeft;
7297                 y+= op.offsetTop;
7298                 op = op.offsetParent;
7299             }
7300             if(restorePos){
7301                 el.position('static');
7302             }
7303             return [x, y];
7304         },
7305
7306         /**
7307          * Scrolls this element into view within the passed container.
7308          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7309          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7310          * @return {Roo.Element} this
7311          */
7312         scrollIntoView : function(container, hscroll){
7313             var c = Roo.getDom(container) || document.body;
7314             var el = this.dom;
7315
7316             var o = this.calcOffsetsTo(c),
7317                 l = o[0],
7318                 t = o[1],
7319                 b = t+el.offsetHeight,
7320                 r = l+el.offsetWidth;
7321
7322             var ch = c.clientHeight;
7323             var ct = parseInt(c.scrollTop, 10);
7324             var cl = parseInt(c.scrollLeft, 10);
7325             var cb = ct + ch;
7326             var cr = cl + c.clientWidth;
7327
7328             if(t < ct){
7329                 c.scrollTop = t;
7330             }else if(b > cb){
7331                 c.scrollTop = b-ch;
7332             }
7333
7334             if(hscroll !== false){
7335                 if(l < cl){
7336                     c.scrollLeft = l;
7337                 }else if(r > cr){
7338                     c.scrollLeft = r-c.clientWidth;
7339                 }
7340             }
7341             return this;
7342         },
7343
7344         // private
7345         scrollChildIntoView : function(child, hscroll){
7346             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7347         },
7348
7349         /**
7350          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7351          * the new height may not be available immediately.
7352          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7353          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7354          * @param {Function} onComplete (optional) Function to call when animation completes
7355          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7356          * @return {Roo.Element} this
7357          */
7358         autoHeight : function(animate, duration, onComplete, easing){
7359             var oldHeight = this.getHeight();
7360             this.clip();
7361             this.setHeight(1); // force clipping
7362             setTimeout(function(){
7363                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7364                 if(!animate){
7365                     this.setHeight(height);
7366                     this.unclip();
7367                     if(typeof onComplete == "function"){
7368                         onComplete();
7369                     }
7370                 }else{
7371                     this.setHeight(oldHeight); // restore original height
7372                     this.setHeight(height, animate, duration, function(){
7373                         this.unclip();
7374                         if(typeof onComplete == "function") { onComplete(); }
7375                     }.createDelegate(this), easing);
7376                 }
7377             }.createDelegate(this), 0);
7378             return this;
7379         },
7380
7381         /**
7382          * Returns true if this element is an ancestor of the passed element
7383          * @param {HTMLElement/String} el The element to check
7384          * @return {Boolean} True if this element is an ancestor of el, else false
7385          */
7386         contains : function(el){
7387             if(!el){return false;}
7388             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7389         },
7390
7391         /**
7392          * Checks whether the element is currently visible using both visibility and display properties.
7393          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7394          * @return {Boolean} True if the element is currently visible, else false
7395          */
7396         isVisible : function(deep) {
7397             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7398             if(deep !== true || !vis){
7399                 return vis;
7400             }
7401             var p = this.dom.parentNode;
7402             while(p && p.tagName.toLowerCase() != "body"){
7403                 if(!Roo.fly(p, '_isVisible').isVisible()){
7404                     return false;
7405                 }
7406                 p = p.parentNode;
7407             }
7408             return true;
7409         },
7410
7411         /**
7412          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7413          * @param {String} selector The CSS selector
7414          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7415          * @return {CompositeElement/CompositeElementLite} The composite element
7416          */
7417         select : function(selector, unique){
7418             return El.select(selector, unique, this.dom);
7419         },
7420
7421         /**
7422          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7423          * @param {String} selector The CSS selector
7424          * @return {Array} An array of the matched nodes
7425          */
7426         query : function(selector, unique){
7427             return Roo.DomQuery.select(selector, this.dom);
7428         },
7429
7430         /**
7431          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7432          * @param {String} selector The CSS selector
7433          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7434          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7435          */
7436         child : function(selector, returnDom){
7437             var n = Roo.DomQuery.selectNode(selector, this.dom);
7438             return returnDom ? n : Roo.get(n);
7439         },
7440
7441         /**
7442          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7443          * @param {String} selector The CSS selector
7444          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7445          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7446          */
7447         down : function(selector, returnDom){
7448             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7449             return returnDom ? n : Roo.get(n);
7450         },
7451
7452         /**
7453          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7454          * @param {String} group The group the DD object is member of
7455          * @param {Object} config The DD config object
7456          * @param {Object} overrides An object containing methods to override/implement on the DD object
7457          * @return {Roo.dd.DD} The DD object
7458          */
7459         initDD : function(group, config, overrides){
7460             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7461             return Roo.apply(dd, overrides);
7462         },
7463
7464         /**
7465          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7466          * @param {String} group The group the DDProxy object is member of
7467          * @param {Object} config The DDProxy config object
7468          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7469          * @return {Roo.dd.DDProxy} The DDProxy object
7470          */
7471         initDDProxy : function(group, config, overrides){
7472             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7473             return Roo.apply(dd, overrides);
7474         },
7475
7476         /**
7477          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7478          * @param {String} group The group the DDTarget object is member of
7479          * @param {Object} config The DDTarget config object
7480          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7481          * @return {Roo.dd.DDTarget} The DDTarget object
7482          */
7483         initDDTarget : function(group, config, overrides){
7484             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7485             return Roo.apply(dd, overrides);
7486         },
7487
7488         /**
7489          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7490          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7491          * @param {Boolean} visible Whether the element is visible
7492          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7493          * @return {Roo.Element} this
7494          */
7495          setVisible : function(visible, animate){
7496             if(!animate || !A){
7497                 if(this.visibilityMode == El.DISPLAY){
7498                     this.setDisplayed(visible);
7499                 }else{
7500                     this.fixDisplay();
7501                     this.dom.style.visibility = visible ? "visible" : "hidden";
7502                 }
7503             }else{
7504                 // closure for composites
7505                 var dom = this.dom;
7506                 var visMode = this.visibilityMode;
7507                 if(visible){
7508                     this.setOpacity(.01);
7509                     this.setVisible(true);
7510                 }
7511                 this.anim({opacity: { to: (visible?1:0) }},
7512                       this.preanim(arguments, 1),
7513                       null, .35, 'easeIn', function(){
7514                          if(!visible){
7515                              if(visMode == El.DISPLAY){
7516                                  dom.style.display = "none";
7517                              }else{
7518                                  dom.style.visibility = "hidden";
7519                              }
7520                              Roo.get(dom).setOpacity(1);
7521                          }
7522                      });
7523             }
7524             return this;
7525         },
7526
7527         /**
7528          * Returns true if display is not "none"
7529          * @return {Boolean}
7530          */
7531         isDisplayed : function() {
7532             return this.getStyle("display") != "none";
7533         },
7534
7535         /**
7536          * Toggles the element's visibility or display, depending on visibility mode.
7537          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7538          * @return {Roo.Element} this
7539          */
7540         toggle : function(animate){
7541             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7542             return this;
7543         },
7544
7545         /**
7546          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7547          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7548          * @return {Roo.Element} this
7549          */
7550         setDisplayed : function(value) {
7551             if(typeof value == "boolean"){
7552                value = value ? this.originalDisplay : "none";
7553             }
7554             this.setStyle("display", value);
7555             return this;
7556         },
7557
7558         /**
7559          * Tries to focus the element. Any exceptions are caught and ignored.
7560          * @return {Roo.Element} this
7561          */
7562         focus : function() {
7563             try{
7564                 this.dom.focus();
7565             }catch(e){}
7566             return this;
7567         },
7568
7569         /**
7570          * Tries to blur the element. Any exceptions are caught and ignored.
7571          * @return {Roo.Element} this
7572          */
7573         blur : function() {
7574             try{
7575                 this.dom.blur();
7576             }catch(e){}
7577             return this;
7578         },
7579
7580         /**
7581          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7582          * @param {String/Array} className The CSS class to add, or an array of classes
7583          * @return {Roo.Element} this
7584          */
7585         addClass : function(className){
7586             if(className instanceof Array){
7587                 for(var i = 0, len = className.length; i < len; i++) {
7588                     this.addClass(className[i]);
7589                 }
7590             }else{
7591                 if(className && !this.hasClass(className)){
7592                     this.dom.className = this.dom.className + " " + className;
7593                 }
7594             }
7595             return this;
7596         },
7597
7598         /**
7599          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7600          * @param {String/Array} className The CSS class to add, or an array of classes
7601          * @return {Roo.Element} this
7602          */
7603         radioClass : function(className){
7604             var siblings = this.dom.parentNode.childNodes;
7605             for(var i = 0; i < siblings.length; i++) {
7606                 var s = siblings[i];
7607                 if(s.nodeType == 1){
7608                     Roo.get(s).removeClass(className);
7609                 }
7610             }
7611             this.addClass(className);
7612             return this;
7613         },
7614
7615         /**
7616          * Removes one or more CSS classes from the element.
7617          * @param {String/Array} className The CSS class to remove, or an array of classes
7618          * @return {Roo.Element} this
7619          */
7620         removeClass : function(className){
7621             if(!className || !this.dom.className){
7622                 return this;
7623             }
7624             if(className instanceof Array){
7625                 for(var i = 0, len = className.length; i < len; i++) {
7626                     this.removeClass(className[i]);
7627                 }
7628             }else{
7629                 if(this.hasClass(className)){
7630                     var re = this.classReCache[className];
7631                     if (!re) {
7632                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7633                        this.classReCache[className] = re;
7634                     }
7635                     this.dom.className =
7636                         this.dom.className.replace(re, " ");
7637                 }
7638             }
7639             return this;
7640         },
7641
7642         // private
7643         classReCache: {},
7644
7645         /**
7646          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7647          * @param {String} className The CSS class to toggle
7648          * @return {Roo.Element} this
7649          */
7650         toggleClass : function(className){
7651             if(this.hasClass(className)){
7652                 this.removeClass(className);
7653             }else{
7654                 this.addClass(className);
7655             }
7656             return this;
7657         },
7658
7659         /**
7660          * Checks if the specified CSS class exists on this element's DOM node.
7661          * @param {String} className The CSS class to check for
7662          * @return {Boolean} True if the class exists, else false
7663          */
7664         hasClass : function(className){
7665             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7666         },
7667
7668         /**
7669          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7670          * @param {String} oldClassName The CSS class to replace
7671          * @param {String} newClassName The replacement CSS class
7672          * @return {Roo.Element} this
7673          */
7674         replaceClass : function(oldClassName, newClassName){
7675             this.removeClass(oldClassName);
7676             this.addClass(newClassName);
7677             return this;
7678         },
7679
7680         /**
7681          * Returns an object with properties matching the styles requested.
7682          * For example, el.getStyles('color', 'font-size', 'width') might return
7683          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7684          * @param {String} style1 A style name
7685          * @param {String} style2 A style name
7686          * @param {String} etc.
7687          * @return {Object} The style object
7688          */
7689         getStyles : function(){
7690             var a = arguments, len = a.length, r = {};
7691             for(var i = 0; i < len; i++){
7692                 r[a[i]] = this.getStyle(a[i]);
7693             }
7694             return r;
7695         },
7696
7697         /**
7698          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7699          * @param {String} property The style property whose value is returned.
7700          * @return {String} The current value of the style property for this element.
7701          */
7702         getStyle : function(){
7703             return view && view.getComputedStyle ?
7704                 function(prop){
7705                     var el = this.dom, v, cs, camel;
7706                     if(prop == 'float'){
7707                         prop = "cssFloat";
7708                     }
7709                     if(el.style && (v = el.style[prop])){
7710                         return v;
7711                     }
7712                     if(cs = view.getComputedStyle(el, "")){
7713                         if(!(camel = propCache[prop])){
7714                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7715                         }
7716                         return cs[camel];
7717                     }
7718                     return null;
7719                 } :
7720                 function(prop){
7721                     var el = this.dom, v, cs, camel;
7722                     if(prop == 'opacity'){
7723                         if(typeof el.style.filter == 'string'){
7724                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7725                             if(m){
7726                                 var fv = parseFloat(m[1]);
7727                                 if(!isNaN(fv)){
7728                                     return fv ? fv / 100 : 0;
7729                                 }
7730                             }
7731                         }
7732                         return 1;
7733                     }else if(prop == 'float'){
7734                         prop = "styleFloat";
7735                     }
7736                     if(!(camel = propCache[prop])){
7737                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7738                     }
7739                     if(v = el.style[camel]){
7740                         return v;
7741                     }
7742                     if(cs = el.currentStyle){
7743                         return cs[camel];
7744                     }
7745                     return null;
7746                 };
7747         }(),
7748
7749         /**
7750          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7751          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7752          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7753          * @return {Roo.Element} this
7754          */
7755         setStyle : function(prop, value){
7756             if(typeof prop == "string"){
7757                 
7758                 if (prop == 'float') {
7759                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7760                     return this;
7761                 }
7762                 
7763                 var camel;
7764                 if(!(camel = propCache[prop])){
7765                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7766                 }
7767                 
7768                 if(camel == 'opacity') {
7769                     this.setOpacity(value);
7770                 }else{
7771                     this.dom.style[camel] = value;
7772                 }
7773             }else{
7774                 for(var style in prop){
7775                     if(typeof prop[style] != "function"){
7776                        this.setStyle(style, prop[style]);
7777                     }
7778                 }
7779             }
7780             return this;
7781         },
7782
7783         /**
7784          * More flexible version of {@link #setStyle} for setting style properties.
7785          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7786          * a function which returns such a specification.
7787          * @return {Roo.Element} this
7788          */
7789         applyStyles : function(style){
7790             Roo.DomHelper.applyStyles(this.dom, style);
7791             return this;
7792         },
7793
7794         /**
7795           * 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).
7796           * @return {Number} The X position of the element
7797           */
7798         getX : function(){
7799             return D.getX(this.dom);
7800         },
7801
7802         /**
7803           * 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).
7804           * @return {Number} The Y position of the element
7805           */
7806         getY : function(){
7807             return D.getY(this.dom);
7808         },
7809
7810         /**
7811           * 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).
7812           * @return {Array} The XY position of the element
7813           */
7814         getXY : function(){
7815             return D.getXY(this.dom);
7816         },
7817
7818         /**
7819          * 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).
7820          * @param {Number} The X position of the element
7821          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7822          * @return {Roo.Element} this
7823          */
7824         setX : function(x, animate){
7825             if(!animate || !A){
7826                 D.setX(this.dom, x);
7827             }else{
7828                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7829             }
7830             return this;
7831         },
7832
7833         /**
7834          * 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).
7835          * @param {Number} The Y position of the element
7836          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7837          * @return {Roo.Element} this
7838          */
7839         setY : function(y, animate){
7840             if(!animate || !A){
7841                 D.setY(this.dom, y);
7842             }else{
7843                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7850          * @param {String} left The left CSS property value
7851          * @return {Roo.Element} this
7852          */
7853         setLeft : function(left){
7854             this.setStyle("left", this.addUnits(left));
7855             return this;
7856         },
7857
7858         /**
7859          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7860          * @param {String} top The top CSS property value
7861          * @return {Roo.Element} this
7862          */
7863         setTop : function(top){
7864             this.setStyle("top", this.addUnits(top));
7865             return this;
7866         },
7867
7868         /**
7869          * Sets the element's CSS right style.
7870          * @param {String} right The right CSS property value
7871          * @return {Roo.Element} this
7872          */
7873         setRight : function(right){
7874             this.setStyle("right", this.addUnits(right));
7875             return this;
7876         },
7877
7878         /**
7879          * Sets the element's CSS bottom style.
7880          * @param {String} bottom The bottom CSS property value
7881          * @return {Roo.Element} this
7882          */
7883         setBottom : function(bottom){
7884             this.setStyle("bottom", this.addUnits(bottom));
7885             return this;
7886         },
7887
7888         /**
7889          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7890          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7891          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7892          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7893          * @return {Roo.Element} this
7894          */
7895         setXY : function(pos, animate){
7896             if(!animate || !A){
7897                 D.setXY(this.dom, pos);
7898             }else{
7899                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7900             }
7901             return this;
7902         },
7903
7904         /**
7905          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7906          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7907          * @param {Number} x X value for new position (coordinates are page-based)
7908          * @param {Number} y Y value 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         setLocation : function(x, y, animate){
7913             this.setXY([x, y], this.preanim(arguments, 2));
7914             return this;
7915         },
7916
7917         /**
7918          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7919          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7920          * @param {Number} x X value for new position (coordinates are page-based)
7921          * @param {Number} y Y value for new position (coordinates are page-based)
7922          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7923          * @return {Roo.Element} this
7924          */
7925         moveTo : function(x, y, animate){
7926             this.setXY([x, y], this.preanim(arguments, 2));
7927             return this;
7928         },
7929
7930         /**
7931          * Returns the region of the given element.
7932          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7933          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7934          */
7935         getRegion : function(){
7936             return D.getRegion(this.dom);
7937         },
7938
7939         /**
7940          * Returns the offset height of the element
7941          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7942          * @return {Number} The element's height
7943          */
7944         getHeight : function(contentHeight){
7945             var h = this.dom.offsetHeight || 0;
7946             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7947         },
7948
7949         /**
7950          * Returns the offset width of the element
7951          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7952          * @return {Number} The element's width
7953          */
7954         getWidth : function(contentWidth){
7955             var w = this.dom.offsetWidth || 0;
7956             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7957         },
7958
7959         /**
7960          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7961          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7962          * if a height has not been set using CSS.
7963          * @return {Number}
7964          */
7965         getComputedHeight : function(){
7966             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7967             if(!h){
7968                 h = parseInt(this.getStyle('height'), 10) || 0;
7969                 if(!this.isBorderBox()){
7970                     h += this.getFrameWidth('tb');
7971                 }
7972             }
7973             return h;
7974         },
7975
7976         /**
7977          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7978          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7979          * if a width has not been set using CSS.
7980          * @return {Number}
7981          */
7982         getComputedWidth : function(){
7983             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7984             if(!w){
7985                 w = parseInt(this.getStyle('width'), 10) || 0;
7986                 if(!this.isBorderBox()){
7987                     w += this.getFrameWidth('lr');
7988                 }
7989             }
7990             return w;
7991         },
7992
7993         /**
7994          * Returns the size of the element.
7995          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7996          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7997          */
7998         getSize : function(contentSize){
7999             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8000         },
8001
8002         /**
8003          * Returns the width and height of the viewport.
8004          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8005          */
8006         getViewSize : function(){
8007             var d = this.dom, doc = document, aw = 0, ah = 0;
8008             if(d == doc || d == doc.body){
8009                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8010             }else{
8011                 return {
8012                     width : d.clientWidth,
8013                     height: d.clientHeight
8014                 };
8015             }
8016         },
8017
8018         /**
8019          * Returns the value of the "value" attribute
8020          * @param {Boolean} asNumber true to parse the value as a number
8021          * @return {String/Number}
8022          */
8023         getValue : function(asNumber){
8024             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8025         },
8026
8027         // private
8028         adjustWidth : function(width){
8029             if(typeof width == "number"){
8030                 if(this.autoBoxAdjust && !this.isBorderBox()){
8031                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8032                 }
8033                 if(width < 0){
8034                     width = 0;
8035                 }
8036             }
8037             return width;
8038         },
8039
8040         // private
8041         adjustHeight : function(height){
8042             if(typeof height == "number"){
8043                if(this.autoBoxAdjust && !this.isBorderBox()){
8044                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8045                }
8046                if(height < 0){
8047                    height = 0;
8048                }
8049             }
8050             return height;
8051         },
8052
8053         /**
8054          * Set the width of the element
8055          * @param {Number} width The new width
8056          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8057          * @return {Roo.Element} this
8058          */
8059         setWidth : function(width, animate){
8060             width = this.adjustWidth(width);
8061             if(!animate || !A){
8062                 this.dom.style.width = this.addUnits(width);
8063             }else{
8064                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8065             }
8066             return this;
8067         },
8068
8069         /**
8070          * Set the height of the element
8071          * @param {Number} height The new height
8072          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8073          * @return {Roo.Element} this
8074          */
8075          setHeight : function(height, animate){
8076             height = this.adjustHeight(height);
8077             if(!animate || !A){
8078                 this.dom.style.height = this.addUnits(height);
8079             }else{
8080                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8081             }
8082             return this;
8083         },
8084
8085         /**
8086          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8087          * @param {Number} width The new width
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          setSize : function(width, height, animate){
8093             if(typeof width == "object"){ // in case of object from getSize()
8094                 height = width.height; width = width.width;
8095             }
8096             width = this.adjustWidth(width); height = this.adjustHeight(height);
8097             if(!animate || !A){
8098                 this.dom.style.width = this.addUnits(width);
8099                 this.dom.style.height = this.addUnits(height);
8100             }else{
8101                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8102             }
8103             return this;
8104         },
8105
8106         /**
8107          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8108          * @param {Number} x X value for new position (coordinates are page-based)
8109          * @param {Number} y Y value for new position (coordinates are page-based)
8110          * @param {Number} width The new width
8111          * @param {Number} height The new height
8112          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8113          * @return {Roo.Element} this
8114          */
8115         setBounds : function(x, y, width, height, animate){
8116             if(!animate || !A){
8117                 this.setSize(width, height);
8118                 this.setLocation(x, y);
8119             }else{
8120                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8121                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8122                               this.preanim(arguments, 4), 'motion');
8123             }
8124             return this;
8125         },
8126
8127         /**
8128          * 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.
8129          * @param {Roo.lib.Region} region The region to fill
8130          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8131          * @return {Roo.Element} this
8132          */
8133         setRegion : function(region, animate){
8134             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8135             return this;
8136         },
8137
8138         /**
8139          * Appends an event handler
8140          *
8141          * @param {String}   eventName     The type of event to append
8142          * @param {Function} fn        The method the event invokes
8143          * @param {Object} scope       (optional) The scope (this object) of the fn
8144          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8145          */
8146         addListener : function(eventName, fn, scope, options){
8147             if (this.dom) {
8148                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8149             }
8150         },
8151
8152         /**
8153          * Removes an event handler from this element
8154          * @param {String} eventName the type of event to remove
8155          * @param {Function} fn the method the event invokes
8156          * @return {Roo.Element} this
8157          */
8158         removeListener : function(eventName, fn){
8159             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8160             return this;
8161         },
8162
8163         /**
8164          * Removes all previous added listeners from this element
8165          * @return {Roo.Element} this
8166          */
8167         removeAllListeners : function(){
8168             E.purgeElement(this.dom);
8169             return this;
8170         },
8171
8172         relayEvent : function(eventName, observable){
8173             this.on(eventName, function(e){
8174                 observable.fireEvent(eventName, e);
8175             });
8176         },
8177
8178         /**
8179          * Set the opacity of the element
8180          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8181          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8182          * @return {Roo.Element} this
8183          */
8184          setOpacity : function(opacity, animate){
8185             if(!animate || !A){
8186                 var s = this.dom.style;
8187                 if(Roo.isIE){
8188                     s.zoom = 1;
8189                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8190                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8191                 }else{
8192                     s.opacity = opacity;
8193                 }
8194             }else{
8195                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8196             }
8197             return this;
8198         },
8199
8200         /**
8201          * Gets the left X coordinate
8202          * @param {Boolean} local True to get the local css position instead of page coordinate
8203          * @return {Number}
8204          */
8205         getLeft : function(local){
8206             if(!local){
8207                 return this.getX();
8208             }else{
8209                 return parseInt(this.getStyle("left"), 10) || 0;
8210             }
8211         },
8212
8213         /**
8214          * Gets the right X coordinate of the element (element X position + element width)
8215          * @param {Boolean} local True to get the local css position instead of page coordinate
8216          * @return {Number}
8217          */
8218         getRight : function(local){
8219             if(!local){
8220                 return this.getX() + this.getWidth();
8221             }else{
8222                 return (this.getLeft(true) + this.getWidth()) || 0;
8223             }
8224         },
8225
8226         /**
8227          * Gets the top Y coordinate
8228          * @param {Boolean} local True to get the local css position instead of page coordinate
8229          * @return {Number}
8230          */
8231         getTop : function(local) {
8232             if(!local){
8233                 return this.getY();
8234             }else{
8235                 return parseInt(this.getStyle("top"), 10) || 0;
8236             }
8237         },
8238
8239         /**
8240          * Gets the bottom Y coordinate of the element (element Y position + element height)
8241          * @param {Boolean} local True to get the local css position instead of page coordinate
8242          * @return {Number}
8243          */
8244         getBottom : function(local){
8245             if(!local){
8246                 return this.getY() + this.getHeight();
8247             }else{
8248                 return (this.getTop(true) + this.getHeight()) || 0;
8249             }
8250         },
8251
8252         /**
8253         * Initializes positioning on this element. If a desired position is not passed, it will make the
8254         * the element positioned relative IF it is not already positioned.
8255         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8256         * @param {Number} zIndex (optional) The zIndex to apply
8257         * @param {Number} x (optional) Set the page X position
8258         * @param {Number} y (optional) Set the page Y position
8259         */
8260         position : function(pos, zIndex, x, y){
8261             if(!pos){
8262                if(this.getStyle('position') == 'static'){
8263                    this.setStyle('position', 'relative');
8264                }
8265             }else{
8266                 this.setStyle("position", pos);
8267             }
8268             if(zIndex){
8269                 this.setStyle("z-index", zIndex);
8270             }
8271             if(x !== undefined && y !== undefined){
8272                 this.setXY([x, y]);
8273             }else if(x !== undefined){
8274                 this.setX(x);
8275             }else if(y !== undefined){
8276                 this.setY(y);
8277             }
8278         },
8279
8280         /**
8281         * Clear positioning back to the default when the document was loaded
8282         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8283         * @return {Roo.Element} this
8284          */
8285         clearPositioning : function(value){
8286             value = value ||'';
8287             this.setStyle({
8288                 "left": value,
8289                 "right": value,
8290                 "top": value,
8291                 "bottom": value,
8292                 "z-index": "",
8293                 "position" : "static"
8294             });
8295             return this;
8296         },
8297
8298         /**
8299         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8300         * snapshot before performing an update and then restoring the element.
8301         * @return {Object}
8302         */
8303         getPositioning : function(){
8304             var l = this.getStyle("left");
8305             var t = this.getStyle("top");
8306             return {
8307                 "position" : this.getStyle("position"),
8308                 "left" : l,
8309                 "right" : l ? "" : this.getStyle("right"),
8310                 "top" : t,
8311                 "bottom" : t ? "" : this.getStyle("bottom"),
8312                 "z-index" : this.getStyle("z-index")
8313             };
8314         },
8315
8316         /**
8317          * Gets the width of the border(s) for the specified side(s)
8318          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8319          * passing lr would get the border (l)eft width + the border (r)ight width.
8320          * @return {Number} The width of the sides passed added together
8321          */
8322         getBorderWidth : function(side){
8323             return this.addStyles(side, El.borders);
8324         },
8325
8326         /**
8327          * Gets the width of the padding(s) for the specified side(s)
8328          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8329          * passing lr would get the padding (l)eft + the padding (r)ight.
8330          * @return {Number} The padding of the sides passed added together
8331          */
8332         getPadding : function(side){
8333             return this.addStyles(side, El.paddings);
8334         },
8335
8336         /**
8337         * Set positioning with an object returned by getPositioning().
8338         * @param {Object} posCfg
8339         * @return {Roo.Element} this
8340          */
8341         setPositioning : function(pc){
8342             this.applyStyles(pc);
8343             if(pc.right == "auto"){
8344                 this.dom.style.right = "";
8345             }
8346             if(pc.bottom == "auto"){
8347                 this.dom.style.bottom = "";
8348             }
8349             return this;
8350         },
8351
8352         // private
8353         fixDisplay : function(){
8354             if(this.getStyle("display") == "none"){
8355                 this.setStyle("visibility", "hidden");
8356                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8357                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8358                     this.setStyle("display", "block");
8359                 }
8360             }
8361         },
8362
8363         /**
8364          * Quick set left and top adding default units
8365          * @param {String} left The left CSS property value
8366          * @param {String} top The top CSS property value
8367          * @return {Roo.Element} this
8368          */
8369          setLeftTop : function(left, top){
8370             this.dom.style.left = this.addUnits(left);
8371             this.dom.style.top = this.addUnits(top);
8372             return this;
8373         },
8374
8375         /**
8376          * Move this element relative to its current position.
8377          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8378          * @param {Number} distance How far to move the element in pixels
8379          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8380          * @return {Roo.Element} this
8381          */
8382          move : function(direction, distance, animate){
8383             var xy = this.getXY();
8384             direction = direction.toLowerCase();
8385             switch(direction){
8386                 case "l":
8387                 case "left":
8388                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8389                     break;
8390                case "r":
8391                case "right":
8392                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8393                     break;
8394                case "t":
8395                case "top":
8396                case "up":
8397                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8398                     break;
8399                case "b":
8400                case "bottom":
8401                case "down":
8402                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8403                     break;
8404             }
8405             return this;
8406         },
8407
8408         /**
8409          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8410          * @return {Roo.Element} this
8411          */
8412         clip : function(){
8413             if(!this.isClipped){
8414                this.isClipped = true;
8415                this.originalClip = {
8416                    "o": this.getStyle("overflow"),
8417                    "x": this.getStyle("overflow-x"),
8418                    "y": this.getStyle("overflow-y")
8419                };
8420                this.setStyle("overflow", "hidden");
8421                this.setStyle("overflow-x", "hidden");
8422                this.setStyle("overflow-y", "hidden");
8423             }
8424             return this;
8425         },
8426
8427         /**
8428          *  Return clipping (overflow) to original clipping before clip() was called
8429          * @return {Roo.Element} this
8430          */
8431         unclip : function(){
8432             if(this.isClipped){
8433                 this.isClipped = false;
8434                 var o = this.originalClip;
8435                 if(o.o){this.setStyle("overflow", o.o);}
8436                 if(o.x){this.setStyle("overflow-x", o.x);}
8437                 if(o.y){this.setStyle("overflow-y", o.y);}
8438             }
8439             return this;
8440         },
8441
8442
8443         /**
8444          * Gets the x,y coordinates specified by the anchor position on the element.
8445          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8446          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8447          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8448          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8449          * @return {Array} [x, y] An array containing the element's x and y coordinates
8450          */
8451         getAnchorXY : function(anchor, local, s){
8452             //Passing a different size is useful for pre-calculating anchors,
8453             //especially for anchored animations that change the el size.
8454
8455             var w, h, vp = false;
8456             if(!s){
8457                 var d = this.dom;
8458                 if(d == document.body || d == document){
8459                     vp = true;
8460                     w = D.getViewWidth(); h = D.getViewHeight();
8461                 }else{
8462                     w = this.getWidth(); h = this.getHeight();
8463                 }
8464             }else{
8465                 w = s.width;  h = s.height;
8466             }
8467             var x = 0, y = 0, r = Math.round;
8468             switch((anchor || "tl").toLowerCase()){
8469                 case "c":
8470                     x = r(w*.5);
8471                     y = r(h*.5);
8472                 break;
8473                 case "t":
8474                     x = r(w*.5);
8475                     y = 0;
8476                 break;
8477                 case "l":
8478                     x = 0;
8479                     y = r(h*.5);
8480                 break;
8481                 case "r":
8482                     x = w;
8483                     y = r(h*.5);
8484                 break;
8485                 case "b":
8486                     x = r(w*.5);
8487                     y = h;
8488                 break;
8489                 case "tl":
8490                     x = 0;
8491                     y = 0;
8492                 break;
8493                 case "bl":
8494                     x = 0;
8495                     y = h;
8496                 break;
8497                 case "br":
8498                     x = w;
8499                     y = h;
8500                 break;
8501                 case "tr":
8502                     x = w;
8503                     y = 0;
8504                 break;
8505             }
8506             if(local === true){
8507                 return [x, y];
8508             }
8509             if(vp){
8510                 var sc = this.getScroll();
8511                 return [x + sc.left, y + sc.top];
8512             }
8513             //Add the element's offset xy
8514             var o = this.getXY();
8515             return [x+o[0], y+o[1]];
8516         },
8517
8518         /**
8519          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8520          * supported position values.
8521          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8522          * @param {String} position The position to align to.
8523          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8524          * @return {Array} [x, y]
8525          */
8526         getAlignToXY : function(el, p, o){
8527             el = Roo.get(el);
8528             var d = this.dom;
8529             if(!el.dom){
8530                 throw "Element.alignTo with an element that doesn't exist";
8531             }
8532             var c = false; //constrain to viewport
8533             var p1 = "", p2 = "";
8534             o = o || [0,0];
8535
8536             if(!p){
8537                 p = "tl-bl";
8538             }else if(p == "?"){
8539                 p = "tl-bl?";
8540             }else if(p.indexOf("-") == -1){
8541                 p = "tl-" + p;
8542             }
8543             p = p.toLowerCase();
8544             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8545             if(!m){
8546                throw "Element.alignTo with an invalid alignment " + p;
8547             }
8548             p1 = m[1]; p2 = m[2]; c = !!m[3];
8549
8550             //Subtract the aligned el's internal xy from the target's offset xy
8551             //plus custom offset to get the aligned el's new offset xy
8552             var a1 = this.getAnchorXY(p1, true);
8553             var a2 = el.getAnchorXY(p2, false);
8554             var x = a2[0] - a1[0] + o[0];
8555             var y = a2[1] - a1[1] + o[1];
8556             if(c){
8557                 //constrain the aligned el to viewport if necessary
8558                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8559                 // 5px of margin for ie
8560                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8561
8562                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8563                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8564                 //otherwise swap the aligned el to the opposite border of the target.
8565                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8566                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8567                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8568                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8569
8570                var doc = document;
8571                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8572                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8573
8574                if((x+w) > dw + scrollX){
8575                     x = swapX ? r.left-w : dw+scrollX-w;
8576                 }
8577                if(x < scrollX){
8578                    x = swapX ? r.right : scrollX;
8579                }
8580                if((y+h) > dh + scrollY){
8581                     y = swapY ? r.top-h : dh+scrollY-h;
8582                 }
8583                if (y < scrollY){
8584                    y = swapY ? r.bottom : scrollY;
8585                }
8586             }
8587             return [x,y];
8588         },
8589
8590         // private
8591         getConstrainToXY : function(){
8592             var os = {top:0, left:0, bottom:0, right: 0};
8593
8594             return function(el, local, offsets, proposedXY){
8595                 el = Roo.get(el);
8596                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8597
8598                 var vw, vh, vx = 0, vy = 0;
8599                 if(el.dom == document.body || el.dom == document){
8600                     vw = Roo.lib.Dom.getViewWidth();
8601                     vh = Roo.lib.Dom.getViewHeight();
8602                 }else{
8603                     vw = el.dom.clientWidth;
8604                     vh = el.dom.clientHeight;
8605                     if(!local){
8606                         var vxy = el.getXY();
8607                         vx = vxy[0];
8608                         vy = vxy[1];
8609                     }
8610                 }
8611
8612                 var s = el.getScroll();
8613
8614                 vx += offsets.left + s.left;
8615                 vy += offsets.top + s.top;
8616
8617                 vw -= offsets.right;
8618                 vh -= offsets.bottom;
8619
8620                 var vr = vx+vw;
8621                 var vb = vy+vh;
8622
8623                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8624                 var x = xy[0], y = xy[1];
8625                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8626
8627                 // only move it if it needs it
8628                 var moved = false;
8629
8630                 // first validate right/bottom
8631                 if((x + w) > vr){
8632                     x = vr - w;
8633                     moved = true;
8634                 }
8635                 if((y + h) > vb){
8636                     y = vb - h;
8637                     moved = true;
8638                 }
8639                 // then make sure top/left isn't negative
8640                 if(x < vx){
8641                     x = vx;
8642                     moved = true;
8643                 }
8644                 if(y < vy){
8645                     y = vy;
8646                     moved = true;
8647                 }
8648                 return moved ? [x, y] : false;
8649             };
8650         }(),
8651
8652         // private
8653         adjustForConstraints : function(xy, parent, offsets){
8654             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8655         },
8656
8657         /**
8658          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8659          * document it aligns it to the viewport.
8660          * The position parameter is optional, and can be specified in any one of the following formats:
8661          * <ul>
8662          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8663          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8664          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8665          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8666          *   <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
8667          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8668          * </ul>
8669          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8670          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8671          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8672          * that specified in order to enforce the viewport constraints.
8673          * Following are all of the supported anchor positions:
8674     <pre>
8675     Value  Description
8676     -----  -----------------------------
8677     tl     The top left corner (default)
8678     t      The center of the top edge
8679     tr     The top right corner
8680     l      The center of the left edge
8681     c      In the center of the element
8682     r      The center of the right edge
8683     bl     The bottom left corner
8684     b      The center of the bottom edge
8685     br     The bottom right corner
8686     </pre>
8687     Example Usage:
8688     <pre><code>
8689     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8690     el.alignTo("other-el");
8691
8692     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8693     el.alignTo("other-el", "tr?");
8694
8695     // align the bottom right corner of el with the center left edge of other-el
8696     el.alignTo("other-el", "br-l?");
8697
8698     // align the center of el with the bottom left corner of other-el and
8699     // adjust the x position by -6 pixels (and the y position by 0)
8700     el.alignTo("other-el", "c-bl", [-6, 0]);
8701     </code></pre>
8702          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8703          * @param {String} position The position to align to.
8704          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8705          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8706          * @return {Roo.Element} this
8707          */
8708         alignTo : function(element, position, offsets, animate){
8709             var xy = this.getAlignToXY(element, position, offsets);
8710             this.setXY(xy, this.preanim(arguments, 3));
8711             return this;
8712         },
8713
8714         /**
8715          * Anchors an element to another element and realigns it when the window is resized.
8716          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8717          * @param {String} position The position to align to.
8718          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8719          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8720          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8721          * is a number, it is used as the buffer delay (defaults to 50ms).
8722          * @param {Function} callback The function to call after the animation finishes
8723          * @return {Roo.Element} this
8724          */
8725         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8726             var action = function(){
8727                 this.alignTo(el, alignment, offsets, animate);
8728                 Roo.callback(callback, this);
8729             };
8730             Roo.EventManager.onWindowResize(action, this);
8731             var tm = typeof monitorScroll;
8732             if(tm != 'undefined'){
8733                 Roo.EventManager.on(window, 'scroll', action, this,
8734                     {buffer: tm == 'number' ? monitorScroll : 50});
8735             }
8736             action.call(this); // align immediately
8737             return this;
8738         },
8739         /**
8740          * Clears any opacity settings from this element. Required in some cases for IE.
8741          * @return {Roo.Element} this
8742          */
8743         clearOpacity : function(){
8744             if (window.ActiveXObject) {
8745                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8746                     this.dom.style.filter = "";
8747                 }
8748             } else {
8749                 this.dom.style.opacity = "";
8750                 this.dom.style["-moz-opacity"] = "";
8751                 this.dom.style["-khtml-opacity"] = "";
8752             }
8753             return this;
8754         },
8755
8756         /**
8757          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8758          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8759          * @return {Roo.Element} this
8760          */
8761         hide : function(animate){
8762             this.setVisible(false, this.preanim(arguments, 0));
8763             return this;
8764         },
8765
8766         /**
8767         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8768         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8769          * @return {Roo.Element} this
8770          */
8771         show : function(animate){
8772             this.setVisible(true, this.preanim(arguments, 0));
8773             return this;
8774         },
8775
8776         /**
8777          * @private Test if size has a unit, otherwise appends the default
8778          */
8779         addUnits : function(size){
8780             return Roo.Element.addUnits(size, this.defaultUnit);
8781         },
8782
8783         /**
8784          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8785          * @return {Roo.Element} this
8786          */
8787         beginMeasure : function(){
8788             var el = this.dom;
8789             if(el.offsetWidth || el.offsetHeight){
8790                 return this; // offsets work already
8791             }
8792             var changed = [];
8793             var p = this.dom, b = document.body; // start with this element
8794             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8795                 var pe = Roo.get(p);
8796                 if(pe.getStyle('display') == 'none'){
8797                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8798                     p.style.visibility = "hidden";
8799                     p.style.display = "block";
8800                 }
8801                 p = p.parentNode;
8802             }
8803             this._measureChanged = changed;
8804             return this;
8805
8806         },
8807
8808         /**
8809          * Restores displays to before beginMeasure was called
8810          * @return {Roo.Element} this
8811          */
8812         endMeasure : function(){
8813             var changed = this._measureChanged;
8814             if(changed){
8815                 for(var i = 0, len = changed.length; i < len; i++) {
8816                     var r = changed[i];
8817                     r.el.style.visibility = r.visibility;
8818                     r.el.style.display = "none";
8819                 }
8820                 this._measureChanged = null;
8821             }
8822             return this;
8823         },
8824
8825         /**
8826         * Update the innerHTML of this element, optionally searching for and processing scripts
8827         * @param {String} html The new HTML
8828         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8829         * @param {Function} callback For async script loading you can be noticed when the update completes
8830         * @return {Roo.Element} this
8831          */
8832         update : function(html, loadScripts, callback){
8833             if(typeof html == "undefined"){
8834                 html = "";
8835             }
8836             if(loadScripts !== true){
8837                 this.dom.innerHTML = html;
8838                 if(typeof callback == "function"){
8839                     callback();
8840                 }
8841                 return this;
8842             }
8843             var id = Roo.id();
8844             var dom = this.dom;
8845
8846             html += '<span id="' + id + '"></span>';
8847
8848             E.onAvailable(id, function(){
8849                 var hd = document.getElementsByTagName("head")[0];
8850                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8851                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8852                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8853
8854                 var match;
8855                 while(match = re.exec(html)){
8856                     var attrs = match[1];
8857                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8858                     if(srcMatch && srcMatch[2]){
8859                        var s = document.createElement("script");
8860                        s.src = srcMatch[2];
8861                        var typeMatch = attrs.match(typeRe);
8862                        if(typeMatch && typeMatch[2]){
8863                            s.type = typeMatch[2];
8864                        }
8865                        hd.appendChild(s);
8866                     }else if(match[2] && match[2].length > 0){
8867                         if(window.execScript) {
8868                            window.execScript(match[2]);
8869                         } else {
8870                             /**
8871                              * eval:var:id
8872                              * eval:var:dom
8873                              * eval:var:html
8874                              * 
8875                              */
8876                            window.eval(match[2]);
8877                         }
8878                     }
8879                 }
8880                 var el = document.getElementById(id);
8881                 if(el){el.parentNode.removeChild(el);}
8882                 if(typeof callback == "function"){
8883                     callback();
8884                 }
8885             });
8886             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8887             return this;
8888         },
8889
8890         /**
8891          * Direct access to the UpdateManager update() method (takes the same parameters).
8892          * @param {String/Function} url The url for this request or a function to call to get the url
8893          * @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}
8894          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8895          * @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.
8896          * @return {Roo.Element} this
8897          */
8898         load : function(){
8899             var um = this.getUpdateManager();
8900             um.update.apply(um, arguments);
8901             return this;
8902         },
8903
8904         /**
8905         * Gets this element's UpdateManager
8906         * @return {Roo.UpdateManager} The UpdateManager
8907         */
8908         getUpdateManager : function(){
8909             if(!this.updateManager){
8910                 this.updateManager = new Roo.UpdateManager(this);
8911             }
8912             return this.updateManager;
8913         },
8914
8915         /**
8916          * Disables text selection for this element (normalized across browsers)
8917          * @return {Roo.Element} this
8918          */
8919         unselectable : function(){
8920             this.dom.unselectable = "on";
8921             this.swallowEvent("selectstart", true);
8922             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8923             this.addClass("x-unselectable");
8924             return this;
8925         },
8926
8927         /**
8928         * Calculates the x, y to center this element on the screen
8929         * @return {Array} The x, y values [x, y]
8930         */
8931         getCenterXY : function(){
8932             return this.getAlignToXY(document, 'c-c');
8933         },
8934
8935         /**
8936         * Centers the Element in either the viewport, or another Element.
8937         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8938         */
8939         center : function(centerIn){
8940             this.alignTo(centerIn || document, 'c-c');
8941             return this;
8942         },
8943
8944         /**
8945          * Tests various css rules/browsers to determine if this element uses a border box
8946          * @return {Boolean}
8947          */
8948         isBorderBox : function(){
8949             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8950         },
8951
8952         /**
8953          * Return a box {x, y, width, height} that can be used to set another elements
8954          * size/location to match this element.
8955          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8956          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8957          * @return {Object} box An object in the format {x, y, width, height}
8958          */
8959         getBox : function(contentBox, local){
8960             var xy;
8961             if(!local){
8962                 xy = this.getXY();
8963             }else{
8964                 var left = parseInt(this.getStyle("left"), 10) || 0;
8965                 var top = parseInt(this.getStyle("top"), 10) || 0;
8966                 xy = [left, top];
8967             }
8968             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8969             if(!contentBox){
8970                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8971             }else{
8972                 var l = this.getBorderWidth("l")+this.getPadding("l");
8973                 var r = this.getBorderWidth("r")+this.getPadding("r");
8974                 var t = this.getBorderWidth("t")+this.getPadding("t");
8975                 var b = this.getBorderWidth("b")+this.getPadding("b");
8976                 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)};
8977             }
8978             bx.right = bx.x + bx.width;
8979             bx.bottom = bx.y + bx.height;
8980             return bx;
8981         },
8982
8983         /**
8984          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8985          for more information about the sides.
8986          * @param {String} sides
8987          * @return {Number}
8988          */
8989         getFrameWidth : function(sides, onlyContentBox){
8990             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8991         },
8992
8993         /**
8994          * 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.
8995          * @param {Object} box The box to fill {x, y, width, height}
8996          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8997          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8998          * @return {Roo.Element} this
8999          */
9000         setBox : function(box, adjust, animate){
9001             var w = box.width, h = box.height;
9002             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9003                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9004                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9005             }
9006             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9007             return this;
9008         },
9009
9010         /**
9011          * Forces the browser to repaint this element
9012          * @return {Roo.Element} this
9013          */
9014          repaint : function(){
9015             var dom = this.dom;
9016             this.addClass("x-repaint");
9017             setTimeout(function(){
9018                 Roo.get(dom).removeClass("x-repaint");
9019             }, 1);
9020             return this;
9021         },
9022
9023         /**
9024          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9025          * then it returns the calculated width of the sides (see getPadding)
9026          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9027          * @return {Object/Number}
9028          */
9029         getMargins : function(side){
9030             if(!side){
9031                 return {
9032                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9033                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9034                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9035                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9036                 };
9037             }else{
9038                 return this.addStyles(side, El.margins);
9039              }
9040         },
9041
9042         // private
9043         addStyles : function(sides, styles){
9044             var val = 0, v, w;
9045             for(var i = 0, len = sides.length; i < len; i++){
9046                 v = this.getStyle(styles[sides.charAt(i)]);
9047                 if(v){
9048                      w = parseInt(v, 10);
9049                      if(w){ val += w; }
9050                 }
9051             }
9052             return val;
9053         },
9054
9055         /**
9056          * Creates a proxy element of this element
9057          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9058          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9059          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9060          * @return {Roo.Element} The new proxy element
9061          */
9062         createProxy : function(config, renderTo, matchBox){
9063             if(renderTo){
9064                 renderTo = Roo.getDom(renderTo);
9065             }else{
9066                 renderTo = document.body;
9067             }
9068             config = typeof config == "object" ?
9069                 config : {tag : "div", cls: config};
9070             var proxy = Roo.DomHelper.append(renderTo, config, true);
9071             if(matchBox){
9072                proxy.setBox(this.getBox());
9073             }
9074             return proxy;
9075         },
9076
9077         /**
9078          * Puts a mask over this element to disable user interaction. Requires core.css.
9079          * This method can only be applied to elements which accept child nodes.
9080          * @param {String} msg (optional) A message to display in the mask
9081          * @param {String} msgCls (optional) A css class to apply to the msg element
9082          * @return {Element} The mask  element
9083          */
9084         mask : function(msg, msgCls)
9085         {
9086             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9087                 this.setStyle("position", "relative");
9088             }
9089             if(!this._mask){
9090                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9091             }
9092             this.addClass("x-masked");
9093             this._mask.setDisplayed(true);
9094             
9095             // we wander
9096             var z = 0;
9097             var dom = this.dom;
9098             while (dom && dom.style) {
9099                 if (!isNaN(parseInt(dom.style.zIndex))) {
9100                     z = Math.max(z, parseInt(dom.style.zIndex));
9101                 }
9102                 dom = dom.parentNode;
9103             }
9104             // if we are masking the body - then it hides everything..
9105             if (this.dom == document.body) {
9106                 z = 1000000;
9107                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9108                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9109             }
9110            
9111             if(typeof msg == 'string'){
9112                 if(!this._maskMsg){
9113                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9114                 }
9115                 var mm = this._maskMsg;
9116                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9117                 if (mm.dom.firstChild) { // weird IE issue?
9118                     mm.dom.firstChild.innerHTML = msg;
9119                 }
9120                 mm.setDisplayed(true);
9121                 mm.center(this);
9122                 mm.setStyle('z-index', z + 102);
9123             }
9124             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9125                 this._mask.setHeight(this.getHeight());
9126             }
9127             this._mask.setStyle('z-index', z + 100);
9128             
9129             return this._mask;
9130         },
9131
9132         /**
9133          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9134          * it is cached for reuse.
9135          */
9136         unmask : function(removeEl){
9137             if(this._mask){
9138                 if(removeEl === true){
9139                     this._mask.remove();
9140                     delete this._mask;
9141                     if(this._maskMsg){
9142                         this._maskMsg.remove();
9143                         delete this._maskMsg;
9144                     }
9145                 }else{
9146                     this._mask.setDisplayed(false);
9147                     if(this._maskMsg){
9148                         this._maskMsg.setDisplayed(false);
9149                     }
9150                 }
9151             }
9152             this.removeClass("x-masked");
9153         },
9154
9155         /**
9156          * Returns true if this element is masked
9157          * @return {Boolean}
9158          */
9159         isMasked : function(){
9160             return this._mask && this._mask.isVisible();
9161         },
9162
9163         /**
9164          * Creates an iframe shim for this element to keep selects and other windowed objects from
9165          * showing through.
9166          * @return {Roo.Element} The new shim element
9167          */
9168         createShim : function(){
9169             var el = document.createElement('iframe');
9170             el.frameBorder = 'no';
9171             el.className = 'roo-shim';
9172             if(Roo.isIE && Roo.isSecure){
9173                 el.src = Roo.SSL_SECURE_URL;
9174             }
9175             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9176             shim.autoBoxAdjust = false;
9177             return shim;
9178         },
9179
9180         /**
9181          * Removes this element from the DOM and deletes it from the cache
9182          */
9183         remove : function(){
9184             if(this.dom.parentNode){
9185                 this.dom.parentNode.removeChild(this.dom);
9186             }
9187             delete El.cache[this.dom.id];
9188         },
9189
9190         /**
9191          * Sets up event handlers to add and remove a css class when the mouse is over this element
9192          * @param {String} className
9193          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9194          * mouseout events for children elements
9195          * @return {Roo.Element} this
9196          */
9197         addClassOnOver : function(className, preventFlicker){
9198             this.on("mouseover", function(){
9199                 Roo.fly(this, '_internal').addClass(className);
9200             }, this.dom);
9201             var removeFn = function(e){
9202                 if(preventFlicker !== true || !e.within(this, true)){
9203                     Roo.fly(this, '_internal').removeClass(className);
9204                 }
9205             };
9206             this.on("mouseout", removeFn, this.dom);
9207             return this;
9208         },
9209
9210         /**
9211          * Sets up event handlers to add and remove a css class when this element has the focus
9212          * @param {String} className
9213          * @return {Roo.Element} this
9214          */
9215         addClassOnFocus : function(className){
9216             this.on("focus", function(){
9217                 Roo.fly(this, '_internal').addClass(className);
9218             }, this.dom);
9219             this.on("blur", function(){
9220                 Roo.fly(this, '_internal').removeClass(className);
9221             }, this.dom);
9222             return this;
9223         },
9224         /**
9225          * 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)
9226          * @param {String} className
9227          * @return {Roo.Element} this
9228          */
9229         addClassOnClick : function(className){
9230             var dom = this.dom;
9231             this.on("mousedown", function(){
9232                 Roo.fly(dom, '_internal').addClass(className);
9233                 var d = Roo.get(document);
9234                 var fn = function(){
9235                     Roo.fly(dom, '_internal').removeClass(className);
9236                     d.removeListener("mouseup", fn);
9237                 };
9238                 d.on("mouseup", fn);
9239             });
9240             return this;
9241         },
9242
9243         /**
9244          * Stops the specified event from bubbling and optionally prevents the default action
9245          * @param {String} eventName
9246          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9247          * @return {Roo.Element} this
9248          */
9249         swallowEvent : function(eventName, preventDefault){
9250             var fn = function(e){
9251                 e.stopPropagation();
9252                 if(preventDefault){
9253                     e.preventDefault();
9254                 }
9255             };
9256             if(eventName instanceof Array){
9257                 for(var i = 0, len = eventName.length; i < len; i++){
9258                      this.on(eventName[i], fn);
9259                 }
9260                 return this;
9261             }
9262             this.on(eventName, fn);
9263             return this;
9264         },
9265
9266         /**
9267          * @private
9268          */
9269       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9270
9271         /**
9272          * Sizes this element to its parent element's dimensions performing
9273          * neccessary box adjustments.
9274          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9275          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9276          * @return {Roo.Element} this
9277          */
9278         fitToParent : function(monitorResize, targetParent) {
9279           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9280           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9281           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9282             return;
9283           }
9284           var p = Roo.get(targetParent || this.dom.parentNode);
9285           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9286           if (monitorResize === true) {
9287             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9288             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9289           }
9290           return this;
9291         },
9292
9293         /**
9294          * Gets the next sibling, skipping text nodes
9295          * @return {HTMLElement} The next sibling or null
9296          */
9297         getNextSibling : function(){
9298             var n = this.dom.nextSibling;
9299             while(n && n.nodeType != 1){
9300                 n = n.nextSibling;
9301             }
9302             return n;
9303         },
9304
9305         /**
9306          * Gets the previous sibling, skipping text nodes
9307          * @return {HTMLElement} The previous sibling or null
9308          */
9309         getPrevSibling : function(){
9310             var n = this.dom.previousSibling;
9311             while(n && n.nodeType != 1){
9312                 n = n.previousSibling;
9313             }
9314             return n;
9315         },
9316
9317
9318         /**
9319          * Appends the passed element(s) to this element
9320          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9321          * @return {Roo.Element} this
9322          */
9323         appendChild: function(el){
9324             el = Roo.get(el);
9325             el.appendTo(this);
9326             return this;
9327         },
9328
9329         /**
9330          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9331          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9332          * automatically generated with the specified attributes.
9333          * @param {HTMLElement} insertBefore (optional) a child element of this element
9334          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9335          * @return {Roo.Element} The new child element
9336          */
9337         createChild: function(config, insertBefore, returnDom){
9338             config = config || {tag:'div'};
9339             if(insertBefore){
9340                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9341             }
9342             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9343         },
9344
9345         /**
9346          * Appends this element to the passed element
9347          * @param {String/HTMLElement/Element} el The new parent element
9348          * @return {Roo.Element} this
9349          */
9350         appendTo: function(el){
9351             el = Roo.getDom(el);
9352             el.appendChild(this.dom);
9353             return this;
9354         },
9355
9356         /**
9357          * Inserts this element before the passed element in the DOM
9358          * @param {String/HTMLElement/Element} el The element to insert before
9359          * @return {Roo.Element} this
9360          */
9361         insertBefore: function(el){
9362             el = Roo.getDom(el);
9363             el.parentNode.insertBefore(this.dom, el);
9364             return this;
9365         },
9366
9367         /**
9368          * Inserts this element after the passed element in the DOM
9369          * @param {String/HTMLElement/Element} el The element to insert after
9370          * @return {Roo.Element} this
9371          */
9372         insertAfter: function(el){
9373             el = Roo.getDom(el);
9374             el.parentNode.insertBefore(this.dom, el.nextSibling);
9375             return this;
9376         },
9377
9378         /**
9379          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9380          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9381          * @return {Roo.Element} The new child
9382          */
9383         insertFirst: function(el, returnDom){
9384             el = el || {};
9385             if(typeof el == 'object' && !el.nodeType){ // dh config
9386                 return this.createChild(el, this.dom.firstChild, returnDom);
9387             }else{
9388                 el = Roo.getDom(el);
9389                 this.dom.insertBefore(el, this.dom.firstChild);
9390                 return !returnDom ? Roo.get(el) : el;
9391             }
9392         },
9393
9394         /**
9395          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9396          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9397          * @param {String} where (optional) 'before' or 'after' defaults to before
9398          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9399          * @return {Roo.Element} the inserted Element
9400          */
9401         insertSibling: function(el, where, returnDom){
9402             where = where ? where.toLowerCase() : 'before';
9403             el = el || {};
9404             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9405
9406             if(typeof el == 'object' && !el.nodeType){ // dh config
9407                 if(where == 'after' && !this.dom.nextSibling){
9408                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9409                 }else{
9410                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9411                 }
9412
9413             }else{
9414                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9415                             where == 'before' ? this.dom : this.dom.nextSibling);
9416                 if(!returnDom){
9417                     rt = Roo.get(rt);
9418                 }
9419             }
9420             return rt;
9421         },
9422
9423         /**
9424          * Creates and wraps this element with another element
9425          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9426          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9427          * @return {HTMLElement/Element} The newly created wrapper element
9428          */
9429         wrap: function(config, returnDom){
9430             if(!config){
9431                 config = {tag: "div"};
9432             }
9433             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9434             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9435             return newEl;
9436         },
9437
9438         /**
9439          * Replaces the passed element with this element
9440          * @param {String/HTMLElement/Element} el The element to replace
9441          * @return {Roo.Element} this
9442          */
9443         replace: function(el){
9444             el = Roo.get(el);
9445             this.insertBefore(el);
9446             el.remove();
9447             return this;
9448         },
9449
9450         /**
9451          * Inserts an html fragment into this element
9452          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9453          * @param {String} html The HTML fragment
9454          * @param {Boolean} returnEl True to return an Roo.Element
9455          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9456          */
9457         insertHtml : function(where, html, returnEl){
9458             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9459             return returnEl ? Roo.get(el) : el;
9460         },
9461
9462         /**
9463          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9464          * @param {Object} o The object with the attributes
9465          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9466          * @return {Roo.Element} this
9467          */
9468         set : function(o, useSet){
9469             var el = this.dom;
9470             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9471             for(var attr in o){
9472                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9473                 if(attr=="cls"){
9474                     el.className = o["cls"];
9475                 }else{
9476                     if(useSet) {
9477                         el.setAttribute(attr, o[attr]);
9478                     } else {
9479                         el[attr] = o[attr];
9480                     }
9481                 }
9482             }
9483             if(o.style){
9484                 Roo.DomHelper.applyStyles(el, o.style);
9485             }
9486             return this;
9487         },
9488
9489         /**
9490          * Convenience method for constructing a KeyMap
9491          * @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:
9492          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9493          * @param {Function} fn The function to call
9494          * @param {Object} scope (optional) The scope of the function
9495          * @return {Roo.KeyMap} The KeyMap created
9496          */
9497         addKeyListener : function(key, fn, scope){
9498             var config;
9499             if(typeof key != "object" || key instanceof Array){
9500                 config = {
9501                     key: key,
9502                     fn: fn,
9503                     scope: scope
9504                 };
9505             }else{
9506                 config = {
9507                     key : key.key,
9508                     shift : key.shift,
9509                     ctrl : key.ctrl,
9510                     alt : key.alt,
9511                     fn: fn,
9512                     scope: scope
9513                 };
9514             }
9515             return new Roo.KeyMap(this, config);
9516         },
9517
9518         /**
9519          * Creates a KeyMap for this element
9520          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9521          * @return {Roo.KeyMap} The KeyMap created
9522          */
9523         addKeyMap : function(config){
9524             return new Roo.KeyMap(this, config);
9525         },
9526
9527         /**
9528          * Returns true if this element is scrollable.
9529          * @return {Boolean}
9530          */
9531          isScrollable : function(){
9532             var dom = this.dom;
9533             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9534         },
9535
9536         /**
9537          * 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().
9538          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9539          * @param {Number} value The new scroll value
9540          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9541          * @return {Element} this
9542          */
9543
9544         scrollTo : function(side, value, animate){
9545             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9546             if(!animate || !A){
9547                 this.dom[prop] = value;
9548             }else{
9549                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9550                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9551             }
9552             return this;
9553         },
9554
9555         /**
9556          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9557          * within this element's scrollable range.
9558          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9559          * @param {Number} distance How far to scroll the element in pixels
9560          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9561          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9562          * was scrolled as far as it could go.
9563          */
9564          scroll : function(direction, distance, animate){
9565              if(!this.isScrollable()){
9566                  return;
9567              }
9568              var el = this.dom;
9569              var l = el.scrollLeft, t = el.scrollTop;
9570              var w = el.scrollWidth, h = el.scrollHeight;
9571              var cw = el.clientWidth, ch = el.clientHeight;
9572              direction = direction.toLowerCase();
9573              var scrolled = false;
9574              var a = this.preanim(arguments, 2);
9575              switch(direction){
9576                  case "l":
9577                  case "left":
9578                      if(w - l > cw){
9579                          var v = Math.min(l + distance, w-cw);
9580                          this.scrollTo("left", v, a);
9581                          scrolled = true;
9582                      }
9583                      break;
9584                 case "r":
9585                 case "right":
9586                      if(l > 0){
9587                          var v = Math.max(l - distance, 0);
9588                          this.scrollTo("left", v, a);
9589                          scrolled = true;
9590                      }
9591                      break;
9592                 case "t":
9593                 case "top":
9594                 case "up":
9595                      if(t > 0){
9596                          var v = Math.max(t - distance, 0);
9597                          this.scrollTo("top", v, a);
9598                          scrolled = true;
9599                      }
9600                      break;
9601                 case "b":
9602                 case "bottom":
9603                 case "down":
9604                      if(h - t > ch){
9605                          var v = Math.min(t + distance, h-ch);
9606                          this.scrollTo("top", v, a);
9607                          scrolled = true;
9608                      }
9609                      break;
9610              }
9611              return scrolled;
9612         },
9613
9614         /**
9615          * Translates the passed page coordinates into left/top css values for this element
9616          * @param {Number/Array} x The page x or an array containing [x, y]
9617          * @param {Number} y The page y
9618          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9619          */
9620         translatePoints : function(x, y){
9621             if(typeof x == 'object' || x instanceof Array){
9622                 y = x[1]; x = x[0];
9623             }
9624             var p = this.getStyle('position');
9625             var o = this.getXY();
9626
9627             var l = parseInt(this.getStyle('left'), 10);
9628             var t = parseInt(this.getStyle('top'), 10);
9629
9630             if(isNaN(l)){
9631                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9632             }
9633             if(isNaN(t)){
9634                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9635             }
9636
9637             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9638         },
9639
9640         /**
9641          * Returns the current scroll position of the element.
9642          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9643          */
9644         getScroll : function(){
9645             var d = this.dom, doc = document;
9646             if(d == doc || d == doc.body){
9647                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9648                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9649                 return {left: l, top: t};
9650             }else{
9651                 return {left: d.scrollLeft, top: d.scrollTop};
9652             }
9653         },
9654
9655         /**
9656          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9657          * are convert to standard 6 digit hex color.
9658          * @param {String} attr The css attribute
9659          * @param {String} defaultValue The default value to use when a valid color isn't found
9660          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9661          * YUI color anims.
9662          */
9663         getColor : function(attr, defaultValue, prefix){
9664             var v = this.getStyle(attr);
9665             if(!v || v == "transparent" || v == "inherit") {
9666                 return defaultValue;
9667             }
9668             var color = typeof prefix == "undefined" ? "#" : prefix;
9669             if(v.substr(0, 4) == "rgb("){
9670                 var rvs = v.slice(4, v.length -1).split(",");
9671                 for(var i = 0; i < 3; i++){
9672                     var h = parseInt(rvs[i]).toString(16);
9673                     if(h < 16){
9674                         h = "0" + h;
9675                     }
9676                     color += h;
9677                 }
9678             } else {
9679                 if(v.substr(0, 1) == "#"){
9680                     if(v.length == 4) {
9681                         for(var i = 1; i < 4; i++){
9682                             var c = v.charAt(i);
9683                             color +=  c + c;
9684                         }
9685                     }else if(v.length == 7){
9686                         color += v.substr(1);
9687                     }
9688                 }
9689             }
9690             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9691         },
9692
9693         /**
9694          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9695          * gradient background, rounded corners and a 4-way shadow.
9696          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9697          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9698          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9699          * @return {Roo.Element} this
9700          */
9701         boxWrap : function(cls){
9702             cls = cls || 'x-box';
9703             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9704             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9705             return el;
9706         },
9707
9708         /**
9709          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9710          * @param {String} namespace The namespace in which to look for the attribute
9711          * @param {String} name The attribute name
9712          * @return {String} The attribute value
9713          */
9714         getAttributeNS : Roo.isIE ? function(ns, name){
9715             var d = this.dom;
9716             var type = typeof d[ns+":"+name];
9717             if(type != 'undefined' && type != 'unknown'){
9718                 return d[ns+":"+name];
9719             }
9720             return d[name];
9721         } : function(ns, name){
9722             var d = this.dom;
9723             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9724         },
9725         
9726         
9727         /**
9728          * Sets or Returns the value the dom attribute value
9729          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9730          * @param {String} value (optional) The value to set the attribute to
9731          * @return {String} The attribute value
9732          */
9733         attr : function(name){
9734             if (arguments.length > 1) {
9735                 this.dom.setAttribute(name, arguments[1]);
9736                 return arguments[1];
9737             }
9738             if (typeof(name) == 'object') {
9739                 for(var i in name) {
9740                     this.attr(i, name[i]);
9741                 }
9742                 return name;
9743             }
9744             
9745             
9746             if (!this.dom.hasAttribute(name)) {
9747                 return undefined;
9748             }
9749             return this.dom.getAttribute(name);
9750         }
9751         
9752         
9753         
9754     };
9755
9756     var ep = El.prototype;
9757
9758     /**
9759      * Appends an event handler (Shorthand for addListener)
9760      * @param {String}   eventName     The type of event to append
9761      * @param {Function} fn        The method the event invokes
9762      * @param {Object} scope       (optional) The scope (this object) of the fn
9763      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9764      * @method
9765      */
9766     ep.on = ep.addListener;
9767         // backwards compat
9768     ep.mon = ep.addListener;
9769
9770     /**
9771      * Removes an event handler from this element (shorthand for removeListener)
9772      * @param {String} eventName the type of event to remove
9773      * @param {Function} fn the method the event invokes
9774      * @return {Roo.Element} this
9775      * @method
9776      */
9777     ep.un = ep.removeListener;
9778
9779     /**
9780      * true to automatically adjust width and height settings for box-model issues (default to true)
9781      */
9782     ep.autoBoxAdjust = true;
9783
9784     // private
9785     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9786
9787     // private
9788     El.addUnits = function(v, defaultUnit){
9789         if(v === "" || v == "auto"){
9790             return v;
9791         }
9792         if(v === undefined){
9793             return '';
9794         }
9795         if(typeof v == "number" || !El.unitPattern.test(v)){
9796             return v + (defaultUnit || 'px');
9797         }
9798         return v;
9799     };
9800
9801     // special markup used throughout Roo when box wrapping elements
9802     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>';
9803     /**
9804      * Visibility mode constant - Use visibility to hide element
9805      * @static
9806      * @type Number
9807      */
9808     El.VISIBILITY = 1;
9809     /**
9810      * Visibility mode constant - Use display to hide element
9811      * @static
9812      * @type Number
9813      */
9814     El.DISPLAY = 2;
9815
9816     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9817     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9818     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9819
9820
9821
9822     /**
9823      * @private
9824      */
9825     El.cache = {};
9826
9827     var docEl;
9828
9829     /**
9830      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9831      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9832      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9833      * @return {Element} The Element object
9834      * @static
9835      */
9836     El.get = function(el){
9837         var ex, elm, id;
9838         if(!el){ return null; }
9839         if(typeof el == "string"){ // element id
9840             if(!(elm = document.getElementById(el))){
9841                 return null;
9842             }
9843             if(ex = El.cache[el]){
9844                 ex.dom = elm;
9845             }else{
9846                 ex = El.cache[el] = new El(elm);
9847             }
9848             return ex;
9849         }else if(el.tagName){ // dom element
9850             if(!(id = el.id)){
9851                 id = Roo.id(el);
9852             }
9853             if(ex = El.cache[id]){
9854                 ex.dom = el;
9855             }else{
9856                 ex = El.cache[id] = new El(el);
9857             }
9858             return ex;
9859         }else if(el instanceof El){
9860             if(el != docEl){
9861                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9862                                                               // catch case where it hasn't been appended
9863                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9864             }
9865             return el;
9866         }else if(el.isComposite){
9867             return el;
9868         }else if(el instanceof Array){
9869             return El.select(el);
9870         }else if(el == document){
9871             // create a bogus element object representing the document object
9872             if(!docEl){
9873                 var f = function(){};
9874                 f.prototype = El.prototype;
9875                 docEl = new f();
9876                 docEl.dom = document;
9877             }
9878             return docEl;
9879         }
9880         return null;
9881     };
9882
9883     // private
9884     El.uncache = function(el){
9885         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9886             if(a[i]){
9887                 delete El.cache[a[i].id || a[i]];
9888             }
9889         }
9890     };
9891
9892     // private
9893     // Garbage collection - uncache elements/purge listeners on orphaned elements
9894     // so we don't hold a reference and cause the browser to retain them
9895     El.garbageCollect = function(){
9896         if(!Roo.enableGarbageCollector){
9897             clearInterval(El.collectorThread);
9898             return;
9899         }
9900         for(var eid in El.cache){
9901             var el = El.cache[eid], d = el.dom;
9902             // -------------------------------------------------------
9903             // Determining what is garbage:
9904             // -------------------------------------------------------
9905             // !d
9906             // dom node is null, definitely garbage
9907             // -------------------------------------------------------
9908             // !d.parentNode
9909             // no parentNode == direct orphan, definitely garbage
9910             // -------------------------------------------------------
9911             // !d.offsetParent && !document.getElementById(eid)
9912             // display none elements have no offsetParent so we will
9913             // also try to look it up by it's id. However, check
9914             // offsetParent first so we don't do unneeded lookups.
9915             // This enables collection of elements that are not orphans
9916             // directly, but somewhere up the line they have an orphan
9917             // parent.
9918             // -------------------------------------------------------
9919             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9920                 delete El.cache[eid];
9921                 if(d && Roo.enableListenerCollection){
9922                     E.purgeElement(d);
9923                 }
9924             }
9925         }
9926     }
9927     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9928
9929
9930     // dom is optional
9931     El.Flyweight = function(dom){
9932         this.dom = dom;
9933     };
9934     El.Flyweight.prototype = El.prototype;
9935
9936     El._flyweights = {};
9937     /**
9938      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9939      * the dom node can be overwritten by other code.
9940      * @param {String/HTMLElement} el The dom node or id
9941      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9942      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9943      * @static
9944      * @return {Element} The shared Element object
9945      */
9946     El.fly = function(el, named){
9947         named = named || '_global';
9948         el = Roo.getDom(el);
9949         if(!el){
9950             return null;
9951         }
9952         if(!El._flyweights[named]){
9953             El._flyweights[named] = new El.Flyweight();
9954         }
9955         El._flyweights[named].dom = el;
9956         return El._flyweights[named];
9957     };
9958
9959     /**
9960      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9961      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9962      * Shorthand of {@link Roo.Element#get}
9963      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9964      * @return {Element} The Element object
9965      * @member Roo
9966      * @method get
9967      */
9968     Roo.get = El.get;
9969     /**
9970      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9971      * the dom node can be overwritten by other code.
9972      * Shorthand of {@link Roo.Element#fly}
9973      * @param {String/HTMLElement} el The dom node or id
9974      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9975      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9976      * @static
9977      * @return {Element} The shared Element object
9978      * @member Roo
9979      * @method fly
9980      */
9981     Roo.fly = El.fly;
9982
9983     // speedy lookup for elements never to box adjust
9984     var noBoxAdjust = Roo.isStrict ? {
9985         select:1
9986     } : {
9987         input:1, select:1, textarea:1
9988     };
9989     if(Roo.isIE || Roo.isGecko){
9990         noBoxAdjust['button'] = 1;
9991     }
9992
9993
9994     Roo.EventManager.on(window, 'unload', function(){
9995         delete El.cache;
9996         delete El._flyweights;
9997     });
9998 })();
9999
10000
10001
10002
10003 if(Roo.DomQuery){
10004     Roo.Element.selectorFunction = Roo.DomQuery.select;
10005 }
10006
10007 Roo.Element.select = function(selector, unique, root){
10008     var els;
10009     if(typeof selector == "string"){
10010         els = Roo.Element.selectorFunction(selector, root);
10011     }else if(selector.length !== undefined){
10012         els = selector;
10013     }else{
10014         throw "Invalid selector";
10015     }
10016     if(unique === true){
10017         return new Roo.CompositeElement(els);
10018     }else{
10019         return new Roo.CompositeElementLite(els);
10020     }
10021 };
10022 /**
10023  * Selects elements based on the passed CSS selector to enable working on them as 1.
10024  * @param {String/Array} selector The CSS selector or an array of elements
10025  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10026  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10027  * @return {CompositeElementLite/CompositeElement}
10028  * @member Roo
10029  * @method select
10030  */
10031 Roo.select = Roo.Element.select;
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046 /*
10047  * Based on:
10048  * Ext JS Library 1.1.1
10049  * Copyright(c) 2006-2007, Ext JS, LLC.
10050  *
10051  * Originally Released Under LGPL - original licence link has changed is not relivant.
10052  *
10053  * Fork - LGPL
10054  * <script type="text/javascript">
10055  */
10056
10057
10058
10059 //Notifies Element that fx methods are available
10060 Roo.enableFx = true;
10061
10062 /**
10063  * @class Roo.Fx
10064  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10065  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10066  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10067  * Element effects to work.</p><br/>
10068  *
10069  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10070  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10071  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10072  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10073  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10074  * expected results and should be done with care.</p><br/>
10075  *
10076  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10077  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10078 <pre>
10079 Value  Description
10080 -----  -----------------------------
10081 tl     The top left corner
10082 t      The center of the top edge
10083 tr     The top right corner
10084 l      The center of the left edge
10085 r      The center of the right edge
10086 bl     The bottom left corner
10087 b      The center of the bottom edge
10088 br     The bottom right corner
10089 </pre>
10090  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10091  * below are common options that can be passed to any Fx method.</b>
10092  * @cfg {Function} callback A function called when the effect is finished
10093  * @cfg {Object} scope The scope of the effect function
10094  * @cfg {String} easing A valid Easing value for the effect
10095  * @cfg {String} afterCls A css class to apply after the effect
10096  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10097  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10098  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10099  * effects that end with the element being visually hidden, ignored otherwise)
10100  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10101  * a function which returns such a specification that will be applied to the Element after the effect finishes
10102  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10103  * @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
10104  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10105  */
10106 Roo.Fx = {
10107         /**
10108          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10109          * origin for the slide effect.  This function automatically handles wrapping the element with
10110          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10111          * Usage:
10112          *<pre><code>
10113 // default: slide the element in from the top
10114 el.slideIn();
10115
10116 // custom: slide the element in from the right with a 2-second duration
10117 el.slideIn('r', { duration: 2 });
10118
10119 // common config options shown with default values
10120 el.slideIn('t', {
10121     easing: 'easeOut',
10122     duration: .5
10123 });
10124 </code></pre>
10125          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10126          * @param {Object} options (optional) Object literal with any of the Fx config options
10127          * @return {Roo.Element} The Element
10128          */
10129     slideIn : function(anchor, o){
10130         var el = this.getFxEl();
10131         o = o || {};
10132
10133         el.queueFx(o, function(){
10134
10135             anchor = anchor || "t";
10136
10137             // fix display to visibility
10138             this.fixDisplay();
10139
10140             // restore values after effect
10141             var r = this.getFxRestore();
10142             var b = this.getBox();
10143             // fixed size for slide
10144             this.setSize(b);
10145
10146             // wrap if needed
10147             var wrap = this.fxWrap(r.pos, o, "hidden");
10148
10149             var st = this.dom.style;
10150             st.visibility = "visible";
10151             st.position = "absolute";
10152
10153             // clear out temp styles after slide and unwrap
10154             var after = function(){
10155                 el.fxUnwrap(wrap, r.pos, o);
10156                 st.width = r.width;
10157                 st.height = r.height;
10158                 el.afterFx(o);
10159             };
10160             // time to calc the positions
10161             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10162
10163             switch(anchor.toLowerCase()){
10164                 case "t":
10165                     wrap.setSize(b.width, 0);
10166                     st.left = st.bottom = "0";
10167                     a = {height: bh};
10168                 break;
10169                 case "l":
10170                     wrap.setSize(0, b.height);
10171                     st.right = st.top = "0";
10172                     a = {width: bw};
10173                 break;
10174                 case "r":
10175                     wrap.setSize(0, b.height);
10176                     wrap.setX(b.right);
10177                     st.left = st.top = "0";
10178                     a = {width: bw, points: pt};
10179                 break;
10180                 case "b":
10181                     wrap.setSize(b.width, 0);
10182                     wrap.setY(b.bottom);
10183                     st.left = st.top = "0";
10184                     a = {height: bh, points: pt};
10185                 break;
10186                 case "tl":
10187                     wrap.setSize(0, 0);
10188                     st.right = st.bottom = "0";
10189                     a = {width: bw, height: bh};
10190                 break;
10191                 case "bl":
10192                     wrap.setSize(0, 0);
10193                     wrap.setY(b.y+b.height);
10194                     st.right = st.top = "0";
10195                     a = {width: bw, height: bh, points: pt};
10196                 break;
10197                 case "br":
10198                     wrap.setSize(0, 0);
10199                     wrap.setXY([b.right, b.bottom]);
10200                     st.left = st.top = "0";
10201                     a = {width: bw, height: bh, points: pt};
10202                 break;
10203                 case "tr":
10204                     wrap.setSize(0, 0);
10205                     wrap.setX(b.x+b.width);
10206                     st.left = st.bottom = "0";
10207                     a = {width: bw, height: bh, points: pt};
10208                 break;
10209             }
10210             this.dom.style.visibility = "visible";
10211             wrap.show();
10212
10213             arguments.callee.anim = wrap.fxanim(a,
10214                 o,
10215                 'motion',
10216                 .5,
10217                 'easeOut', after);
10218         });
10219         return this;
10220     },
10221     
10222         /**
10223          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10224          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10225          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10226          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10227          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10228          * Usage:
10229          *<pre><code>
10230 // default: slide the element out to the top
10231 el.slideOut();
10232
10233 // custom: slide the element out to the right with a 2-second duration
10234 el.slideOut('r', { duration: 2 });
10235
10236 // common config options shown with default values
10237 el.slideOut('t', {
10238     easing: 'easeOut',
10239     duration: .5,
10240     remove: false,
10241     useDisplay: false
10242 });
10243 </code></pre>
10244          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10245          * @param {Object} options (optional) Object literal with any of the Fx config options
10246          * @return {Roo.Element} The Element
10247          */
10248     slideOut : function(anchor, o){
10249         var el = this.getFxEl();
10250         o = o || {};
10251
10252         el.queueFx(o, function(){
10253
10254             anchor = anchor || "t";
10255
10256             // restore values after effect
10257             var r = this.getFxRestore();
10258             
10259             var b = this.getBox();
10260             // fixed size for slide
10261             this.setSize(b);
10262
10263             // wrap if needed
10264             var wrap = this.fxWrap(r.pos, o, "visible");
10265
10266             var st = this.dom.style;
10267             st.visibility = "visible";
10268             st.position = "absolute";
10269
10270             wrap.setSize(b);
10271
10272             var after = function(){
10273                 if(o.useDisplay){
10274                     el.setDisplayed(false);
10275                 }else{
10276                     el.hide();
10277                 }
10278
10279                 el.fxUnwrap(wrap, r.pos, o);
10280
10281                 st.width = r.width;
10282                 st.height = r.height;
10283
10284                 el.afterFx(o);
10285             };
10286
10287             var a, zero = {to: 0};
10288             switch(anchor.toLowerCase()){
10289                 case "t":
10290                     st.left = st.bottom = "0";
10291                     a = {height: zero};
10292                 break;
10293                 case "l":
10294                     st.right = st.top = "0";
10295                     a = {width: zero};
10296                 break;
10297                 case "r":
10298                     st.left = st.top = "0";
10299                     a = {width: zero, points: {to:[b.right, b.y]}};
10300                 break;
10301                 case "b":
10302                     st.left = st.top = "0";
10303                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10304                 break;
10305                 case "tl":
10306                     st.right = st.bottom = "0";
10307                     a = {width: zero, height: zero};
10308                 break;
10309                 case "bl":
10310                     st.right = st.top = "0";
10311                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10312                 break;
10313                 case "br":
10314                     st.left = st.top = "0";
10315                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10316                 break;
10317                 case "tr":
10318                     st.left = st.bottom = "0";
10319                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10320                 break;
10321             }
10322
10323             arguments.callee.anim = wrap.fxanim(a,
10324                 o,
10325                 'motion',
10326                 .5,
10327                 "easeOut", after);
10328         });
10329         return this;
10330     },
10331
10332         /**
10333          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10334          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10335          * The element must be removed from the DOM using the 'remove' config option if desired.
10336          * Usage:
10337          *<pre><code>
10338 // default
10339 el.puff();
10340
10341 // common config options shown with default values
10342 el.puff({
10343     easing: 'easeOut',
10344     duration: .5,
10345     remove: false,
10346     useDisplay: false
10347 });
10348 </code></pre>
10349          * @param {Object} options (optional) Object literal with any of the Fx config options
10350          * @return {Roo.Element} The Element
10351          */
10352     puff : function(o){
10353         var el = this.getFxEl();
10354         o = o || {};
10355
10356         el.queueFx(o, function(){
10357             this.clearOpacity();
10358             this.show();
10359
10360             // restore values after effect
10361             var r = this.getFxRestore();
10362             var st = this.dom.style;
10363
10364             var after = function(){
10365                 if(o.useDisplay){
10366                     el.setDisplayed(false);
10367                 }else{
10368                     el.hide();
10369                 }
10370
10371                 el.clearOpacity();
10372
10373                 el.setPositioning(r.pos);
10374                 st.width = r.width;
10375                 st.height = r.height;
10376                 st.fontSize = '';
10377                 el.afterFx(o);
10378             };
10379
10380             var width = this.getWidth();
10381             var height = this.getHeight();
10382
10383             arguments.callee.anim = this.fxanim({
10384                     width : {to: this.adjustWidth(width * 2)},
10385                     height : {to: this.adjustHeight(height * 2)},
10386                     points : {by: [-(width * .5), -(height * .5)]},
10387                     opacity : {to: 0},
10388                     fontSize: {to:200, unit: "%"}
10389                 },
10390                 o,
10391                 'motion',
10392                 .5,
10393                 "easeOut", after);
10394         });
10395         return this;
10396     },
10397
10398         /**
10399          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10400          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10401          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10402          * Usage:
10403          *<pre><code>
10404 // default
10405 el.switchOff();
10406
10407 // all config options shown with default values
10408 el.switchOff({
10409     easing: 'easeIn',
10410     duration: .3,
10411     remove: false,
10412     useDisplay: false
10413 });
10414 </code></pre>
10415          * @param {Object} options (optional) Object literal with any of the Fx config options
10416          * @return {Roo.Element} The Element
10417          */
10418     switchOff : function(o){
10419         var el = this.getFxEl();
10420         o = o || {};
10421
10422         el.queueFx(o, function(){
10423             this.clearOpacity();
10424             this.clip();
10425
10426             // restore values after effect
10427             var r = this.getFxRestore();
10428             var st = this.dom.style;
10429
10430             var after = function(){
10431                 if(o.useDisplay){
10432                     el.setDisplayed(false);
10433                 }else{
10434                     el.hide();
10435                 }
10436
10437                 el.clearOpacity();
10438                 el.setPositioning(r.pos);
10439                 st.width = r.width;
10440                 st.height = r.height;
10441
10442                 el.afterFx(o);
10443             };
10444
10445             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10446                 this.clearOpacity();
10447                 (function(){
10448                     this.fxanim({
10449                         height:{to:1},
10450                         points:{by:[0, this.getHeight() * .5]}
10451                     }, o, 'motion', 0.3, 'easeIn', after);
10452                 }).defer(100, this);
10453             });
10454         });
10455         return this;
10456     },
10457
10458     /**
10459      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10460      * changed using the "attr" config option) and then fading back to the original color. If no original
10461      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10462      * Usage:
10463 <pre><code>
10464 // default: highlight background to yellow
10465 el.highlight();
10466
10467 // custom: highlight foreground text to blue for 2 seconds
10468 el.highlight("0000ff", { attr: 'color', duration: 2 });
10469
10470 // common config options shown with default values
10471 el.highlight("ffff9c", {
10472     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10473     endColor: (current color) or "ffffff",
10474     easing: 'easeIn',
10475     duration: 1
10476 });
10477 </code></pre>
10478      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10479      * @param {Object} options (optional) Object literal with any of the Fx config options
10480      * @return {Roo.Element} The Element
10481      */ 
10482     highlight : function(color, o){
10483         var el = this.getFxEl();
10484         o = o || {};
10485
10486         el.queueFx(o, function(){
10487             color = color || "ffff9c";
10488             attr = o.attr || "backgroundColor";
10489
10490             this.clearOpacity();
10491             this.show();
10492
10493             var origColor = this.getColor(attr);
10494             var restoreColor = this.dom.style[attr];
10495             endColor = (o.endColor || origColor) || "ffffff";
10496
10497             var after = function(){
10498                 el.dom.style[attr] = restoreColor;
10499                 el.afterFx(o);
10500             };
10501
10502             var a = {};
10503             a[attr] = {from: color, to: endColor};
10504             arguments.callee.anim = this.fxanim(a,
10505                 o,
10506                 'color',
10507                 1,
10508                 'easeIn', after);
10509         });
10510         return this;
10511     },
10512
10513    /**
10514     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10515     * Usage:
10516 <pre><code>
10517 // default: a single light blue ripple
10518 el.frame();
10519
10520 // custom: 3 red ripples lasting 3 seconds total
10521 el.frame("ff0000", 3, { duration: 3 });
10522
10523 // common config options shown with default values
10524 el.frame("C3DAF9", 1, {
10525     duration: 1 //duration of entire animation (not each individual ripple)
10526     // Note: Easing is not configurable and will be ignored if included
10527 });
10528 </code></pre>
10529     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10530     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10531     * @param {Object} options (optional) Object literal with any of the Fx config options
10532     * @return {Roo.Element} The Element
10533     */
10534     frame : function(color, count, o){
10535         var el = this.getFxEl();
10536         o = o || {};
10537
10538         el.queueFx(o, function(){
10539             color = color || "#C3DAF9";
10540             if(color.length == 6){
10541                 color = "#" + color;
10542             }
10543             count = count || 1;
10544             duration = o.duration || 1;
10545             this.show();
10546
10547             var b = this.getBox();
10548             var animFn = function(){
10549                 var proxy = this.createProxy({
10550
10551                      style:{
10552                         visbility:"hidden",
10553                         position:"absolute",
10554                         "z-index":"35000", // yee haw
10555                         border:"0px solid " + color
10556                      }
10557                   });
10558                 var scale = Roo.isBorderBox ? 2 : 1;
10559                 proxy.animate({
10560                     top:{from:b.y, to:b.y - 20},
10561                     left:{from:b.x, to:b.x - 20},
10562                     borderWidth:{from:0, to:10},
10563                     opacity:{from:1, to:0},
10564                     height:{from:b.height, to:(b.height + (20*scale))},
10565                     width:{from:b.width, to:(b.width + (20*scale))}
10566                 }, duration, function(){
10567                     proxy.remove();
10568                 });
10569                 if(--count > 0){
10570                      animFn.defer((duration/2)*1000, this);
10571                 }else{
10572                     el.afterFx(o);
10573                 }
10574             };
10575             animFn.call(this);
10576         });
10577         return this;
10578     },
10579
10580    /**
10581     * Creates a pause before any subsequent queued effects begin.  If there are
10582     * no effects queued after the pause it will have no effect.
10583     * Usage:
10584 <pre><code>
10585 el.pause(1);
10586 </code></pre>
10587     * @param {Number} seconds The length of time to pause (in seconds)
10588     * @return {Roo.Element} The Element
10589     */
10590     pause : function(seconds){
10591         var el = this.getFxEl();
10592         var o = {};
10593
10594         el.queueFx(o, function(){
10595             setTimeout(function(){
10596                 el.afterFx(o);
10597             }, seconds * 1000);
10598         });
10599         return this;
10600     },
10601
10602    /**
10603     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10604     * using the "endOpacity" config option.
10605     * Usage:
10606 <pre><code>
10607 // default: fade in from opacity 0 to 100%
10608 el.fadeIn();
10609
10610 // custom: fade in from opacity 0 to 75% over 2 seconds
10611 el.fadeIn({ endOpacity: .75, duration: 2});
10612
10613 // common config options shown with default values
10614 el.fadeIn({
10615     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10616     easing: 'easeOut',
10617     duration: .5
10618 });
10619 </code></pre>
10620     * @param {Object} options (optional) Object literal with any of the Fx config options
10621     * @return {Roo.Element} The Element
10622     */
10623     fadeIn : function(o){
10624         var el = this.getFxEl();
10625         o = o || {};
10626         el.queueFx(o, function(){
10627             this.setOpacity(0);
10628             this.fixDisplay();
10629             this.dom.style.visibility = 'visible';
10630             var to = o.endOpacity || 1;
10631             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10632                 o, null, .5, "easeOut", function(){
10633                 if(to == 1){
10634                     this.clearOpacity();
10635                 }
10636                 el.afterFx(o);
10637             });
10638         });
10639         return this;
10640     },
10641
10642    /**
10643     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10644     * using the "endOpacity" config option.
10645     * Usage:
10646 <pre><code>
10647 // default: fade out from the element's current opacity to 0
10648 el.fadeOut();
10649
10650 // custom: fade out from the element's current opacity to 25% over 2 seconds
10651 el.fadeOut({ endOpacity: .25, duration: 2});
10652
10653 // common config options shown with default values
10654 el.fadeOut({
10655     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10656     easing: 'easeOut',
10657     duration: .5
10658     remove: false,
10659     useDisplay: false
10660 });
10661 </code></pre>
10662     * @param {Object} options (optional) Object literal with any of the Fx config options
10663     * @return {Roo.Element} The Element
10664     */
10665     fadeOut : function(o){
10666         var el = this.getFxEl();
10667         o = o || {};
10668         el.queueFx(o, function(){
10669             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10670                 o, null, .5, "easeOut", function(){
10671                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10672                      this.dom.style.display = "none";
10673                 }else{
10674                      this.dom.style.visibility = "hidden";
10675                 }
10676                 this.clearOpacity();
10677                 el.afterFx(o);
10678             });
10679         });
10680         return this;
10681     },
10682
10683    /**
10684     * Animates the transition of an element's dimensions from a starting height/width
10685     * to an ending height/width.
10686     * Usage:
10687 <pre><code>
10688 // change height and width to 100x100 pixels
10689 el.scale(100, 100);
10690
10691 // common config options shown with default values.  The height and width will default to
10692 // the element's existing values if passed as null.
10693 el.scale(
10694     [element's width],
10695     [element's height], {
10696     easing: 'easeOut',
10697     duration: .35
10698 });
10699 </code></pre>
10700     * @param {Number} width  The new width (pass undefined to keep the original width)
10701     * @param {Number} height  The new height (pass undefined to keep the original height)
10702     * @param {Object} options (optional) Object literal with any of the Fx config options
10703     * @return {Roo.Element} The Element
10704     */
10705     scale : function(w, h, o){
10706         this.shift(Roo.apply({}, o, {
10707             width: w,
10708             height: h
10709         }));
10710         return this;
10711     },
10712
10713    /**
10714     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10715     * Any of these properties not specified in the config object will not be changed.  This effect 
10716     * requires that at least one new dimension, position or opacity setting must be passed in on
10717     * the config object in order for the function to have any effect.
10718     * Usage:
10719 <pre><code>
10720 // slide the element horizontally to x position 200 while changing the height and opacity
10721 el.shift({ x: 200, height: 50, opacity: .8 });
10722
10723 // common config options shown with default values.
10724 el.shift({
10725     width: [element's width],
10726     height: [element's height],
10727     x: [element's x position],
10728     y: [element's y position],
10729     opacity: [element's opacity],
10730     easing: 'easeOut',
10731     duration: .35
10732 });
10733 </code></pre>
10734     * @param {Object} options  Object literal with any of the Fx config options
10735     * @return {Roo.Element} The Element
10736     */
10737     shift : function(o){
10738         var el = this.getFxEl();
10739         o = o || {};
10740         el.queueFx(o, function(){
10741             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10742             if(w !== undefined){
10743                 a.width = {to: this.adjustWidth(w)};
10744             }
10745             if(h !== undefined){
10746                 a.height = {to: this.adjustHeight(h)};
10747             }
10748             if(x !== undefined || y !== undefined){
10749                 a.points = {to: [
10750                     x !== undefined ? x : this.getX(),
10751                     y !== undefined ? y : this.getY()
10752                 ]};
10753             }
10754             if(op !== undefined){
10755                 a.opacity = {to: op};
10756             }
10757             if(o.xy !== undefined){
10758                 a.points = {to: o.xy};
10759             }
10760             arguments.callee.anim = this.fxanim(a,
10761                 o, 'motion', .35, "easeOut", function(){
10762                 el.afterFx(o);
10763             });
10764         });
10765         return this;
10766     },
10767
10768         /**
10769          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10770          * ending point of the effect.
10771          * Usage:
10772          *<pre><code>
10773 // default: slide the element downward while fading out
10774 el.ghost();
10775
10776 // custom: slide the element out to the right with a 2-second duration
10777 el.ghost('r', { duration: 2 });
10778
10779 // common config options shown with default values
10780 el.ghost('b', {
10781     easing: 'easeOut',
10782     duration: .5
10783     remove: false,
10784     useDisplay: false
10785 });
10786 </code></pre>
10787          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10788          * @param {Object} options (optional) Object literal with any of the Fx config options
10789          * @return {Roo.Element} The Element
10790          */
10791     ghost : function(anchor, o){
10792         var el = this.getFxEl();
10793         o = o || {};
10794
10795         el.queueFx(o, function(){
10796             anchor = anchor || "b";
10797
10798             // restore values after effect
10799             var r = this.getFxRestore();
10800             var w = this.getWidth(),
10801                 h = this.getHeight();
10802
10803             var st = this.dom.style;
10804
10805             var after = function(){
10806                 if(o.useDisplay){
10807                     el.setDisplayed(false);
10808                 }else{
10809                     el.hide();
10810                 }
10811
10812                 el.clearOpacity();
10813                 el.setPositioning(r.pos);
10814                 st.width = r.width;
10815                 st.height = r.height;
10816
10817                 el.afterFx(o);
10818             };
10819
10820             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10821             switch(anchor.toLowerCase()){
10822                 case "t":
10823                     pt.by = [0, -h];
10824                 break;
10825                 case "l":
10826                     pt.by = [-w, 0];
10827                 break;
10828                 case "r":
10829                     pt.by = [w, 0];
10830                 break;
10831                 case "b":
10832                     pt.by = [0, h];
10833                 break;
10834                 case "tl":
10835                     pt.by = [-w, -h];
10836                 break;
10837                 case "bl":
10838                     pt.by = [-w, h];
10839                 break;
10840                 case "br":
10841                     pt.by = [w, h];
10842                 break;
10843                 case "tr":
10844                     pt.by = [w, -h];
10845                 break;
10846             }
10847
10848             arguments.callee.anim = this.fxanim(a,
10849                 o,
10850                 'motion',
10851                 .5,
10852                 "easeOut", after);
10853         });
10854         return this;
10855     },
10856
10857         /**
10858          * Ensures that all effects queued after syncFx is called on the element are
10859          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10860          * @return {Roo.Element} The Element
10861          */
10862     syncFx : function(){
10863         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10864             block : false,
10865             concurrent : true,
10866             stopFx : false
10867         });
10868         return this;
10869     },
10870
10871         /**
10872          * Ensures that all effects queued after sequenceFx is called on the element are
10873          * run in sequence.  This is the opposite of {@link #syncFx}.
10874          * @return {Roo.Element} The Element
10875          */
10876     sequenceFx : function(){
10877         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10878             block : false,
10879             concurrent : false,
10880             stopFx : false
10881         });
10882         return this;
10883     },
10884
10885         /* @private */
10886     nextFx : function(){
10887         var ef = this.fxQueue[0];
10888         if(ef){
10889             ef.call(this);
10890         }
10891     },
10892
10893         /**
10894          * Returns true if the element has any effects actively running or queued, else returns false.
10895          * @return {Boolean} True if element has active effects, else false
10896          */
10897     hasActiveFx : function(){
10898         return this.fxQueue && this.fxQueue[0];
10899     },
10900
10901         /**
10902          * Stops any running effects and clears the element's internal effects queue if it contains
10903          * any additional effects that haven't started yet.
10904          * @return {Roo.Element} The Element
10905          */
10906     stopFx : function(){
10907         if(this.hasActiveFx()){
10908             var cur = this.fxQueue[0];
10909             if(cur && cur.anim && cur.anim.isAnimated()){
10910                 this.fxQueue = [cur]; // clear out others
10911                 cur.anim.stop(true);
10912             }
10913         }
10914         return this;
10915     },
10916
10917         /* @private */
10918     beforeFx : function(o){
10919         if(this.hasActiveFx() && !o.concurrent){
10920            if(o.stopFx){
10921                this.stopFx();
10922                return true;
10923            }
10924            return false;
10925         }
10926         return true;
10927     },
10928
10929         /**
10930          * Returns true if the element is currently blocking so that no other effect can be queued
10931          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10932          * used to ensure that an effect initiated by a user action runs to completion prior to the
10933          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10934          * @return {Boolean} True if blocking, else false
10935          */
10936     hasFxBlock : function(){
10937         var q = this.fxQueue;
10938         return q && q[0] && q[0].block;
10939     },
10940
10941         /* @private */
10942     queueFx : function(o, fn){
10943         if(!this.fxQueue){
10944             this.fxQueue = [];
10945         }
10946         if(!this.hasFxBlock()){
10947             Roo.applyIf(o, this.fxDefaults);
10948             if(!o.concurrent){
10949                 var run = this.beforeFx(o);
10950                 fn.block = o.block;
10951                 this.fxQueue.push(fn);
10952                 if(run){
10953                     this.nextFx();
10954                 }
10955             }else{
10956                 fn.call(this);
10957             }
10958         }
10959         return this;
10960     },
10961
10962         /* @private */
10963     fxWrap : function(pos, o, vis){
10964         var wrap;
10965         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10966             var wrapXY;
10967             if(o.fixPosition){
10968                 wrapXY = this.getXY();
10969             }
10970             var div = document.createElement("div");
10971             div.style.visibility = vis;
10972             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10973             wrap.setPositioning(pos);
10974             if(wrap.getStyle("position") == "static"){
10975                 wrap.position("relative");
10976             }
10977             this.clearPositioning('auto');
10978             wrap.clip();
10979             wrap.dom.appendChild(this.dom);
10980             if(wrapXY){
10981                 wrap.setXY(wrapXY);
10982             }
10983         }
10984         return wrap;
10985     },
10986
10987         /* @private */
10988     fxUnwrap : function(wrap, pos, o){
10989         this.clearPositioning();
10990         this.setPositioning(pos);
10991         if(!o.wrap){
10992             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10993             wrap.remove();
10994         }
10995     },
10996
10997         /* @private */
10998     getFxRestore : function(){
10999         var st = this.dom.style;
11000         return {pos: this.getPositioning(), width: st.width, height : st.height};
11001     },
11002
11003         /* @private */
11004     afterFx : function(o){
11005         if(o.afterStyle){
11006             this.applyStyles(o.afterStyle);
11007         }
11008         if(o.afterCls){
11009             this.addClass(o.afterCls);
11010         }
11011         if(o.remove === true){
11012             this.remove();
11013         }
11014         Roo.callback(o.callback, o.scope, [this]);
11015         if(!o.concurrent){
11016             this.fxQueue.shift();
11017             this.nextFx();
11018         }
11019     },
11020
11021         /* @private */
11022     getFxEl : function(){ // support for composite element fx
11023         return Roo.get(this.dom);
11024     },
11025
11026         /* @private */
11027     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11028         animType = animType || 'run';
11029         opt = opt || {};
11030         var anim = Roo.lib.Anim[animType](
11031             this.dom, args,
11032             (opt.duration || defaultDur) || .35,
11033             (opt.easing || defaultEase) || 'easeOut',
11034             function(){
11035                 Roo.callback(cb, this);
11036             },
11037             this
11038         );
11039         opt.anim = anim;
11040         return anim;
11041     }
11042 };
11043
11044 // backwords compat
11045 Roo.Fx.resize = Roo.Fx.scale;
11046
11047 //When included, Roo.Fx is automatically applied to Element so that all basic
11048 //effects are available directly via the Element API
11049 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11050  * Based on:
11051  * Ext JS Library 1.1.1
11052  * Copyright(c) 2006-2007, Ext JS, LLC.
11053  *
11054  * Originally Released Under LGPL - original licence link has changed is not relivant.
11055  *
11056  * Fork - LGPL
11057  * <script type="text/javascript">
11058  */
11059
11060
11061 /**
11062  * @class Roo.CompositeElement
11063  * Standard composite class. Creates a Roo.Element for every element in the collection.
11064  * <br><br>
11065  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11066  * actions will be performed on all the elements in this collection.</b>
11067  * <br><br>
11068  * All methods return <i>this</i> and can be chained.
11069  <pre><code>
11070  var els = Roo.select("#some-el div.some-class", true);
11071  // or select directly from an existing element
11072  var el = Roo.get('some-el');
11073  el.select('div.some-class', true);
11074
11075  els.setWidth(100); // all elements become 100 width
11076  els.hide(true); // all elements fade out and hide
11077  // or
11078  els.setWidth(100).hide(true);
11079  </code></pre>
11080  */
11081 Roo.CompositeElement = function(els){
11082     this.elements = [];
11083     this.addElements(els);
11084 };
11085 Roo.CompositeElement.prototype = {
11086     isComposite: true,
11087     addElements : function(els){
11088         if(!els) {
11089             return this;
11090         }
11091         if(typeof els == "string"){
11092             els = Roo.Element.selectorFunction(els);
11093         }
11094         var yels = this.elements;
11095         var index = yels.length-1;
11096         for(var i = 0, len = els.length; i < len; i++) {
11097                 yels[++index] = Roo.get(els[i]);
11098         }
11099         return this;
11100     },
11101
11102     /**
11103     * Clears this composite and adds the elements returned by the passed selector.
11104     * @param {String/Array} els A string CSS selector, an array of elements or an element
11105     * @return {CompositeElement} this
11106     */
11107     fill : function(els){
11108         this.elements = [];
11109         this.add(els);
11110         return this;
11111     },
11112
11113     /**
11114     * Filters this composite to only elements that match the passed selector.
11115     * @param {String} selector A string CSS selector
11116     * @param {Boolean} inverse return inverse filter (not matches)
11117     * @return {CompositeElement} this
11118     */
11119     filter : function(selector, inverse){
11120         var els = [];
11121         inverse = inverse || false;
11122         this.each(function(el){
11123             var match = inverse ? !el.is(selector) : el.is(selector);
11124             if(match){
11125                 els[els.length] = el.dom;
11126             }
11127         });
11128         this.fill(els);
11129         return this;
11130     },
11131
11132     invoke : function(fn, args){
11133         var els = this.elements;
11134         for(var i = 0, len = els.length; i < len; i++) {
11135                 Roo.Element.prototype[fn].apply(els[i], args);
11136         }
11137         return this;
11138     },
11139     /**
11140     * Adds elements to this composite.
11141     * @param {String/Array} els A string CSS selector, an array of elements or an element
11142     * @return {CompositeElement} this
11143     */
11144     add : function(els){
11145         if(typeof els == "string"){
11146             this.addElements(Roo.Element.selectorFunction(els));
11147         }else if(els.length !== undefined){
11148             this.addElements(els);
11149         }else{
11150             this.addElements([els]);
11151         }
11152         return this;
11153     },
11154     /**
11155     * Calls the passed function passing (el, this, index) for each element in this composite.
11156     * @param {Function} fn The function to call
11157     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11158     * @return {CompositeElement} this
11159     */
11160     each : function(fn, scope){
11161         var els = this.elements;
11162         for(var i = 0, len = els.length; i < len; i++){
11163             if(fn.call(scope || els[i], els[i], this, i) === false) {
11164                 break;
11165             }
11166         }
11167         return this;
11168     },
11169
11170     /**
11171      * Returns the Element object at the specified index
11172      * @param {Number} index
11173      * @return {Roo.Element}
11174      */
11175     item : function(index){
11176         return this.elements[index] || null;
11177     },
11178
11179     /**
11180      * Returns the first Element
11181      * @return {Roo.Element}
11182      */
11183     first : function(){
11184         return this.item(0);
11185     },
11186
11187     /**
11188      * Returns the last Element
11189      * @return {Roo.Element}
11190      */
11191     last : function(){
11192         return this.item(this.elements.length-1);
11193     },
11194
11195     /**
11196      * Returns the number of elements in this composite
11197      * @return Number
11198      */
11199     getCount : function(){
11200         return this.elements.length;
11201     },
11202
11203     /**
11204      * Returns true if this composite contains the passed element
11205      * @return Boolean
11206      */
11207     contains : function(el){
11208         return this.indexOf(el) !== -1;
11209     },
11210
11211     /**
11212      * Returns true if this composite contains the passed element
11213      * @return Boolean
11214      */
11215     indexOf : function(el){
11216         return this.elements.indexOf(Roo.get(el));
11217     },
11218
11219
11220     /**
11221     * Removes the specified element(s).
11222     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11223     * or an array of any of those.
11224     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11225     * @return {CompositeElement} this
11226     */
11227     removeElement : function(el, removeDom){
11228         if(el instanceof Array){
11229             for(var i = 0, len = el.length; i < len; i++){
11230                 this.removeElement(el[i]);
11231             }
11232             return this;
11233         }
11234         var index = typeof el == 'number' ? el : this.indexOf(el);
11235         if(index !== -1){
11236             if(removeDom){
11237                 var d = this.elements[index];
11238                 if(d.dom){
11239                     d.remove();
11240                 }else{
11241                     d.parentNode.removeChild(d);
11242                 }
11243             }
11244             this.elements.splice(index, 1);
11245         }
11246         return this;
11247     },
11248
11249     /**
11250     * Replaces the specified element with the passed element.
11251     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11252     * to replace.
11253     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11254     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11255     * @return {CompositeElement} this
11256     */
11257     replaceElement : function(el, replacement, domReplace){
11258         var index = typeof el == 'number' ? el : this.indexOf(el);
11259         if(index !== -1){
11260             if(domReplace){
11261                 this.elements[index].replaceWith(replacement);
11262             }else{
11263                 this.elements.splice(index, 1, Roo.get(replacement))
11264             }
11265         }
11266         return this;
11267     },
11268
11269     /**
11270      * Removes all elements.
11271      */
11272     clear : function(){
11273         this.elements = [];
11274     }
11275 };
11276 (function(){
11277     Roo.CompositeElement.createCall = function(proto, fnName){
11278         if(!proto[fnName]){
11279             proto[fnName] = function(){
11280                 return this.invoke(fnName, arguments);
11281             };
11282         }
11283     };
11284     for(var fnName in Roo.Element.prototype){
11285         if(typeof Roo.Element.prototype[fnName] == "function"){
11286             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11287         }
11288     };
11289 })();
11290 /*
11291  * Based on:
11292  * Ext JS Library 1.1.1
11293  * Copyright(c) 2006-2007, Ext JS, LLC.
11294  *
11295  * Originally Released Under LGPL - original licence link has changed is not relivant.
11296  *
11297  * Fork - LGPL
11298  * <script type="text/javascript">
11299  */
11300
11301 /**
11302  * @class Roo.CompositeElementLite
11303  * @extends Roo.CompositeElement
11304  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11305  <pre><code>
11306  var els = Roo.select("#some-el div.some-class");
11307  // or select directly from an existing element
11308  var el = Roo.get('some-el');
11309  el.select('div.some-class');
11310
11311  els.setWidth(100); // all elements become 100 width
11312  els.hide(true); // all elements fade out and hide
11313  // or
11314  els.setWidth(100).hide(true);
11315  </code></pre><br><br>
11316  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11317  * actions will be performed on all the elements in this collection.</b>
11318  */
11319 Roo.CompositeElementLite = function(els){
11320     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11321     this.el = new Roo.Element.Flyweight();
11322 };
11323 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11324     addElements : function(els){
11325         if(els){
11326             if(els instanceof Array){
11327                 this.elements = this.elements.concat(els);
11328             }else{
11329                 var yels = this.elements;
11330                 var index = yels.length-1;
11331                 for(var i = 0, len = els.length; i < len; i++) {
11332                     yels[++index] = els[i];
11333                 }
11334             }
11335         }
11336         return this;
11337     },
11338     invoke : function(fn, args){
11339         var els = this.elements;
11340         var el = this.el;
11341         for(var i = 0, len = els.length; i < len; i++) {
11342             el.dom = els[i];
11343                 Roo.Element.prototype[fn].apply(el, args);
11344         }
11345         return this;
11346     },
11347     /**
11348      * Returns a flyweight Element of the dom element object at the specified index
11349      * @param {Number} index
11350      * @return {Roo.Element}
11351      */
11352     item : function(index){
11353         if(!this.elements[index]){
11354             return null;
11355         }
11356         this.el.dom = this.elements[index];
11357         return this.el;
11358     },
11359
11360     // fixes scope with flyweight
11361     addListener : function(eventName, handler, scope, opt){
11362         var els = this.elements;
11363         for(var i = 0, len = els.length; i < len; i++) {
11364             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11365         }
11366         return this;
11367     },
11368
11369     /**
11370     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11371     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11372     * a reference to the dom node, use el.dom.</b>
11373     * @param {Function} fn The function to call
11374     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11375     * @return {CompositeElement} this
11376     */
11377     each : function(fn, scope){
11378         var els = this.elements;
11379         var el = this.el;
11380         for(var i = 0, len = els.length; i < len; i++){
11381             el.dom = els[i];
11382                 if(fn.call(scope || el, el, this, i) === false){
11383                 break;
11384             }
11385         }
11386         return this;
11387     },
11388
11389     indexOf : function(el){
11390         return this.elements.indexOf(Roo.getDom(el));
11391     },
11392
11393     replaceElement : function(el, replacement, domReplace){
11394         var index = typeof el == 'number' ? el : this.indexOf(el);
11395         if(index !== -1){
11396             replacement = Roo.getDom(replacement);
11397             if(domReplace){
11398                 var d = this.elements[index];
11399                 d.parentNode.insertBefore(replacement, d);
11400                 d.parentNode.removeChild(d);
11401             }
11402             this.elements.splice(index, 1, replacement);
11403         }
11404         return this;
11405     }
11406 });
11407 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11408
11409 /*
11410  * Based on:
11411  * Ext JS Library 1.1.1
11412  * Copyright(c) 2006-2007, Ext JS, LLC.
11413  *
11414  * Originally Released Under LGPL - original licence link has changed is not relivant.
11415  *
11416  * Fork - LGPL
11417  * <script type="text/javascript">
11418  */
11419
11420  
11421
11422 /**
11423  * @class Roo.data.Connection
11424  * @extends Roo.util.Observable
11425  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11426  * either to a configured URL, or to a URL specified at request time.<br><br>
11427  * <p>
11428  * Requests made by this class are asynchronous, and will return immediately. No data from
11429  * the server will be available to the statement immediately following the {@link #request} call.
11430  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11431  * <p>
11432  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11433  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11434  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11435  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11436  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11437  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11438  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11439  * standard DOM methods.
11440  * @constructor
11441  * @param {Object} config a configuration object.
11442  */
11443 Roo.data.Connection = function(config){
11444     Roo.apply(this, config);
11445     this.addEvents({
11446         /**
11447          * @event beforerequest
11448          * Fires before a network request is made to retrieve a data object.
11449          * @param {Connection} conn This Connection object.
11450          * @param {Object} options The options config object passed to the {@link #request} method.
11451          */
11452         "beforerequest" : true,
11453         /**
11454          * @event requestcomplete
11455          * Fires if the request was successfully completed.
11456          * @param {Connection} conn This Connection object.
11457          * @param {Object} response The XHR object containing the response data.
11458          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11459          * @param {Object} options The options config object passed to the {@link #request} method.
11460          */
11461         "requestcomplete" : true,
11462         /**
11463          * @event requestexception
11464          * Fires if an error HTTP status was returned from the server.
11465          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11466          * @param {Connection} conn This Connection object.
11467          * @param {Object} response The XHR object containing the response data.
11468          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11469          * @param {Object} options The options config object passed to the {@link #request} method.
11470          */
11471         "requestexception" : true
11472     });
11473     Roo.data.Connection.superclass.constructor.call(this);
11474 };
11475
11476 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11477     /**
11478      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11479      */
11480     /**
11481      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11482      * extra parameters to each request made by this object. (defaults to undefined)
11483      */
11484     /**
11485      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11486      *  to each request made by this object. (defaults to undefined)
11487      */
11488     /**
11489      * @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)
11490      */
11491     /**
11492      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11493      */
11494     timeout : 30000,
11495     /**
11496      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11497      * @type Boolean
11498      */
11499     autoAbort:false,
11500
11501     /**
11502      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11503      * @type Boolean
11504      */
11505     disableCaching: true,
11506
11507     /**
11508      * Sends an HTTP request to a remote server.
11509      * @param {Object} options An object which may contain the following properties:<ul>
11510      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11511      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11512      * request, a url encoded string or a function to call to get either.</li>
11513      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11514      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11515      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11516      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11517      * <li>options {Object} The parameter to the request call.</li>
11518      * <li>success {Boolean} True if the request succeeded.</li>
11519      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11520      * </ul></li>
11521      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11522      * The callback is passed the following parameters:<ul>
11523      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11524      * <li>options {Object} The parameter to the request call.</li>
11525      * </ul></li>
11526      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11527      * The callback is passed the following parameters:<ul>
11528      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11529      * <li>options {Object} The parameter to the request call.</li>
11530      * </ul></li>
11531      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11532      * for the callback function. Defaults to the browser window.</li>
11533      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11534      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11535      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11536      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11537      * params for the post data. Any params will be appended to the URL.</li>
11538      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11539      * </ul>
11540      * @return {Number} transactionId
11541      */
11542     request : function(o){
11543         if(this.fireEvent("beforerequest", this, o) !== false){
11544             var p = o.params;
11545
11546             if(typeof p == "function"){
11547                 p = p.call(o.scope||window, o);
11548             }
11549             if(typeof p == "object"){
11550                 p = Roo.urlEncode(o.params);
11551             }
11552             if(this.extraParams){
11553                 var extras = Roo.urlEncode(this.extraParams);
11554                 p = p ? (p + '&' + extras) : extras;
11555             }
11556
11557             var url = o.url || this.url;
11558             if(typeof url == 'function'){
11559                 url = url.call(o.scope||window, o);
11560             }
11561
11562             if(o.form){
11563                 var form = Roo.getDom(o.form);
11564                 url = url || form.action;
11565
11566                 var enctype = form.getAttribute("enctype");
11567                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11568                     return this.doFormUpload(o, p, url);
11569                 }
11570                 var f = Roo.lib.Ajax.serializeForm(form);
11571                 p = p ? (p + '&' + f) : f;
11572             }
11573
11574             var hs = o.headers;
11575             if(this.defaultHeaders){
11576                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11577                 if(!o.headers){
11578                     o.headers = hs;
11579                 }
11580             }
11581
11582             var cb = {
11583                 success: this.handleResponse,
11584                 failure: this.handleFailure,
11585                 scope: this,
11586                 argument: {options: o},
11587                 timeout : o.timeout || this.timeout
11588             };
11589
11590             var method = o.method||this.method||(p ? "POST" : "GET");
11591
11592             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11593                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11594             }
11595
11596             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11597                 if(o.autoAbort){
11598                     this.abort();
11599                 }
11600             }else if(this.autoAbort !== false){
11601                 this.abort();
11602             }
11603
11604             if((method == 'GET' && p) || o.xmlData){
11605                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11606                 p = '';
11607             }
11608             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11609             return this.transId;
11610         }else{
11611             Roo.callback(o.callback, o.scope, [o, null, null]);
11612             return null;
11613         }
11614     },
11615
11616     /**
11617      * Determine whether this object has a request outstanding.
11618      * @param {Number} transactionId (Optional) defaults to the last transaction
11619      * @return {Boolean} True if there is an outstanding request.
11620      */
11621     isLoading : function(transId){
11622         if(transId){
11623             return Roo.lib.Ajax.isCallInProgress(transId);
11624         }else{
11625             return this.transId ? true : false;
11626         }
11627     },
11628
11629     /**
11630      * Aborts any outstanding request.
11631      * @param {Number} transactionId (Optional) defaults to the last transaction
11632      */
11633     abort : function(transId){
11634         if(transId || this.isLoading()){
11635             Roo.lib.Ajax.abort(transId || this.transId);
11636         }
11637     },
11638
11639     // private
11640     handleResponse : function(response){
11641         this.transId = false;
11642         var options = response.argument.options;
11643         response.argument = options ? options.argument : null;
11644         this.fireEvent("requestcomplete", this, response, options);
11645         Roo.callback(options.success, options.scope, [response, options]);
11646         Roo.callback(options.callback, options.scope, [options, true, response]);
11647     },
11648
11649     // private
11650     handleFailure : function(response, e){
11651         this.transId = false;
11652         var options = response.argument.options;
11653         response.argument = options ? options.argument : null;
11654         this.fireEvent("requestexception", this, response, options, e);
11655         Roo.callback(options.failure, options.scope, [response, options]);
11656         Roo.callback(options.callback, options.scope, [options, false, response]);
11657     },
11658
11659     // private
11660     doFormUpload : function(o, ps, url){
11661         var id = Roo.id();
11662         var frame = document.createElement('iframe');
11663         frame.id = id;
11664         frame.name = id;
11665         frame.className = 'x-hidden';
11666         if(Roo.isIE){
11667             frame.src = Roo.SSL_SECURE_URL;
11668         }
11669         document.body.appendChild(frame);
11670
11671         if(Roo.isIE){
11672            document.frames[id].name = id;
11673         }
11674
11675         var form = Roo.getDom(o.form);
11676         form.target = id;
11677         form.method = 'POST';
11678         form.enctype = form.encoding = 'multipart/form-data';
11679         if(url){
11680             form.action = url;
11681         }
11682
11683         var hiddens, hd;
11684         if(ps){ // add dynamic params
11685             hiddens = [];
11686             ps = Roo.urlDecode(ps, false);
11687             for(var k in ps){
11688                 if(ps.hasOwnProperty(k)){
11689                     hd = document.createElement('input');
11690                     hd.type = 'hidden';
11691                     hd.name = k;
11692                     hd.value = ps[k];
11693                     form.appendChild(hd);
11694                     hiddens.push(hd);
11695                 }
11696             }
11697         }
11698
11699         function cb(){
11700             var r = {  // bogus response object
11701                 responseText : '',
11702                 responseXML : null
11703             };
11704
11705             r.argument = o ? o.argument : null;
11706
11707             try { //
11708                 var doc;
11709                 if(Roo.isIE){
11710                     doc = frame.contentWindow.document;
11711                 }else {
11712                     doc = (frame.contentDocument || window.frames[id].document);
11713                 }
11714                 if(doc && doc.body){
11715                     r.responseText = doc.body.innerHTML;
11716                 }
11717                 if(doc && doc.XMLDocument){
11718                     r.responseXML = doc.XMLDocument;
11719                 }else {
11720                     r.responseXML = doc;
11721                 }
11722             }
11723             catch(e) {
11724                 // ignore
11725             }
11726
11727             Roo.EventManager.removeListener(frame, 'load', cb, this);
11728
11729             this.fireEvent("requestcomplete", this, r, o);
11730             Roo.callback(o.success, o.scope, [r, o]);
11731             Roo.callback(o.callback, o.scope, [o, true, r]);
11732
11733             setTimeout(function(){document.body.removeChild(frame);}, 100);
11734         }
11735
11736         Roo.EventManager.on(frame, 'load', cb, this);
11737         form.submit();
11738
11739         if(hiddens){ // remove dynamic params
11740             for(var i = 0, len = hiddens.length; i < len; i++){
11741                 form.removeChild(hiddens[i]);
11742             }
11743         }
11744     }
11745 });
11746 /*
11747  * Based on:
11748  * Ext JS Library 1.1.1
11749  * Copyright(c) 2006-2007, Ext JS, LLC.
11750  *
11751  * Originally Released Under LGPL - original licence link has changed is not relivant.
11752  *
11753  * Fork - LGPL
11754  * <script type="text/javascript">
11755  */
11756  
11757 /**
11758  * Global Ajax request class.
11759  * 
11760  * @class Roo.Ajax
11761  * @extends Roo.data.Connection
11762  * @static
11763  * 
11764  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11765  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11766  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11767  * @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)
11768  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11769  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11770  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11771  */
11772 Roo.Ajax = new Roo.data.Connection({
11773     // fix up the docs
11774     /**
11775      * @scope Roo.Ajax
11776      * @type {Boolear} 
11777      */
11778     autoAbort : false,
11779
11780     /**
11781      * Serialize the passed form into a url encoded string
11782      * @scope Roo.Ajax
11783      * @param {String/HTMLElement} form
11784      * @return {String}
11785      */
11786     serializeForm : function(form){
11787         return Roo.lib.Ajax.serializeForm(form);
11788     }
11789 });/*
11790  * Based on:
11791  * Ext JS Library 1.1.1
11792  * Copyright(c) 2006-2007, Ext JS, LLC.
11793  *
11794  * Originally Released Under LGPL - original licence link has changed is not relivant.
11795  *
11796  * Fork - LGPL
11797  * <script type="text/javascript">
11798  */
11799
11800  
11801 /**
11802  * @class Roo.UpdateManager
11803  * @extends Roo.util.Observable
11804  * Provides AJAX-style update for Element object.<br><br>
11805  * Usage:<br>
11806  * <pre><code>
11807  * // Get it from a Roo.Element object
11808  * var el = Roo.get("foo");
11809  * var mgr = el.getUpdateManager();
11810  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11811  * ...
11812  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11813  * <br>
11814  * // or directly (returns the same UpdateManager instance)
11815  * var mgr = new Roo.UpdateManager("myElementId");
11816  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11817  * mgr.on("update", myFcnNeedsToKnow);
11818  * <br>
11819    // short handed call directly from the element object
11820    Roo.get("foo").load({
11821         url: "bar.php",
11822         scripts:true,
11823         params: "for=bar",
11824         text: "Loading Foo..."
11825    });
11826  * </code></pre>
11827  * @constructor
11828  * Create new UpdateManager directly.
11829  * @param {String/HTMLElement/Roo.Element} el The element to update
11830  * @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).
11831  */
11832 Roo.UpdateManager = function(el, forceNew){
11833     el = Roo.get(el);
11834     if(!forceNew && el.updateManager){
11835         return el.updateManager;
11836     }
11837     /**
11838      * The Element object
11839      * @type Roo.Element
11840      */
11841     this.el = el;
11842     /**
11843      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11844      * @type String
11845      */
11846     this.defaultUrl = null;
11847
11848     this.addEvents({
11849         /**
11850          * @event beforeupdate
11851          * Fired before an update is made, return false from your handler and the update is cancelled.
11852          * @param {Roo.Element} el
11853          * @param {String/Object/Function} url
11854          * @param {String/Object} params
11855          */
11856         "beforeupdate": true,
11857         /**
11858          * @event update
11859          * Fired after successful update is made.
11860          * @param {Roo.Element} el
11861          * @param {Object} oResponseObject The response Object
11862          */
11863         "update": true,
11864         /**
11865          * @event failure
11866          * Fired on update failure.
11867          * @param {Roo.Element} el
11868          * @param {Object} oResponseObject The response Object
11869          */
11870         "failure": true
11871     });
11872     var d = Roo.UpdateManager.defaults;
11873     /**
11874      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11875      * @type String
11876      */
11877     this.sslBlankUrl = d.sslBlankUrl;
11878     /**
11879      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11880      * @type Boolean
11881      */
11882     this.disableCaching = d.disableCaching;
11883     /**
11884      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11885      * @type String
11886      */
11887     this.indicatorText = d.indicatorText;
11888     /**
11889      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11890      * @type String
11891      */
11892     this.showLoadIndicator = d.showLoadIndicator;
11893     /**
11894      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11895      * @type Number
11896      */
11897     this.timeout = d.timeout;
11898
11899     /**
11900      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11901      * @type Boolean
11902      */
11903     this.loadScripts = d.loadScripts;
11904
11905     /**
11906      * Transaction object of current executing transaction
11907      */
11908     this.transaction = null;
11909
11910     /**
11911      * @private
11912      */
11913     this.autoRefreshProcId = null;
11914     /**
11915      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11916      * @type Function
11917      */
11918     this.refreshDelegate = this.refresh.createDelegate(this);
11919     /**
11920      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11921      * @type Function
11922      */
11923     this.updateDelegate = this.update.createDelegate(this);
11924     /**
11925      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11926      * @type Function
11927      */
11928     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11929     /**
11930      * @private
11931      */
11932     this.successDelegate = this.processSuccess.createDelegate(this);
11933     /**
11934      * @private
11935      */
11936     this.failureDelegate = this.processFailure.createDelegate(this);
11937
11938     if(!this.renderer){
11939      /**
11940       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11941       */
11942     this.renderer = new Roo.UpdateManager.BasicRenderer();
11943     }
11944     
11945     Roo.UpdateManager.superclass.constructor.call(this);
11946 };
11947
11948 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11949     /**
11950      * Get the Element this UpdateManager is bound to
11951      * @return {Roo.Element} The element
11952      */
11953     getEl : function(){
11954         return this.el;
11955     },
11956     /**
11957      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11958      * @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:
11959 <pre><code>
11960 um.update({<br/>
11961     url: "your-url.php",<br/>
11962     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11963     callback: yourFunction,<br/>
11964     scope: yourObject, //(optional scope)  <br/>
11965     discardUrl: false, <br/>
11966     nocache: false,<br/>
11967     text: "Loading...",<br/>
11968     timeout: 30,<br/>
11969     scripts: false<br/>
11970 });
11971 </code></pre>
11972      * The only required property is url. The optional properties nocache, text and scripts
11973      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11974      * @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}
11975      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11976      * @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.
11977      */
11978     update : function(url, params, callback, discardUrl){
11979         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11980             var method = this.method,
11981                 cfg;
11982             if(typeof url == "object"){ // must be config object
11983                 cfg = url;
11984                 url = cfg.url;
11985                 params = params || cfg.params;
11986                 callback = callback || cfg.callback;
11987                 discardUrl = discardUrl || cfg.discardUrl;
11988                 if(callback && cfg.scope){
11989                     callback = callback.createDelegate(cfg.scope);
11990                 }
11991                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11992                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11993                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11994                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11995                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11996             }
11997             this.showLoading();
11998             if(!discardUrl){
11999                 this.defaultUrl = url;
12000             }
12001             if(typeof url == "function"){
12002                 url = url.call(this);
12003             }
12004
12005             method = method || (params ? "POST" : "GET");
12006             if(method == "GET"){
12007                 url = this.prepareUrl(url);
12008             }
12009
12010             var o = Roo.apply(cfg ||{}, {
12011                 url : url,
12012                 params: params,
12013                 success: this.successDelegate,
12014                 failure: this.failureDelegate,
12015                 callback: undefined,
12016                 timeout: (this.timeout*1000),
12017                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12018             });
12019             Roo.log("updated manager called with timeout of " + o.timeout);
12020             this.transaction = Roo.Ajax.request(o);
12021         }
12022     },
12023
12024     /**
12025      * 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.
12026      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12027      * @param {String/HTMLElement} form The form Id or form element
12028      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12029      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12030      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12031      */
12032     formUpdate : function(form, url, reset, callback){
12033         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12034             if(typeof url == "function"){
12035                 url = url.call(this);
12036             }
12037             form = Roo.getDom(form);
12038             this.transaction = Roo.Ajax.request({
12039                 form: form,
12040                 url:url,
12041                 success: this.successDelegate,
12042                 failure: this.failureDelegate,
12043                 timeout: (this.timeout*1000),
12044                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12045             });
12046             this.showLoading.defer(1, this);
12047         }
12048     },
12049
12050     /**
12051      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12052      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12053      */
12054     refresh : function(callback){
12055         if(this.defaultUrl == null){
12056             return;
12057         }
12058         this.update(this.defaultUrl, null, callback, true);
12059     },
12060
12061     /**
12062      * Set this element to auto refresh.
12063      * @param {Number} interval How often to update (in seconds).
12064      * @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)
12065      * @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}
12066      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12067      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12068      */
12069     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12070         if(refreshNow){
12071             this.update(url || this.defaultUrl, params, callback, true);
12072         }
12073         if(this.autoRefreshProcId){
12074             clearInterval(this.autoRefreshProcId);
12075         }
12076         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12077     },
12078
12079     /**
12080      * Stop auto refresh on this element.
12081      */
12082      stopAutoRefresh : function(){
12083         if(this.autoRefreshProcId){
12084             clearInterval(this.autoRefreshProcId);
12085             delete this.autoRefreshProcId;
12086         }
12087     },
12088
12089     isAutoRefreshing : function(){
12090        return this.autoRefreshProcId ? true : false;
12091     },
12092     /**
12093      * Called to update the element to "Loading" state. Override to perform custom action.
12094      */
12095     showLoading : function(){
12096         if(this.showLoadIndicator){
12097             this.el.update(this.indicatorText);
12098         }
12099     },
12100
12101     /**
12102      * Adds unique parameter to query string if disableCaching = true
12103      * @private
12104      */
12105     prepareUrl : function(url){
12106         if(this.disableCaching){
12107             var append = "_dc=" + (new Date().getTime());
12108             if(url.indexOf("?") !== -1){
12109                 url += "&" + append;
12110             }else{
12111                 url += "?" + append;
12112             }
12113         }
12114         return url;
12115     },
12116
12117     /**
12118      * @private
12119      */
12120     processSuccess : function(response){
12121         this.transaction = null;
12122         if(response.argument.form && response.argument.reset){
12123             try{ // put in try/catch since some older FF releases had problems with this
12124                 response.argument.form.reset();
12125             }catch(e){}
12126         }
12127         if(this.loadScripts){
12128             this.renderer.render(this.el, response, this,
12129                 this.updateComplete.createDelegate(this, [response]));
12130         }else{
12131             this.renderer.render(this.el, response, this);
12132             this.updateComplete(response);
12133         }
12134     },
12135
12136     updateComplete : function(response){
12137         this.fireEvent("update", this.el, response);
12138         if(typeof response.argument.callback == "function"){
12139             response.argument.callback(this.el, true, response);
12140         }
12141     },
12142
12143     /**
12144      * @private
12145      */
12146     processFailure : function(response){
12147         this.transaction = null;
12148         this.fireEvent("failure", this.el, response);
12149         if(typeof response.argument.callback == "function"){
12150             response.argument.callback(this.el, false, response);
12151         }
12152     },
12153
12154     /**
12155      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12156      * @param {Object} renderer The object implementing the render() method
12157      */
12158     setRenderer : function(renderer){
12159         this.renderer = renderer;
12160     },
12161
12162     getRenderer : function(){
12163        return this.renderer;
12164     },
12165
12166     /**
12167      * Set the defaultUrl used for updates
12168      * @param {String/Function} defaultUrl The url or a function to call to get the url
12169      */
12170     setDefaultUrl : function(defaultUrl){
12171         this.defaultUrl = defaultUrl;
12172     },
12173
12174     /**
12175      * Aborts the executing transaction
12176      */
12177     abort : function(){
12178         if(this.transaction){
12179             Roo.Ajax.abort(this.transaction);
12180         }
12181     },
12182
12183     /**
12184      * Returns true if an update is in progress
12185      * @return {Boolean}
12186      */
12187     isUpdating : function(){
12188         if(this.transaction){
12189             return Roo.Ajax.isLoading(this.transaction);
12190         }
12191         return false;
12192     }
12193 });
12194
12195 /**
12196  * @class Roo.UpdateManager.defaults
12197  * @static (not really - but it helps the doc tool)
12198  * The defaults collection enables customizing the default properties of UpdateManager
12199  */
12200    Roo.UpdateManager.defaults = {
12201        /**
12202          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12203          * @type Number
12204          */
12205          timeout : 30,
12206
12207          /**
12208          * True to process scripts by default (Defaults to false).
12209          * @type Boolean
12210          */
12211         loadScripts : false,
12212
12213         /**
12214         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12215         * @type String
12216         */
12217         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12218         /**
12219          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12220          * @type Boolean
12221          */
12222         disableCaching : false,
12223         /**
12224          * Whether to show indicatorText when loading (Defaults to true).
12225          * @type Boolean
12226          */
12227         showLoadIndicator : true,
12228         /**
12229          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12230          * @type String
12231          */
12232         indicatorText : '<div class="loading-indicator">Loading...</div>'
12233    };
12234
12235 /**
12236  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12237  *Usage:
12238  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12239  * @param {String/HTMLElement/Roo.Element} el The element to update
12240  * @param {String} url The url
12241  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12242  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12243  * @static
12244  * @deprecated
12245  * @member Roo.UpdateManager
12246  */
12247 Roo.UpdateManager.updateElement = function(el, url, params, options){
12248     var um = Roo.get(el, true).getUpdateManager();
12249     Roo.apply(um, options);
12250     um.update(url, params, options ? options.callback : null);
12251 };
12252 // alias for backwards compat
12253 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12254 /**
12255  * @class Roo.UpdateManager.BasicRenderer
12256  * Default Content renderer. Updates the elements innerHTML with the responseText.
12257  */
12258 Roo.UpdateManager.BasicRenderer = function(){};
12259
12260 Roo.UpdateManager.BasicRenderer.prototype = {
12261     /**
12262      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12263      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12264      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12265      * @param {Roo.Element} el The element being rendered
12266      * @param {Object} response The YUI Connect response object
12267      * @param {UpdateManager} updateManager The calling update manager
12268      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12269      */
12270      render : function(el, response, updateManager, callback){
12271         el.update(response.responseText, updateManager.loadScripts, callback);
12272     }
12273 };
12274 /*
12275  * Based on:
12276  * Roo JS
12277  * (c)) Alan Knowles
12278  * Licence : LGPL
12279  */
12280
12281
12282 /**
12283  * @class Roo.DomTemplate
12284  * @extends Roo.Template
12285  * An effort at a dom based template engine..
12286  *
12287  * Similar to XTemplate, except it uses dom parsing to create the template..
12288  *
12289  * Supported features:
12290  *
12291  *  Tags:
12292
12293 <pre><code>
12294       {a_variable} - output encoded.
12295       {a_variable.format:("Y-m-d")} - call a method on the variable
12296       {a_variable:raw} - unencoded output
12297       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12298       {a_variable:this.method_on_template(...)} - call a method on the template object.
12299  
12300 </code></pre>
12301  *  The tpl tag:
12302 <pre><code>
12303         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12304         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12305         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12306         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12307   
12308 </code></pre>
12309  *      
12310  */
12311 Roo.DomTemplate = function()
12312 {
12313      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12314      if (this.html) {
12315         this.compile();
12316      }
12317 };
12318
12319
12320 Roo.extend(Roo.DomTemplate, Roo.Template, {
12321     /**
12322      * id counter for sub templates.
12323      */
12324     id : 0,
12325     /**
12326      * flag to indicate if dom parser is inside a pre,
12327      * it will strip whitespace if not.
12328      */
12329     inPre : false,
12330     
12331     /**
12332      * The various sub templates
12333      */
12334     tpls : false,
12335     
12336     
12337     
12338     /**
12339      *
12340      * basic tag replacing syntax
12341      * WORD:WORD()
12342      *
12343      * // you can fake an object call by doing this
12344      *  x.t:(test,tesT) 
12345      * 
12346      */
12347     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12348     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12349     
12350     iterChild : function (node, method) {
12351         
12352         var oldPre = this.inPre;
12353         if (node.tagName == 'PRE') {
12354             this.inPre = true;
12355         }
12356         for( var i = 0; i < node.childNodes.length; i++) {
12357             method.call(this, node.childNodes[i]);
12358         }
12359         this.inPre = oldPre;
12360     },
12361     
12362     
12363     
12364     /**
12365      * compile the template
12366      *
12367      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12368      *
12369      */
12370     compile: function()
12371     {
12372         var s = this.html;
12373         
12374         // covert the html into DOM...
12375         var doc = false;
12376         var div =false;
12377         try {
12378             doc = document.implementation.createHTMLDocument("");
12379             doc.documentElement.innerHTML =   this.html  ;
12380             div = doc.documentElement;
12381         } catch (e) {
12382             // old IE... - nasty -- it causes all sorts of issues.. with
12383             // images getting pulled from server..
12384             div = document.createElement('div');
12385             div.innerHTML = this.html;
12386         }
12387         //doc.documentElement.innerHTML = htmlBody
12388          
12389         
12390         
12391         this.tpls = [];
12392         var _t = this;
12393         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12394         
12395         var tpls = this.tpls;
12396         
12397         // create a top level template from the snippet..
12398         
12399         //Roo.log(div.innerHTML);
12400         
12401         var tpl = {
12402             uid : 'master',
12403             id : this.id++,
12404             attr : false,
12405             value : false,
12406             body : div.innerHTML,
12407             
12408             forCall : false,
12409             execCall : false,
12410             dom : div,
12411             isTop : true
12412             
12413         };
12414         tpls.unshift(tpl);
12415         
12416         
12417         // compile them...
12418         this.tpls = [];
12419         Roo.each(tpls, function(tp){
12420             this.compileTpl(tp);
12421             this.tpls[tp.id] = tp;
12422         }, this);
12423         
12424         this.master = tpls[0];
12425         return this;
12426         
12427         
12428     },
12429     
12430     compileNode : function(node, istop) {
12431         // test for
12432         //Roo.log(node);
12433         
12434         
12435         // skip anything not a tag..
12436         if (node.nodeType != 1) {
12437             if (node.nodeType == 3 && !this.inPre) {
12438                 // reduce white space..
12439                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12440                 
12441             }
12442             return;
12443         }
12444         
12445         var tpl = {
12446             uid : false,
12447             id : false,
12448             attr : false,
12449             value : false,
12450             body : '',
12451             
12452             forCall : false,
12453             execCall : false,
12454             dom : false,
12455             isTop : istop
12456             
12457             
12458         };
12459         
12460         
12461         switch(true) {
12462             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12463             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12464             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12465             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12466             // no default..
12467         }
12468         
12469         
12470         if (!tpl.attr) {
12471             // just itterate children..
12472             this.iterChild(node,this.compileNode);
12473             return;
12474         }
12475         tpl.uid = this.id++;
12476         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12477         node.removeAttribute('roo-'+ tpl.attr);
12478         if (tpl.attr != 'name') {
12479             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12480             node.parentNode.replaceChild(placeholder,  node);
12481         } else {
12482             
12483             var placeholder =  document.createElement('span');
12484             placeholder.className = 'roo-tpl-' + tpl.value;
12485             node.parentNode.replaceChild(placeholder,  node);
12486         }
12487         
12488         // parent now sees '{domtplXXXX}
12489         this.iterChild(node,this.compileNode);
12490         
12491         // we should now have node body...
12492         var div = document.createElement('div');
12493         div.appendChild(node);
12494         tpl.dom = node;
12495         // this has the unfortunate side effect of converting tagged attributes
12496         // eg. href="{...}" into %7C...%7D
12497         // this has been fixed by searching for those combo's although it's a bit hacky..
12498         
12499         
12500         tpl.body = div.innerHTML;
12501         
12502         
12503          
12504         tpl.id = tpl.uid;
12505         switch(tpl.attr) {
12506             case 'for' :
12507                 switch (tpl.value) {
12508                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12509                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12510                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12511                 }
12512                 break;
12513             
12514             case 'exec':
12515                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12516                 break;
12517             
12518             case 'if':     
12519                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12520                 break;
12521             
12522             case 'name':
12523                 tpl.id  = tpl.value; // replace non characters???
12524                 break;
12525             
12526         }
12527         
12528         
12529         this.tpls.push(tpl);
12530         
12531         
12532         
12533     },
12534     
12535     
12536     
12537     
12538     /**
12539      * Compile a segment of the template into a 'sub-template'
12540      *
12541      * 
12542      * 
12543      *
12544      */
12545     compileTpl : function(tpl)
12546     {
12547         var fm = Roo.util.Format;
12548         var useF = this.disableFormats !== true;
12549         
12550         var sep = Roo.isGecko ? "+\n" : ",\n";
12551         
12552         var undef = function(str) {
12553             Roo.debug && Roo.log("Property not found :"  + str);
12554             return '';
12555         };
12556           
12557         //Roo.log(tpl.body);
12558         
12559         
12560         
12561         var fn = function(m, lbrace, name, format, args)
12562         {
12563             //Roo.log("ARGS");
12564             //Roo.log(arguments);
12565             args = args ? args.replace(/\\'/g,"'") : args;
12566             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12567             if (typeof(format) == 'undefined') {
12568                 format =  'htmlEncode'; 
12569             }
12570             if (format == 'raw' ) {
12571                 format = false;
12572             }
12573             
12574             if(name.substr(0, 6) == 'domtpl'){
12575                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12576             }
12577             
12578             // build an array of options to determine if value is undefined..
12579             
12580             // basically get 'xxxx.yyyy' then do
12581             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12582             //    (function () { Roo.log("Property not found"); return ''; })() :
12583             //    ......
12584             
12585             var udef_ar = [];
12586             var lookfor = '';
12587             Roo.each(name.split('.'), function(st) {
12588                 lookfor += (lookfor.length ? '.': '') + st;
12589                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12590             });
12591             
12592             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12593             
12594             
12595             if(format && useF){
12596                 
12597                 args = args ? ',' + args : "";
12598                  
12599                 if(format.substr(0, 5) != "this."){
12600                     format = "fm." + format + '(';
12601                 }else{
12602                     format = 'this.call("'+ format.substr(5) + '", ';
12603                     args = ", values";
12604                 }
12605                 
12606                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12607             }
12608              
12609             if (args && args.length) {
12610                 // called with xxyx.yuu:(test,test)
12611                 // change to ()
12612                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12613             }
12614             // raw.. - :raw modifier..
12615             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12616             
12617         };
12618         var body;
12619         // branched to use + in gecko and [].join() in others
12620         if(Roo.isGecko){
12621             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12622                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12623                     "';};};";
12624         }else{
12625             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12626             body.push(tpl.body.replace(/(\r\n|\n)/g,
12627                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12628             body.push("'].join('');};};");
12629             body = body.join('');
12630         }
12631         
12632         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12633        
12634         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12635         eval(body);
12636         
12637         return this;
12638     },
12639      
12640     /**
12641      * same as applyTemplate, except it's done to one of the subTemplates
12642      * when using named templates, you can do:
12643      *
12644      * var str = pl.applySubTemplate('your-name', values);
12645      *
12646      * 
12647      * @param {Number} id of the template
12648      * @param {Object} values to apply to template
12649      * @param {Object} parent (normaly the instance of this object)
12650      */
12651     applySubTemplate : function(id, values, parent)
12652     {
12653         
12654         
12655         var t = this.tpls[id];
12656         
12657         
12658         try { 
12659             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12660                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12661                 return '';
12662             }
12663         } catch(e) {
12664             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12665             Roo.log(values);
12666           
12667             return '';
12668         }
12669         try { 
12670             
12671             if(t.execCall && t.execCall.call(this, values, parent)){
12672                 return '';
12673             }
12674         } catch(e) {
12675             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12676             Roo.log(values);
12677             return '';
12678         }
12679         
12680         try {
12681             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12682             parent = t.target ? values : parent;
12683             if(t.forCall && vs instanceof Array){
12684                 var buf = [];
12685                 for(var i = 0, len = vs.length; i < len; i++){
12686                     try {
12687                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12688                     } catch (e) {
12689                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12690                         Roo.log(e.body);
12691                         //Roo.log(t.compiled);
12692                         Roo.log(vs[i]);
12693                     }   
12694                 }
12695                 return buf.join('');
12696             }
12697         } catch (e) {
12698             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12699             Roo.log(values);
12700             return '';
12701         }
12702         try {
12703             return t.compiled.call(this, vs, parent);
12704         } catch (e) {
12705             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12706             Roo.log(e.body);
12707             //Roo.log(t.compiled);
12708             Roo.log(values);
12709             return '';
12710         }
12711     },
12712
12713    
12714
12715     applyTemplate : function(values){
12716         return this.master.compiled.call(this, values, {});
12717         //var s = this.subs;
12718     },
12719
12720     apply : function(){
12721         return this.applyTemplate.apply(this, arguments);
12722     }
12723
12724  });
12725
12726 Roo.DomTemplate.from = function(el){
12727     el = Roo.getDom(el);
12728     return new Roo.Domtemplate(el.value || el.innerHTML);
12729 };/*
12730  * Based on:
12731  * Ext JS Library 1.1.1
12732  * Copyright(c) 2006-2007, Ext JS, LLC.
12733  *
12734  * Originally Released Under LGPL - original licence link has changed is not relivant.
12735  *
12736  * Fork - LGPL
12737  * <script type="text/javascript">
12738  */
12739
12740 /**
12741  * @class Roo.util.DelayedTask
12742  * Provides a convenient method of performing setTimeout where a new
12743  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12744  * You can use this class to buffer
12745  * the keypress events for a certain number of milliseconds, and perform only if they stop
12746  * for that amount of time.
12747  * @constructor The parameters to this constructor serve as defaults and are not required.
12748  * @param {Function} fn (optional) The default function to timeout
12749  * @param {Object} scope (optional) The default scope of that timeout
12750  * @param {Array} args (optional) The default Array of arguments
12751  */
12752 Roo.util.DelayedTask = function(fn, scope, args){
12753     var id = null, d, t;
12754
12755     var call = function(){
12756         var now = new Date().getTime();
12757         if(now - t >= d){
12758             clearInterval(id);
12759             id = null;
12760             fn.apply(scope, args || []);
12761         }
12762     };
12763     /**
12764      * Cancels any pending timeout and queues a new one
12765      * @param {Number} delay The milliseconds to delay
12766      * @param {Function} newFn (optional) Overrides function passed to constructor
12767      * @param {Object} newScope (optional) Overrides scope passed to constructor
12768      * @param {Array} newArgs (optional) Overrides args passed to constructor
12769      */
12770     this.delay = function(delay, newFn, newScope, newArgs){
12771         if(id && delay != d){
12772             this.cancel();
12773         }
12774         d = delay;
12775         t = new Date().getTime();
12776         fn = newFn || fn;
12777         scope = newScope || scope;
12778         args = newArgs || args;
12779         if(!id){
12780             id = setInterval(call, d);
12781         }
12782     };
12783
12784     /**
12785      * Cancel the last queued timeout
12786      */
12787     this.cancel = function(){
12788         if(id){
12789             clearInterval(id);
12790             id = null;
12791         }
12792     };
12793 };/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803  
12804  
12805 Roo.util.TaskRunner = function(interval){
12806     interval = interval || 10;
12807     var tasks = [], removeQueue = [];
12808     var id = 0;
12809     var running = false;
12810
12811     var stopThread = function(){
12812         running = false;
12813         clearInterval(id);
12814         id = 0;
12815     };
12816
12817     var startThread = function(){
12818         if(!running){
12819             running = true;
12820             id = setInterval(runTasks, interval);
12821         }
12822     };
12823
12824     var removeTask = function(task){
12825         removeQueue.push(task);
12826         if(task.onStop){
12827             task.onStop();
12828         }
12829     };
12830
12831     var runTasks = function(){
12832         if(removeQueue.length > 0){
12833             for(var i = 0, len = removeQueue.length; i < len; i++){
12834                 tasks.remove(removeQueue[i]);
12835             }
12836             removeQueue = [];
12837             if(tasks.length < 1){
12838                 stopThread();
12839                 return;
12840             }
12841         }
12842         var now = new Date().getTime();
12843         for(var i = 0, len = tasks.length; i < len; ++i){
12844             var t = tasks[i];
12845             var itime = now - t.taskRunTime;
12846             if(t.interval <= itime){
12847                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12848                 t.taskRunTime = now;
12849                 if(rt === false || t.taskRunCount === t.repeat){
12850                     removeTask(t);
12851                     return;
12852                 }
12853             }
12854             if(t.duration && t.duration <= (now - t.taskStartTime)){
12855                 removeTask(t);
12856             }
12857         }
12858     };
12859
12860     /**
12861      * Queues a new task.
12862      * @param {Object} task
12863      */
12864     this.start = function(task){
12865         tasks.push(task);
12866         task.taskStartTime = new Date().getTime();
12867         task.taskRunTime = 0;
12868         task.taskRunCount = 0;
12869         startThread();
12870         return task;
12871     };
12872
12873     this.stop = function(task){
12874         removeTask(task);
12875         return task;
12876     };
12877
12878     this.stopAll = function(){
12879         stopThread();
12880         for(var i = 0, len = tasks.length; i < len; i++){
12881             if(tasks[i].onStop){
12882                 tasks[i].onStop();
12883             }
12884         }
12885         tasks = [];
12886         removeQueue = [];
12887     };
12888 };
12889
12890 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12891  * Based on:
12892  * Ext JS Library 1.1.1
12893  * Copyright(c) 2006-2007, Ext JS, LLC.
12894  *
12895  * Originally Released Under LGPL - original licence link has changed is not relivant.
12896  *
12897  * Fork - LGPL
12898  * <script type="text/javascript">
12899  */
12900
12901  
12902 /**
12903  * @class Roo.util.MixedCollection
12904  * @extends Roo.util.Observable
12905  * A Collection class that maintains both numeric indexes and keys and exposes events.
12906  * @constructor
12907  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12908  * collection (defaults to false)
12909  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12910  * and return the key value for that item.  This is used when available to look up the key on items that
12911  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12912  * equivalent to providing an implementation for the {@link #getKey} method.
12913  */
12914 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12915     this.items = [];
12916     this.map = {};
12917     this.keys = [];
12918     this.length = 0;
12919     this.addEvents({
12920         /**
12921          * @event clear
12922          * Fires when the collection is cleared.
12923          */
12924         "clear" : true,
12925         /**
12926          * @event add
12927          * Fires when an item is added to the collection.
12928          * @param {Number} index The index at which the item was added.
12929          * @param {Object} o The item added.
12930          * @param {String} key The key associated with the added item.
12931          */
12932         "add" : true,
12933         /**
12934          * @event replace
12935          * Fires when an item is replaced in the collection.
12936          * @param {String} key he key associated with the new added.
12937          * @param {Object} old The item being replaced.
12938          * @param {Object} new The new item.
12939          */
12940         "replace" : true,
12941         /**
12942          * @event remove
12943          * Fires when an item is removed from the collection.
12944          * @param {Object} o The item being removed.
12945          * @param {String} key (optional) The key associated with the removed item.
12946          */
12947         "remove" : true,
12948         "sort" : true
12949     });
12950     this.allowFunctions = allowFunctions === true;
12951     if(keyFn){
12952         this.getKey = keyFn;
12953     }
12954     Roo.util.MixedCollection.superclass.constructor.call(this);
12955 };
12956
12957 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12958     allowFunctions : false,
12959     
12960 /**
12961  * Adds an item to the collection.
12962  * @param {String} key The key to associate with the item
12963  * @param {Object} o The item to add.
12964  * @return {Object} The item added.
12965  */
12966     add : function(key, o){
12967         if(arguments.length == 1){
12968             o = arguments[0];
12969             key = this.getKey(o);
12970         }
12971         if(typeof key == "undefined" || key === null){
12972             this.length++;
12973             this.items.push(o);
12974             this.keys.push(null);
12975         }else{
12976             var old = this.map[key];
12977             if(old){
12978                 return this.replace(key, o);
12979             }
12980             this.length++;
12981             this.items.push(o);
12982             this.map[key] = o;
12983             this.keys.push(key);
12984         }
12985         this.fireEvent("add", this.length-1, o, key);
12986         return o;
12987     },
12988        
12989 /**
12990   * MixedCollection has a generic way to fetch keys if you implement getKey.
12991 <pre><code>
12992 // normal way
12993 var mc = new Roo.util.MixedCollection();
12994 mc.add(someEl.dom.id, someEl);
12995 mc.add(otherEl.dom.id, otherEl);
12996 //and so on
12997
12998 // using getKey
12999 var mc = new Roo.util.MixedCollection();
13000 mc.getKey = function(el){
13001    return el.dom.id;
13002 };
13003 mc.add(someEl);
13004 mc.add(otherEl);
13005
13006 // or via the constructor
13007 var mc = new Roo.util.MixedCollection(false, function(el){
13008    return el.dom.id;
13009 });
13010 mc.add(someEl);
13011 mc.add(otherEl);
13012 </code></pre>
13013  * @param o {Object} The item for which to find the key.
13014  * @return {Object} The key for the passed item.
13015  */
13016     getKey : function(o){
13017          return o.id; 
13018     },
13019    
13020 /**
13021  * Replaces an item in the collection.
13022  * @param {String} key The key associated with the item to replace, or the item to replace.
13023  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13024  * @return {Object}  The new item.
13025  */
13026     replace : function(key, o){
13027         if(arguments.length == 1){
13028             o = arguments[0];
13029             key = this.getKey(o);
13030         }
13031         var old = this.item(key);
13032         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13033              return this.add(key, o);
13034         }
13035         var index = this.indexOfKey(key);
13036         this.items[index] = o;
13037         this.map[key] = o;
13038         this.fireEvent("replace", key, old, o);
13039         return o;
13040     },
13041    
13042 /**
13043  * Adds all elements of an Array or an Object to the collection.
13044  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13045  * an Array of values, each of which are added to the collection.
13046  */
13047     addAll : function(objs){
13048         if(arguments.length > 1 || objs instanceof Array){
13049             var args = arguments.length > 1 ? arguments : objs;
13050             for(var i = 0, len = args.length; i < len; i++){
13051                 this.add(args[i]);
13052             }
13053         }else{
13054             for(var key in objs){
13055                 if(this.allowFunctions || typeof objs[key] != "function"){
13056                     this.add(key, objs[key]);
13057                 }
13058             }
13059         }
13060     },
13061    
13062 /**
13063  * Executes the specified function once for every item in the collection, passing each
13064  * item as the first and only parameter. returning false from the function will stop the iteration.
13065  * @param {Function} fn The function to execute for each item.
13066  * @param {Object} scope (optional) The scope in which to execute the function.
13067  */
13068     each : function(fn, scope){
13069         var items = [].concat(this.items); // each safe for removal
13070         for(var i = 0, len = items.length; i < len; i++){
13071             if(fn.call(scope || items[i], items[i], i, len) === false){
13072                 break;
13073             }
13074         }
13075     },
13076    
13077 /**
13078  * Executes the specified function once for every key in the collection, passing each
13079  * key, and its associated item as the first two parameters.
13080  * @param {Function} fn The function to execute for each item.
13081  * @param {Object} scope (optional) The scope in which to execute the function.
13082  */
13083     eachKey : function(fn, scope){
13084         for(var i = 0, len = this.keys.length; i < len; i++){
13085             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13086         }
13087     },
13088    
13089 /**
13090  * Returns the first item in the collection which elicits a true return value from the
13091  * passed selection function.
13092  * @param {Function} fn The selection function to execute for each item.
13093  * @param {Object} scope (optional) The scope in which to execute the function.
13094  * @return {Object} The first item in the collection which returned true from the selection function.
13095  */
13096     find : function(fn, scope){
13097         for(var i = 0, len = this.items.length; i < len; i++){
13098             if(fn.call(scope || window, this.items[i], this.keys[i])){
13099                 return this.items[i];
13100             }
13101         }
13102         return null;
13103     },
13104    
13105 /**
13106  * Inserts an item at the specified index in the collection.
13107  * @param {Number} index The index to insert the item at.
13108  * @param {String} key The key to associate with the new item, or the item itself.
13109  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13110  * @return {Object} The item inserted.
13111  */
13112     insert : function(index, key, o){
13113         if(arguments.length == 2){
13114             o = arguments[1];
13115             key = this.getKey(o);
13116         }
13117         if(index >= this.length){
13118             return this.add(key, o);
13119         }
13120         this.length++;
13121         this.items.splice(index, 0, o);
13122         if(typeof key != "undefined" && key != null){
13123             this.map[key] = o;
13124         }
13125         this.keys.splice(index, 0, key);
13126         this.fireEvent("add", index, o, key);
13127         return o;
13128     },
13129    
13130 /**
13131  * Removed an item from the collection.
13132  * @param {Object} o The item to remove.
13133  * @return {Object} The item removed.
13134  */
13135     remove : function(o){
13136         return this.removeAt(this.indexOf(o));
13137     },
13138    
13139 /**
13140  * Remove an item from a specified index in the collection.
13141  * @param {Number} index The index within the collection of the item to remove.
13142  */
13143     removeAt : function(index){
13144         if(index < this.length && index >= 0){
13145             this.length--;
13146             var o = this.items[index];
13147             this.items.splice(index, 1);
13148             var key = this.keys[index];
13149             if(typeof key != "undefined"){
13150                 delete this.map[key];
13151             }
13152             this.keys.splice(index, 1);
13153             this.fireEvent("remove", o, key);
13154         }
13155     },
13156    
13157 /**
13158  * Removed an item associated with the passed key fom the collection.
13159  * @param {String} key The key of the item to remove.
13160  */
13161     removeKey : function(key){
13162         return this.removeAt(this.indexOfKey(key));
13163     },
13164    
13165 /**
13166  * Returns the number of items in the collection.
13167  * @return {Number} the number of items in the collection.
13168  */
13169     getCount : function(){
13170         return this.length; 
13171     },
13172    
13173 /**
13174  * Returns index within the collection of the passed Object.
13175  * @param {Object} o The item to find the index of.
13176  * @return {Number} index of the item.
13177  */
13178     indexOf : function(o){
13179         if(!this.items.indexOf){
13180             for(var i = 0, len = this.items.length; i < len; i++){
13181                 if(this.items[i] == o) {
13182                     return i;
13183                 }
13184             }
13185             return -1;
13186         }else{
13187             return this.items.indexOf(o);
13188         }
13189     },
13190    
13191 /**
13192  * Returns index within the collection of the passed key.
13193  * @param {String} key The key to find the index of.
13194  * @return {Number} index of the key.
13195  */
13196     indexOfKey : function(key){
13197         if(!this.keys.indexOf){
13198             for(var i = 0, len = this.keys.length; i < len; i++){
13199                 if(this.keys[i] == key) {
13200                     return i;
13201                 }
13202             }
13203             return -1;
13204         }else{
13205             return this.keys.indexOf(key);
13206         }
13207     },
13208    
13209 /**
13210  * Returns the item associated with the passed key OR index. Key has priority over index.
13211  * @param {String/Number} key The key or index of the item.
13212  * @return {Object} The item associated with the passed key.
13213  */
13214     item : function(key){
13215         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13216         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13217     },
13218     
13219 /**
13220  * Returns the item at the specified index.
13221  * @param {Number} index The index of the item.
13222  * @return {Object}
13223  */
13224     itemAt : function(index){
13225         return this.items[index];
13226     },
13227     
13228 /**
13229  * Returns the item associated with the passed key.
13230  * @param {String/Number} key The key of the item.
13231  * @return {Object} The item associated with the passed key.
13232  */
13233     key : function(key){
13234         return this.map[key];
13235     },
13236    
13237 /**
13238  * Returns true if the collection contains the passed Object as an item.
13239  * @param {Object} o  The Object to look for in the collection.
13240  * @return {Boolean} True if the collection contains the Object as an item.
13241  */
13242     contains : function(o){
13243         return this.indexOf(o) != -1;
13244     },
13245    
13246 /**
13247  * Returns true if the collection contains the passed Object as a key.
13248  * @param {String} key The key to look for in the collection.
13249  * @return {Boolean} True if the collection contains the Object as a key.
13250  */
13251     containsKey : function(key){
13252         return typeof this.map[key] != "undefined";
13253     },
13254    
13255 /**
13256  * Removes all items from the collection.
13257  */
13258     clear : function(){
13259         this.length = 0;
13260         this.items = [];
13261         this.keys = [];
13262         this.map = {};
13263         this.fireEvent("clear");
13264     },
13265    
13266 /**
13267  * Returns the first item in the collection.
13268  * @return {Object} the first item in the collection..
13269  */
13270     first : function(){
13271         return this.items[0]; 
13272     },
13273    
13274 /**
13275  * Returns the last item in the collection.
13276  * @return {Object} the last item in the collection..
13277  */
13278     last : function(){
13279         return this.items[this.length-1];   
13280     },
13281     
13282     _sort : function(property, dir, fn){
13283         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13284         fn = fn || function(a, b){
13285             return a-b;
13286         };
13287         var c = [], k = this.keys, items = this.items;
13288         for(var i = 0, len = items.length; i < len; i++){
13289             c[c.length] = {key: k[i], value: items[i], index: i};
13290         }
13291         c.sort(function(a, b){
13292             var v = fn(a[property], b[property]) * dsc;
13293             if(v == 0){
13294                 v = (a.index < b.index ? -1 : 1);
13295             }
13296             return v;
13297         });
13298         for(var i = 0, len = c.length; i < len; i++){
13299             items[i] = c[i].value;
13300             k[i] = c[i].key;
13301         }
13302         this.fireEvent("sort", this);
13303     },
13304     
13305     /**
13306      * Sorts this collection with the passed comparison function
13307      * @param {String} direction (optional) "ASC" or "DESC"
13308      * @param {Function} fn (optional) comparison function
13309      */
13310     sort : function(dir, fn){
13311         this._sort("value", dir, fn);
13312     },
13313     
13314     /**
13315      * Sorts this collection by keys
13316      * @param {String} direction (optional) "ASC" or "DESC"
13317      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13318      */
13319     keySort : function(dir, fn){
13320         this._sort("key", dir, fn || function(a, b){
13321             return String(a).toUpperCase()-String(b).toUpperCase();
13322         });
13323     },
13324     
13325     /**
13326      * Returns a range of items in this collection
13327      * @param {Number} startIndex (optional) defaults to 0
13328      * @param {Number} endIndex (optional) default to the last item
13329      * @return {Array} An array of items
13330      */
13331     getRange : function(start, end){
13332         var items = this.items;
13333         if(items.length < 1){
13334             return [];
13335         }
13336         start = start || 0;
13337         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13338         var r = [];
13339         if(start <= end){
13340             for(var i = start; i <= end; i++) {
13341                     r[r.length] = items[i];
13342             }
13343         }else{
13344             for(var i = start; i >= end; i--) {
13345                     r[r.length] = items[i];
13346             }
13347         }
13348         return r;
13349     },
13350         
13351     /**
13352      * Filter the <i>objects</i> in this collection by a specific property. 
13353      * Returns a new collection that has been filtered.
13354      * @param {String} property A property on your objects
13355      * @param {String/RegExp} value Either string that the property values 
13356      * should start with or a RegExp to test against the property
13357      * @return {MixedCollection} The new filtered collection
13358      */
13359     filter : function(property, value){
13360         if(!value.exec){ // not a regex
13361             value = String(value);
13362             if(value.length == 0){
13363                 return this.clone();
13364             }
13365             value = new RegExp("^" + Roo.escapeRe(value), "i");
13366         }
13367         return this.filterBy(function(o){
13368             return o && value.test(o[property]);
13369         });
13370         },
13371     
13372     /**
13373      * Filter by a function. * Returns a new collection that has been filtered.
13374      * The passed function will be called with each 
13375      * object in the collection. If the function returns true, the value is included 
13376      * otherwise it is filtered.
13377      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13378      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13379      * @return {MixedCollection} The new filtered collection
13380      */
13381     filterBy : function(fn, scope){
13382         var r = new Roo.util.MixedCollection();
13383         r.getKey = this.getKey;
13384         var k = this.keys, it = this.items;
13385         for(var i = 0, len = it.length; i < len; i++){
13386             if(fn.call(scope||this, it[i], k[i])){
13387                                 r.add(k[i], it[i]);
13388                         }
13389         }
13390         return r;
13391     },
13392     
13393     /**
13394      * Creates a duplicate of this collection
13395      * @return {MixedCollection}
13396      */
13397     clone : function(){
13398         var r = new Roo.util.MixedCollection();
13399         var k = this.keys, it = this.items;
13400         for(var i = 0, len = it.length; i < len; i++){
13401             r.add(k[i], it[i]);
13402         }
13403         r.getKey = this.getKey;
13404         return r;
13405     }
13406 });
13407 /**
13408  * Returns the item associated with the passed key or index.
13409  * @method
13410  * @param {String/Number} key The key or index of the item.
13411  * @return {Object} The item associated with the passed key.
13412  */
13413 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13414  * Based on:
13415  * Ext JS Library 1.1.1
13416  * Copyright(c) 2006-2007, Ext JS, LLC.
13417  *
13418  * Originally Released Under LGPL - original licence link has changed is not relivant.
13419  *
13420  * Fork - LGPL
13421  * <script type="text/javascript">
13422  */
13423 /**
13424  * @class Roo.util.JSON
13425  * Modified version of Douglas Crockford"s json.js that doesn"t
13426  * mess with the Object prototype 
13427  * http://www.json.org/js.html
13428  * @singleton
13429  */
13430 Roo.util.JSON = new (function(){
13431     var useHasOwn = {}.hasOwnProperty ? true : false;
13432     
13433     // crashes Safari in some instances
13434     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13435     
13436     var pad = function(n) {
13437         return n < 10 ? "0" + n : n;
13438     };
13439     
13440     var m = {
13441         "\b": '\\b',
13442         "\t": '\\t',
13443         "\n": '\\n',
13444         "\f": '\\f',
13445         "\r": '\\r',
13446         '"' : '\\"',
13447         "\\": '\\\\'
13448     };
13449
13450     var encodeString = function(s){
13451         if (/["\\\x00-\x1f]/.test(s)) {
13452             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13453                 var c = m[b];
13454                 if(c){
13455                     return c;
13456                 }
13457                 c = b.charCodeAt();
13458                 return "\\u00" +
13459                     Math.floor(c / 16).toString(16) +
13460                     (c % 16).toString(16);
13461             }) + '"';
13462         }
13463         return '"' + s + '"';
13464     };
13465     
13466     var encodeArray = function(o){
13467         var a = ["["], b, i, l = o.length, v;
13468             for (i = 0; i < l; i += 1) {
13469                 v = o[i];
13470                 switch (typeof v) {
13471                     case "undefined":
13472                     case "function":
13473                     case "unknown":
13474                         break;
13475                     default:
13476                         if (b) {
13477                             a.push(',');
13478                         }
13479                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13480                         b = true;
13481                 }
13482             }
13483             a.push("]");
13484             return a.join("");
13485     };
13486     
13487     var encodeDate = function(o){
13488         return '"' + o.getFullYear() + "-" +
13489                 pad(o.getMonth() + 1) + "-" +
13490                 pad(o.getDate()) + "T" +
13491                 pad(o.getHours()) + ":" +
13492                 pad(o.getMinutes()) + ":" +
13493                 pad(o.getSeconds()) + '"';
13494     };
13495     
13496     /**
13497      * Encodes an Object, Array or other value
13498      * @param {Mixed} o The variable to encode
13499      * @return {String} The JSON string
13500      */
13501     this.encode = function(o)
13502     {
13503         // should this be extended to fully wrap stringify..
13504         
13505         if(typeof o == "undefined" || o === null){
13506             return "null";
13507         }else if(o instanceof Array){
13508             return encodeArray(o);
13509         }else if(o instanceof Date){
13510             return encodeDate(o);
13511         }else if(typeof o == "string"){
13512             return encodeString(o);
13513         }else if(typeof o == "number"){
13514             return isFinite(o) ? String(o) : "null";
13515         }else if(typeof o == "boolean"){
13516             return String(o);
13517         }else {
13518             var a = ["{"], b, i, v;
13519             for (i in o) {
13520                 if(!useHasOwn || o.hasOwnProperty(i)) {
13521                     v = o[i];
13522                     switch (typeof v) {
13523                     case "undefined":
13524                     case "function":
13525                     case "unknown":
13526                         break;
13527                     default:
13528                         if(b){
13529                             a.push(',');
13530                         }
13531                         a.push(this.encode(i), ":",
13532                                 v === null ? "null" : this.encode(v));
13533                         b = true;
13534                     }
13535                 }
13536             }
13537             a.push("}");
13538             return a.join("");
13539         }
13540     };
13541     
13542     /**
13543      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13544      * @param {String} json The JSON string
13545      * @return {Object} The resulting object
13546      */
13547     this.decode = function(json){
13548         
13549         return  /** eval:var:json */ eval("(" + json + ')');
13550     };
13551 })();
13552 /** 
13553  * Shorthand for {@link Roo.util.JSON#encode}
13554  * @member Roo encode 
13555  * @method */
13556 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13557 /** 
13558  * Shorthand for {@link Roo.util.JSON#decode}
13559  * @member Roo decode 
13560  * @method */
13561 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13562 /*
13563  * Based on:
13564  * Ext JS Library 1.1.1
13565  * Copyright(c) 2006-2007, Ext JS, LLC.
13566  *
13567  * Originally Released Under LGPL - original licence link has changed is not relivant.
13568  *
13569  * Fork - LGPL
13570  * <script type="text/javascript">
13571  */
13572  
13573 /**
13574  * @class Roo.util.Format
13575  * Reusable data formatting functions
13576  * @singleton
13577  */
13578 Roo.util.Format = function(){
13579     var trimRe = /^\s+|\s+$/g;
13580     return {
13581         /**
13582          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13583          * @param {String} value The string to truncate
13584          * @param {Number} length The maximum length to allow before truncating
13585          * @return {String} The converted text
13586          */
13587         ellipsis : function(value, len){
13588             if(value && value.length > len){
13589                 return value.substr(0, len-3)+"...";
13590             }
13591             return value;
13592         },
13593
13594         /**
13595          * Checks a reference and converts it to empty string if it is undefined
13596          * @param {Mixed} value Reference to check
13597          * @return {Mixed} Empty string if converted, otherwise the original value
13598          */
13599         undef : function(value){
13600             return typeof value != "undefined" ? value : "";
13601         },
13602
13603         /**
13604          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13605          * @param {String} value The string to encode
13606          * @return {String} The encoded text
13607          */
13608         htmlEncode : function(value){
13609             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13610         },
13611
13612         /**
13613          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13614          * @param {String} value The string to decode
13615          * @return {String} The decoded text
13616          */
13617         htmlDecode : function(value){
13618             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13619         },
13620
13621         /**
13622          * Trims any whitespace from either side of a string
13623          * @param {String} value The text to trim
13624          * @return {String} The trimmed text
13625          */
13626         trim : function(value){
13627             return String(value).replace(trimRe, "");
13628         },
13629
13630         /**
13631          * Returns a substring from within an original string
13632          * @param {String} value The original text
13633          * @param {Number} start The start index of the substring
13634          * @param {Number} length The length of the substring
13635          * @return {String} The substring
13636          */
13637         substr : function(value, start, length){
13638             return String(value).substr(start, length);
13639         },
13640
13641         /**
13642          * Converts a string to all lower case letters
13643          * @param {String} value The text to convert
13644          * @return {String} The converted text
13645          */
13646         lowercase : function(value){
13647             return String(value).toLowerCase();
13648         },
13649
13650         /**
13651          * Converts a string to all upper case letters
13652          * @param {String} value The text to convert
13653          * @return {String} The converted text
13654          */
13655         uppercase : function(value){
13656             return String(value).toUpperCase();
13657         },
13658
13659         /**
13660          * Converts the first character only of a string to upper case
13661          * @param {String} value The text to convert
13662          * @return {String} The converted text
13663          */
13664         capitalize : function(value){
13665             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13666         },
13667
13668         // private
13669         call : function(value, fn){
13670             if(arguments.length > 2){
13671                 var args = Array.prototype.slice.call(arguments, 2);
13672                 args.unshift(value);
13673                  
13674                 return /** eval:var:value */  eval(fn).apply(window, args);
13675             }else{
13676                 /** eval:var:value */
13677                 return /** eval:var:value */ eval(fn).call(window, value);
13678             }
13679         },
13680
13681        
13682         /**
13683          * safer version of Math.toFixed..??/
13684          * @param {Number/String} value The numeric value to format
13685          * @param {Number/String} value Decimal places 
13686          * @return {String} The formatted currency string
13687          */
13688         toFixed : function(v, n)
13689         {
13690             // why not use to fixed - precision is buggered???
13691             if (!n) {
13692                 return Math.round(v-0);
13693             }
13694             var fact = Math.pow(10,n+1);
13695             v = (Math.round((v-0)*fact))/fact;
13696             var z = (''+fact).substring(2);
13697             if (v == Math.floor(v)) {
13698                 return Math.floor(v) + '.' + z;
13699             }
13700             
13701             // now just padd decimals..
13702             var ps = String(v).split('.');
13703             var fd = (ps[1] + z);
13704             var r = fd.substring(0,n); 
13705             var rm = fd.substring(n); 
13706             if (rm < 5) {
13707                 return ps[0] + '.' + r;
13708             }
13709             r*=1; // turn it into a number;
13710             r++;
13711             if (String(r).length != n) {
13712                 ps[0]*=1;
13713                 ps[0]++;
13714                 r = String(r).substring(1); // chop the end off.
13715             }
13716             
13717             return ps[0] + '.' + r;
13718              
13719         },
13720         
13721         /**
13722          * Format a number as US currency
13723          * @param {Number/String} value The numeric value to format
13724          * @return {String} The formatted currency string
13725          */
13726         usMoney : function(v){
13727             return '$' + Roo.util.Format.number(v);
13728         },
13729         
13730         /**
13731          * Format a number
13732          * eventually this should probably emulate php's number_format
13733          * @param {Number/String} value The numeric value to format
13734          * @param {Number} decimals number of decimal places
13735          * @return {String} The formatted currency string
13736          */
13737         number : function(v,decimals)
13738         {
13739             // multiply and round.
13740             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13741             var mul = Math.pow(10, decimals);
13742             var zero = String(mul).substring(1);
13743             v = (Math.round((v-0)*mul))/mul;
13744             
13745             // if it's '0' number.. then
13746             
13747             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13748             v = String(v);
13749             var ps = v.split('.');
13750             var whole = ps[0];
13751             
13752             
13753             var r = /(\d+)(\d{3})/;
13754             // add comma's
13755             while (r.test(whole)) {
13756                 whole = whole.replace(r, '$1' + ',' + '$2');
13757             }
13758             
13759             
13760             var sub = ps[1] ?
13761                     // has decimals..
13762                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13763                     // does not have decimals
13764                     (decimals ? ('.' + zero) : '');
13765             
13766             
13767             return whole + sub ;
13768         },
13769         
13770         /**
13771          * Parse a value into a formatted date using the specified format pattern.
13772          * @param {Mixed} value The value to format
13773          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13774          * @return {String} The formatted date string
13775          */
13776         date : function(v, format){
13777             if(!v){
13778                 return "";
13779             }
13780             if(!(v instanceof Date)){
13781                 v = new Date(Date.parse(v));
13782             }
13783             return v.dateFormat(format || Roo.util.Format.defaults.date);
13784         },
13785
13786         /**
13787          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13788          * @param {String} format Any valid date format string
13789          * @return {Function} The date formatting function
13790          */
13791         dateRenderer : function(format){
13792             return function(v){
13793                 return Roo.util.Format.date(v, format);  
13794             };
13795         },
13796
13797         // private
13798         stripTagsRE : /<\/?[^>]+>/gi,
13799         
13800         /**
13801          * Strips all HTML tags
13802          * @param {Mixed} value The text from which to strip tags
13803          * @return {String} The stripped text
13804          */
13805         stripTags : function(v){
13806             return !v ? v : String(v).replace(this.stripTagsRE, "");
13807         }
13808     };
13809 }();
13810 Roo.util.Format.defaults = {
13811     date : 'd/M/Y'
13812 };/*
13813  * Based on:
13814  * Ext JS Library 1.1.1
13815  * Copyright(c) 2006-2007, Ext JS, LLC.
13816  *
13817  * Originally Released Under LGPL - original licence link has changed is not relivant.
13818  *
13819  * Fork - LGPL
13820  * <script type="text/javascript">
13821  */
13822
13823
13824  
13825
13826 /**
13827  * @class Roo.MasterTemplate
13828  * @extends Roo.Template
13829  * Provides a template that can have child templates. The syntax is:
13830 <pre><code>
13831 var t = new Roo.MasterTemplate(
13832         '&lt;select name="{name}"&gt;',
13833                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13834         '&lt;/select&gt;'
13835 );
13836 t.add('options', {value: 'foo', text: 'bar'});
13837 // or you can add multiple child elements in one shot
13838 t.addAll('options', [
13839     {value: 'foo', text: 'bar'},
13840     {value: 'foo2', text: 'bar2'},
13841     {value: 'foo3', text: 'bar3'}
13842 ]);
13843 // then append, applying the master template values
13844 t.append('my-form', {name: 'my-select'});
13845 </code></pre>
13846 * A name attribute for the child template is not required if you have only one child
13847 * template or you want to refer to them by index.
13848  */
13849 Roo.MasterTemplate = function(){
13850     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13851     this.originalHtml = this.html;
13852     var st = {};
13853     var m, re = this.subTemplateRe;
13854     re.lastIndex = 0;
13855     var subIndex = 0;
13856     while(m = re.exec(this.html)){
13857         var name = m[1], content = m[2];
13858         st[subIndex] = {
13859             name: name,
13860             index: subIndex,
13861             buffer: [],
13862             tpl : new Roo.Template(content)
13863         };
13864         if(name){
13865             st[name] = st[subIndex];
13866         }
13867         st[subIndex].tpl.compile();
13868         st[subIndex].tpl.call = this.call.createDelegate(this);
13869         subIndex++;
13870     }
13871     this.subCount = subIndex;
13872     this.subs = st;
13873 };
13874 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13875     /**
13876     * The regular expression used to match sub templates
13877     * @type RegExp
13878     * @property
13879     */
13880     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13881
13882     /**
13883      * Applies the passed values to a child template.
13884      * @param {String/Number} name (optional) The name or index of the child template
13885      * @param {Array/Object} values The values to be applied to the template
13886      * @return {MasterTemplate} this
13887      */
13888      add : function(name, values){
13889         if(arguments.length == 1){
13890             values = arguments[0];
13891             name = 0;
13892         }
13893         var s = this.subs[name];
13894         s.buffer[s.buffer.length] = s.tpl.apply(values);
13895         return this;
13896     },
13897
13898     /**
13899      * Applies all the passed values to a child template.
13900      * @param {String/Number} name (optional) The name or index of the child template
13901      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13902      * @param {Boolean} reset (optional) True to reset the template first
13903      * @return {MasterTemplate} this
13904      */
13905     fill : function(name, values, reset){
13906         var a = arguments;
13907         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13908             values = a[0];
13909             name = 0;
13910             reset = a[1];
13911         }
13912         if(reset){
13913             this.reset();
13914         }
13915         for(var i = 0, len = values.length; i < len; i++){
13916             this.add(name, values[i]);
13917         }
13918         return this;
13919     },
13920
13921     /**
13922      * Resets the template for reuse
13923      * @return {MasterTemplate} this
13924      */
13925      reset : function(){
13926         var s = this.subs;
13927         for(var i = 0; i < this.subCount; i++){
13928             s[i].buffer = [];
13929         }
13930         return this;
13931     },
13932
13933     applyTemplate : function(values){
13934         var s = this.subs;
13935         var replaceIndex = -1;
13936         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13937             return s[++replaceIndex].buffer.join("");
13938         });
13939         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13940     },
13941
13942     apply : function(){
13943         return this.applyTemplate.apply(this, arguments);
13944     },
13945
13946     compile : function(){return this;}
13947 });
13948
13949 /**
13950  * Alias for fill().
13951  * @method
13952  */
13953 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13954  /**
13955  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13956  * var tpl = Roo.MasterTemplate.from('element-id');
13957  * @param {String/HTMLElement} el
13958  * @param {Object} config
13959  * @static
13960  */
13961 Roo.MasterTemplate.from = function(el, config){
13962     el = Roo.getDom(el);
13963     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13964 };/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975  
13976 /**
13977  * @class Roo.util.CSS
13978  * Utility class for manipulating CSS rules
13979  * @singleton
13980  */
13981 Roo.util.CSS = function(){
13982         var rules = null;
13983         var doc = document;
13984
13985     var camelRe = /(-[a-z])/gi;
13986     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13987
13988    return {
13989    /**
13990     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13991     * tag and appended to the HEAD of the document.
13992     * @param {String|Object} cssText The text containing the css rules
13993     * @param {String} id An id to add to the stylesheet for later removal
13994     * @return {StyleSheet}
13995     */
13996     createStyleSheet : function(cssText, id){
13997         var ss;
13998         var head = doc.getElementsByTagName("head")[0];
13999         var nrules = doc.createElement("style");
14000         nrules.setAttribute("type", "text/css");
14001         if(id){
14002             nrules.setAttribute("id", id);
14003         }
14004         if (typeof(cssText) != 'string') {
14005             // support object maps..
14006             // not sure if this a good idea.. 
14007             // perhaps it should be merged with the general css handling
14008             // and handle js style props.
14009             var cssTextNew = [];
14010             for(var n in cssText) {
14011                 var citems = [];
14012                 for(var k in cssText[n]) {
14013                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14014                 }
14015                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14016                 
14017             }
14018             cssText = cssTextNew.join("\n");
14019             
14020         }
14021        
14022        
14023        if(Roo.isIE){
14024            head.appendChild(nrules);
14025            ss = nrules.styleSheet;
14026            ss.cssText = cssText;
14027        }else{
14028            try{
14029                 nrules.appendChild(doc.createTextNode(cssText));
14030            }catch(e){
14031                nrules.cssText = cssText; 
14032            }
14033            head.appendChild(nrules);
14034            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14035        }
14036        this.cacheStyleSheet(ss);
14037        return ss;
14038    },
14039
14040    /**
14041     * Removes a style or link tag by id
14042     * @param {String} id The id of the tag
14043     */
14044    removeStyleSheet : function(id){
14045        var existing = doc.getElementById(id);
14046        if(existing){
14047            existing.parentNode.removeChild(existing);
14048        }
14049    },
14050
14051    /**
14052     * Dynamically swaps an existing stylesheet reference for a new one
14053     * @param {String} id The id of an existing link tag to remove
14054     * @param {String} url The href of the new stylesheet to include
14055     */
14056    swapStyleSheet : function(id, url){
14057        this.removeStyleSheet(id);
14058        var ss = doc.createElement("link");
14059        ss.setAttribute("rel", "stylesheet");
14060        ss.setAttribute("type", "text/css");
14061        ss.setAttribute("id", id);
14062        ss.setAttribute("href", url);
14063        doc.getElementsByTagName("head")[0].appendChild(ss);
14064    },
14065    
14066    /**
14067     * Refresh the rule cache if you have dynamically added stylesheets
14068     * @return {Object} An object (hash) of rules indexed by selector
14069     */
14070    refreshCache : function(){
14071        return this.getRules(true);
14072    },
14073
14074    // private
14075    cacheStyleSheet : function(stylesheet){
14076        if(!rules){
14077            rules = {};
14078        }
14079        try{// try catch for cross domain access issue
14080            var ssRules = stylesheet.cssRules || stylesheet.rules;
14081            for(var j = ssRules.length-1; j >= 0; --j){
14082                rules[ssRules[j].selectorText] = ssRules[j];
14083            }
14084        }catch(e){}
14085    },
14086    
14087    /**
14088     * Gets all css rules for the document
14089     * @param {Boolean} refreshCache true to refresh the internal cache
14090     * @return {Object} An object (hash) of rules indexed by selector
14091     */
14092    getRules : function(refreshCache){
14093                 if(rules == null || refreshCache){
14094                         rules = {};
14095                         var ds = doc.styleSheets;
14096                         for(var i =0, len = ds.length; i < len; i++){
14097                             try{
14098                         this.cacheStyleSheet(ds[i]);
14099                     }catch(e){} 
14100                 }
14101                 }
14102                 return rules;
14103         },
14104         
14105         /**
14106     * Gets an an individual CSS rule by selector(s)
14107     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14108     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14109     * @return {CSSRule} The CSS rule or null if one is not found
14110     */
14111    getRule : function(selector, refreshCache){
14112                 var rs = this.getRules(refreshCache);
14113                 if(!(selector instanceof Array)){
14114                     return rs[selector];
14115                 }
14116                 for(var i = 0; i < selector.length; i++){
14117                         if(rs[selector[i]]){
14118                                 return rs[selector[i]];
14119                         }
14120                 }
14121                 return null;
14122         },
14123         
14124         
14125         /**
14126     * Updates a rule property
14127     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14128     * @param {String} property The css property
14129     * @param {String} value The new value for the property
14130     * @return {Boolean} true If a rule was found and updated
14131     */
14132    updateRule : function(selector, property, value){
14133                 if(!(selector instanceof Array)){
14134                         var rule = this.getRule(selector);
14135                         if(rule){
14136                                 rule.style[property.replace(camelRe, camelFn)] = value;
14137                                 return true;
14138                         }
14139                 }else{
14140                         for(var i = 0; i < selector.length; i++){
14141                                 if(this.updateRule(selector[i], property, value)){
14142                                         return true;
14143                                 }
14144                         }
14145                 }
14146                 return false;
14147         }
14148    };   
14149 }();/*
14150  * Based on:
14151  * Ext JS Library 1.1.1
14152  * Copyright(c) 2006-2007, Ext JS, LLC.
14153  *
14154  * Originally Released Under LGPL - original licence link has changed is not relivant.
14155  *
14156  * Fork - LGPL
14157  * <script type="text/javascript">
14158  */
14159
14160  
14161
14162 /**
14163  * @class Roo.util.ClickRepeater
14164  * @extends Roo.util.Observable
14165  * 
14166  * A wrapper class which can be applied to any element. Fires a "click" event while the
14167  * mouse is pressed. The interval between firings may be specified in the config but
14168  * defaults to 10 milliseconds.
14169  * 
14170  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14171  * 
14172  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14173  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14174  * Similar to an autorepeat key delay.
14175  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14176  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14177  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14178  *           "interval" and "delay" are ignored. "immediate" is honored.
14179  * @cfg {Boolean} preventDefault True to prevent the default click event
14180  * @cfg {Boolean} stopDefault True to stop the default click event
14181  * 
14182  * @history
14183  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14184  *     2007-02-02 jvs Renamed to ClickRepeater
14185  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14186  *
14187  *  @constructor
14188  * @param {String/HTMLElement/Element} el The element to listen on
14189  * @param {Object} config
14190  **/
14191 Roo.util.ClickRepeater = function(el, config)
14192 {
14193     this.el = Roo.get(el);
14194     this.el.unselectable();
14195
14196     Roo.apply(this, config);
14197
14198     this.addEvents({
14199     /**
14200      * @event mousedown
14201      * Fires when the mouse button is depressed.
14202      * @param {Roo.util.ClickRepeater} this
14203      */
14204         "mousedown" : true,
14205     /**
14206      * @event click
14207      * Fires on a specified interval during the time the element is pressed.
14208      * @param {Roo.util.ClickRepeater} this
14209      */
14210         "click" : true,
14211     /**
14212      * @event mouseup
14213      * Fires when the mouse key is released.
14214      * @param {Roo.util.ClickRepeater} this
14215      */
14216         "mouseup" : true
14217     });
14218
14219     this.el.on("mousedown", this.handleMouseDown, this);
14220     if(this.preventDefault || this.stopDefault){
14221         this.el.on("click", function(e){
14222             if(this.preventDefault){
14223                 e.preventDefault();
14224             }
14225             if(this.stopDefault){
14226                 e.stopEvent();
14227             }
14228         }, this);
14229     }
14230
14231     // allow inline handler
14232     if(this.handler){
14233         this.on("click", this.handler,  this.scope || this);
14234     }
14235
14236     Roo.util.ClickRepeater.superclass.constructor.call(this);
14237 };
14238
14239 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14240     interval : 20,
14241     delay: 250,
14242     preventDefault : true,
14243     stopDefault : false,
14244     timer : 0,
14245
14246     // private
14247     handleMouseDown : function(){
14248         clearTimeout(this.timer);
14249         this.el.blur();
14250         if(this.pressClass){
14251             this.el.addClass(this.pressClass);
14252         }
14253         this.mousedownTime = new Date();
14254
14255         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14256         this.el.on("mouseout", this.handleMouseOut, this);
14257
14258         this.fireEvent("mousedown", this);
14259         this.fireEvent("click", this);
14260         
14261         this.timer = this.click.defer(this.delay || this.interval, this);
14262     },
14263
14264     // private
14265     click : function(){
14266         this.fireEvent("click", this);
14267         this.timer = this.click.defer(this.getInterval(), this);
14268     },
14269
14270     // private
14271     getInterval: function(){
14272         if(!this.accelerate){
14273             return this.interval;
14274         }
14275         var pressTime = this.mousedownTime.getElapsed();
14276         if(pressTime < 500){
14277             return 400;
14278         }else if(pressTime < 1700){
14279             return 320;
14280         }else if(pressTime < 2600){
14281             return 250;
14282         }else if(pressTime < 3500){
14283             return 180;
14284         }else if(pressTime < 4400){
14285             return 140;
14286         }else if(pressTime < 5300){
14287             return 80;
14288         }else if(pressTime < 6200){
14289             return 50;
14290         }else{
14291             return 10;
14292         }
14293     },
14294
14295     // private
14296     handleMouseOut : function(){
14297         clearTimeout(this.timer);
14298         if(this.pressClass){
14299             this.el.removeClass(this.pressClass);
14300         }
14301         this.el.on("mouseover", this.handleMouseReturn, this);
14302     },
14303
14304     // private
14305     handleMouseReturn : function(){
14306         this.el.un("mouseover", this.handleMouseReturn);
14307         if(this.pressClass){
14308             this.el.addClass(this.pressClass);
14309         }
14310         this.click();
14311     },
14312
14313     // private
14314     handleMouseUp : function(){
14315         clearTimeout(this.timer);
14316         this.el.un("mouseover", this.handleMouseReturn);
14317         this.el.un("mouseout", this.handleMouseOut);
14318         Roo.get(document).un("mouseup", this.handleMouseUp);
14319         this.el.removeClass(this.pressClass);
14320         this.fireEvent("mouseup", this);
14321     }
14322 });/*
14323  * Based on:
14324  * Ext JS Library 1.1.1
14325  * Copyright(c) 2006-2007, Ext JS, LLC.
14326  *
14327  * Originally Released Under LGPL - original licence link has changed is not relivant.
14328  *
14329  * Fork - LGPL
14330  * <script type="text/javascript">
14331  */
14332
14333  
14334 /**
14335  * @class Roo.KeyNav
14336  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14337  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14338  * way to implement custom navigation schemes for any UI component.</p>
14339  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14340  * pageUp, pageDown, del, home, end.  Usage:</p>
14341  <pre><code>
14342 var nav = new Roo.KeyNav("my-element", {
14343     "left" : function(e){
14344         this.moveLeft(e.ctrlKey);
14345     },
14346     "right" : function(e){
14347         this.moveRight(e.ctrlKey);
14348     },
14349     "enter" : function(e){
14350         this.save();
14351     },
14352     scope : this
14353 });
14354 </code></pre>
14355  * @constructor
14356  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14357  * @param {Object} config The config
14358  */
14359 Roo.KeyNav = function(el, config){
14360     this.el = Roo.get(el);
14361     Roo.apply(this, config);
14362     if(!this.disabled){
14363         this.disabled = true;
14364         this.enable();
14365     }
14366 };
14367
14368 Roo.KeyNav.prototype = {
14369     /**
14370      * @cfg {Boolean} disabled
14371      * True to disable this KeyNav instance (defaults to false)
14372      */
14373     disabled : false,
14374     /**
14375      * @cfg {String} defaultEventAction
14376      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14377      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14378      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14379      */
14380     defaultEventAction: "stopEvent",
14381     /**
14382      * @cfg {Boolean} forceKeyDown
14383      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14384      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14385      * handle keydown instead of keypress.
14386      */
14387     forceKeyDown : false,
14388
14389     // private
14390     prepareEvent : function(e){
14391         var k = e.getKey();
14392         var h = this.keyToHandler[k];
14393         //if(h && this[h]){
14394         //    e.stopPropagation();
14395         //}
14396         if(Roo.isSafari && h && k >= 37 && k <= 40){
14397             e.stopEvent();
14398         }
14399     },
14400
14401     // private
14402     relay : function(e){
14403         var k = e.getKey();
14404         var h = this.keyToHandler[k];
14405         if(h && this[h]){
14406             if(this.doRelay(e, this[h], h) !== true){
14407                 e[this.defaultEventAction]();
14408             }
14409         }
14410     },
14411
14412     // private
14413     doRelay : function(e, h, hname){
14414         return h.call(this.scope || this, e);
14415     },
14416
14417     // possible handlers
14418     enter : false,
14419     left : false,
14420     right : false,
14421     up : false,
14422     down : false,
14423     tab : false,
14424     esc : false,
14425     pageUp : false,
14426     pageDown : false,
14427     del : false,
14428     home : false,
14429     end : false,
14430
14431     // quick lookup hash
14432     keyToHandler : {
14433         37 : "left",
14434         39 : "right",
14435         38 : "up",
14436         40 : "down",
14437         33 : "pageUp",
14438         34 : "pageDown",
14439         46 : "del",
14440         36 : "home",
14441         35 : "end",
14442         13 : "enter",
14443         27 : "esc",
14444         9  : "tab"
14445     },
14446
14447         /**
14448          * Enable this KeyNav
14449          */
14450         enable: function(){
14451                 if(this.disabled){
14452             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14453             // the EventObject will normalize Safari automatically
14454             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14455                 this.el.on("keydown", this.relay,  this);
14456             }else{
14457                 this.el.on("keydown", this.prepareEvent,  this);
14458                 this.el.on("keypress", this.relay,  this);
14459             }
14460                     this.disabled = false;
14461                 }
14462         },
14463
14464         /**
14465          * Disable this KeyNav
14466          */
14467         disable: function(){
14468                 if(!this.disabled){
14469                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14470                 this.el.un("keydown", this.relay);
14471             }else{
14472                 this.el.un("keydown", this.prepareEvent);
14473                 this.el.un("keypress", this.relay);
14474             }
14475                     this.disabled = true;
14476                 }
14477         }
14478 };/*
14479  * Based on:
14480  * Ext JS Library 1.1.1
14481  * Copyright(c) 2006-2007, Ext JS, LLC.
14482  *
14483  * Originally Released Under LGPL - original licence link has changed is not relivant.
14484  *
14485  * Fork - LGPL
14486  * <script type="text/javascript">
14487  */
14488
14489  
14490 /**
14491  * @class Roo.KeyMap
14492  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14493  * The constructor accepts the same config object as defined by {@link #addBinding}.
14494  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14495  * combination it will call the function with this signature (if the match is a multi-key
14496  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14497  * A KeyMap can also handle a string representation of keys.<br />
14498  * Usage:
14499  <pre><code>
14500 // map one key by key code
14501 var map = new Roo.KeyMap("my-element", {
14502     key: 13, // or Roo.EventObject.ENTER
14503     fn: myHandler,
14504     scope: myObject
14505 });
14506
14507 // map multiple keys to one action by string
14508 var map = new Roo.KeyMap("my-element", {
14509     key: "a\r\n\t",
14510     fn: myHandler,
14511     scope: myObject
14512 });
14513
14514 // map multiple keys to multiple actions by strings and array of codes
14515 var map = new Roo.KeyMap("my-element", [
14516     {
14517         key: [10,13],
14518         fn: function(){ alert("Return was pressed"); }
14519     }, {
14520         key: "abc",
14521         fn: function(){ alert('a, b or c was pressed'); }
14522     }, {
14523         key: "\t",
14524         ctrl:true,
14525         shift:true,
14526         fn: function(){ alert('Control + shift + tab was pressed.'); }
14527     }
14528 ]);
14529 </code></pre>
14530  * <b>Note: A KeyMap starts enabled</b>
14531  * @constructor
14532  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14533  * @param {Object} config The config (see {@link #addBinding})
14534  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14535  */
14536 Roo.KeyMap = function(el, config, eventName){
14537     this.el  = Roo.get(el);
14538     this.eventName = eventName || "keydown";
14539     this.bindings = [];
14540     if(config){
14541         this.addBinding(config);
14542     }
14543     this.enable();
14544 };
14545
14546 Roo.KeyMap.prototype = {
14547     /**
14548      * True to stop the event from bubbling and prevent the default browser action if the
14549      * key was handled by the KeyMap (defaults to false)
14550      * @type Boolean
14551      */
14552     stopEvent : false,
14553
14554     /**
14555      * Add a new binding to this KeyMap. The following config object properties are supported:
14556      * <pre>
14557 Property    Type             Description
14558 ----------  ---------------  ----------------------------------------------------------------------
14559 key         String/Array     A single keycode or an array of keycodes to handle
14560 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14561 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14562 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14563 fn          Function         The function to call when KeyMap finds the expected key combination
14564 scope       Object           The scope of the callback function
14565 </pre>
14566      *
14567      * Usage:
14568      * <pre><code>
14569 // Create a KeyMap
14570 var map = new Roo.KeyMap(document, {
14571     key: Roo.EventObject.ENTER,
14572     fn: handleKey,
14573     scope: this
14574 });
14575
14576 //Add a new binding to the existing KeyMap later
14577 map.addBinding({
14578     key: 'abc',
14579     shift: true,
14580     fn: handleKey,
14581     scope: this
14582 });
14583 </code></pre>
14584      * @param {Object/Array} config A single KeyMap config or an array of configs
14585      */
14586         addBinding : function(config){
14587         if(config instanceof Array){
14588             for(var i = 0, len = config.length; i < len; i++){
14589                 this.addBinding(config[i]);
14590             }
14591             return;
14592         }
14593         var keyCode = config.key,
14594             shift = config.shift, 
14595             ctrl = config.ctrl, 
14596             alt = config.alt,
14597             fn = config.fn,
14598             scope = config.scope;
14599         if(typeof keyCode == "string"){
14600             var ks = [];
14601             var keyString = keyCode.toUpperCase();
14602             for(var j = 0, len = keyString.length; j < len; j++){
14603                 ks.push(keyString.charCodeAt(j));
14604             }
14605             keyCode = ks;
14606         }
14607         var keyArray = keyCode instanceof Array;
14608         var handler = function(e){
14609             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14610                 var k = e.getKey();
14611                 if(keyArray){
14612                     for(var i = 0, len = keyCode.length; i < len; i++){
14613                         if(keyCode[i] == k){
14614                           if(this.stopEvent){
14615                               e.stopEvent();
14616                           }
14617                           fn.call(scope || window, k, e);
14618                           return;
14619                         }
14620                     }
14621                 }else{
14622                     if(k == keyCode){
14623                         if(this.stopEvent){
14624                            e.stopEvent();
14625                         }
14626                         fn.call(scope || window, k, e);
14627                     }
14628                 }
14629             }
14630         };
14631         this.bindings.push(handler);  
14632         },
14633
14634     /**
14635      * Shorthand for adding a single key listener
14636      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14637      * following options:
14638      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14639      * @param {Function} fn The function to call
14640      * @param {Object} scope (optional) The scope of the function
14641      */
14642     on : function(key, fn, scope){
14643         var keyCode, shift, ctrl, alt;
14644         if(typeof key == "object" && !(key instanceof Array)){
14645             keyCode = key.key;
14646             shift = key.shift;
14647             ctrl = key.ctrl;
14648             alt = key.alt;
14649         }else{
14650             keyCode = key;
14651         }
14652         this.addBinding({
14653             key: keyCode,
14654             shift: shift,
14655             ctrl: ctrl,
14656             alt: alt,
14657             fn: fn,
14658             scope: scope
14659         })
14660     },
14661
14662     // private
14663     handleKeyDown : function(e){
14664             if(this.enabled){ //just in case
14665             var b = this.bindings;
14666             for(var i = 0, len = b.length; i < len; i++){
14667                 b[i].call(this, e);
14668             }
14669             }
14670         },
14671         
14672         /**
14673          * Returns true if this KeyMap is enabled
14674          * @return {Boolean} 
14675          */
14676         isEnabled : function(){
14677             return this.enabled;  
14678         },
14679         
14680         /**
14681          * Enables this KeyMap
14682          */
14683         enable: function(){
14684                 if(!this.enabled){
14685                     this.el.on(this.eventName, this.handleKeyDown, this);
14686                     this.enabled = true;
14687                 }
14688         },
14689
14690         /**
14691          * Disable this KeyMap
14692          */
14693         disable: function(){
14694                 if(this.enabled){
14695                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14696                     this.enabled = false;
14697                 }
14698         }
14699 };/*
14700  * Based on:
14701  * Ext JS Library 1.1.1
14702  * Copyright(c) 2006-2007, Ext JS, LLC.
14703  *
14704  * Originally Released Under LGPL - original licence link has changed is not relivant.
14705  *
14706  * Fork - LGPL
14707  * <script type="text/javascript">
14708  */
14709
14710  
14711 /**
14712  * @class Roo.util.TextMetrics
14713  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14714  * wide, in pixels, a given block of text will be.
14715  * @singleton
14716  */
14717 Roo.util.TextMetrics = function(){
14718     var shared;
14719     return {
14720         /**
14721          * Measures the size of the specified text
14722          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14723          * that can affect the size of the rendered text
14724          * @param {String} text The text to measure
14725          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14726          * in order to accurately measure the text height
14727          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14728          */
14729         measure : function(el, text, fixedWidth){
14730             if(!shared){
14731                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14732             }
14733             shared.bind(el);
14734             shared.setFixedWidth(fixedWidth || 'auto');
14735             return shared.getSize(text);
14736         },
14737
14738         /**
14739          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14740          * the overhead of multiple calls to initialize the style properties on each measurement.
14741          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
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 {Roo.util.TextMetrics.Instance} instance The new instance
14745          */
14746         createInstance : function(el, fixedWidth){
14747             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14748         }
14749     };
14750 }();
14751
14752  
14753
14754 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14755     var ml = new Roo.Element(document.createElement('div'));
14756     document.body.appendChild(ml.dom);
14757     ml.position('absolute');
14758     ml.setLeftTop(-1000, -1000);
14759     ml.hide();
14760
14761     if(fixedWidth){
14762         ml.setWidth(fixedWidth);
14763     }
14764      
14765     var instance = {
14766         /**
14767          * Returns the size of the specified text based on the internal element's style and width properties
14768          * @memberOf Roo.util.TextMetrics.Instance#
14769          * @param {String} text The text to measure
14770          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14771          */
14772         getSize : function(text){
14773             ml.update(text);
14774             var s = ml.getSize();
14775             ml.update('');
14776             return s;
14777         },
14778
14779         /**
14780          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14781          * that can affect the size of the rendered text
14782          * @memberOf Roo.util.TextMetrics.Instance#
14783          * @param {String/HTMLElement} el The element, dom node or id
14784          */
14785         bind : function(el){
14786             ml.setStyle(
14787                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14788             );
14789         },
14790
14791         /**
14792          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14793          * to set a fixed width in order to accurately measure the text height.
14794          * @memberOf Roo.util.TextMetrics.Instance#
14795          * @param {Number} width The width to set on the element
14796          */
14797         setFixedWidth : function(width){
14798             ml.setWidth(width);
14799         },
14800
14801         /**
14802          * Returns the measured width of the specified text
14803          * @memberOf Roo.util.TextMetrics.Instance#
14804          * @param {String} text The text to measure
14805          * @return {Number} width The width in pixels
14806          */
14807         getWidth : function(text){
14808             ml.dom.style.width = 'auto';
14809             return this.getSize(text).width;
14810         },
14811
14812         /**
14813          * Returns the measured height of the specified text.  For multiline text, be sure to call
14814          * {@link #setFixedWidth} if necessary.
14815          * @memberOf Roo.util.TextMetrics.Instance#
14816          * @param {String} text The text to measure
14817          * @return {Number} height The height in pixels
14818          */
14819         getHeight : function(text){
14820             return this.getSize(text).height;
14821         }
14822     };
14823
14824     instance.bind(bindTo);
14825
14826     return instance;
14827 };
14828
14829 // backwards compat
14830 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14831  * Based on:
14832  * Ext JS Library 1.1.1
14833  * Copyright(c) 2006-2007, Ext JS, LLC.
14834  *
14835  * Originally Released Under LGPL - original licence link has changed is not relivant.
14836  *
14837  * Fork - LGPL
14838  * <script type="text/javascript">
14839  */
14840
14841 /**
14842  * @class Roo.state.Provider
14843  * Abstract base class for state provider implementations. This class provides methods
14844  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14845  * Provider interface.
14846  */
14847 Roo.state.Provider = function(){
14848     /**
14849      * @event statechange
14850      * Fires when a state change occurs.
14851      * @param {Provider} this This state provider
14852      * @param {String} key The state key which was changed
14853      * @param {String} value The encoded value for the state
14854      */
14855     this.addEvents({
14856         "statechange": true
14857     });
14858     this.state = {};
14859     Roo.state.Provider.superclass.constructor.call(this);
14860 };
14861 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14862     /**
14863      * Returns the current value for a key
14864      * @param {String} name The key name
14865      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14866      * @return {Mixed} The state data
14867      */
14868     get : function(name, defaultValue){
14869         return typeof this.state[name] == "undefined" ?
14870             defaultValue : this.state[name];
14871     },
14872     
14873     /**
14874      * Clears a value from the state
14875      * @param {String} name The key name
14876      */
14877     clear : function(name){
14878         delete this.state[name];
14879         this.fireEvent("statechange", this, name, null);
14880     },
14881     
14882     /**
14883      * Sets the value for a key
14884      * @param {String} name The key name
14885      * @param {Mixed} value The value to set
14886      */
14887     set : function(name, value){
14888         this.state[name] = value;
14889         this.fireEvent("statechange", this, name, value);
14890     },
14891     
14892     /**
14893      * Decodes a string previously encoded with {@link #encodeValue}.
14894      * @param {String} value The value to decode
14895      * @return {Mixed} The decoded value
14896      */
14897     decodeValue : function(cookie){
14898         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14899         var matches = re.exec(unescape(cookie));
14900         if(!matches || !matches[1]) {
14901             return; // non state cookie
14902         }
14903         var type = matches[1];
14904         var v = matches[2];
14905         switch(type){
14906             case "n":
14907                 return parseFloat(v);
14908             case "d":
14909                 return new Date(Date.parse(v));
14910             case "b":
14911                 return (v == "1");
14912             case "a":
14913                 var all = [];
14914                 var values = v.split("^");
14915                 for(var i = 0, len = values.length; i < len; i++){
14916                     all.push(this.decodeValue(values[i]));
14917                 }
14918                 return all;
14919            case "o":
14920                 var all = {};
14921                 var values = v.split("^");
14922                 for(var i = 0, len = values.length; i < len; i++){
14923                     var kv = values[i].split("=");
14924                     all[kv[0]] = this.decodeValue(kv[1]);
14925                 }
14926                 return all;
14927            default:
14928                 return v;
14929         }
14930     },
14931     
14932     /**
14933      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14934      * @param {Mixed} value The value to encode
14935      * @return {String} The encoded value
14936      */
14937     encodeValue : function(v){
14938         var enc;
14939         if(typeof v == "number"){
14940             enc = "n:" + v;
14941         }else if(typeof v == "boolean"){
14942             enc = "b:" + (v ? "1" : "0");
14943         }else if(v instanceof Date){
14944             enc = "d:" + v.toGMTString();
14945         }else if(v instanceof Array){
14946             var flat = "";
14947             for(var i = 0, len = v.length; i < len; i++){
14948                 flat += this.encodeValue(v[i]);
14949                 if(i != len-1) {
14950                     flat += "^";
14951                 }
14952             }
14953             enc = "a:" + flat;
14954         }else if(typeof v == "object"){
14955             var flat = "";
14956             for(var key in v){
14957                 if(typeof v[key] != "function"){
14958                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14959                 }
14960             }
14961             enc = "o:" + flat.substring(0, flat.length-1);
14962         }else{
14963             enc = "s:" + v;
14964         }
14965         return escape(enc);        
14966     }
14967 });
14968
14969 /*
14970  * Based on:
14971  * Ext JS Library 1.1.1
14972  * Copyright(c) 2006-2007, Ext JS, LLC.
14973  *
14974  * Originally Released Under LGPL - original licence link has changed is not relivant.
14975  *
14976  * Fork - LGPL
14977  * <script type="text/javascript">
14978  */
14979 /**
14980  * @class Roo.state.Manager
14981  * This is the global state manager. By default all components that are "state aware" check this class
14982  * for state information if you don't pass them a custom state provider. In order for this class
14983  * to be useful, it must be initialized with a provider when your application initializes.
14984  <pre><code>
14985 // in your initialization function
14986 init : function(){
14987    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14988    ...
14989    // supposed you have a {@link Roo.BorderLayout}
14990    var layout = new Roo.BorderLayout(...);
14991    layout.restoreState();
14992    // or a {Roo.BasicDialog}
14993    var dialog = new Roo.BasicDialog(...);
14994    dialog.restoreState();
14995  </code></pre>
14996  * @singleton
14997  */
14998 Roo.state.Manager = function(){
14999     var provider = new Roo.state.Provider();
15000     
15001     return {
15002         /**
15003          * Configures the default state provider for your application
15004          * @param {Provider} stateProvider The state provider to set
15005          */
15006         setProvider : function(stateProvider){
15007             provider = stateProvider;
15008         },
15009         
15010         /**
15011          * Returns the current value for a key
15012          * @param {String} name The key name
15013          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15014          * @return {Mixed} The state data
15015          */
15016         get : function(key, defaultValue){
15017             return provider.get(key, defaultValue);
15018         },
15019         
15020         /**
15021          * Sets the value for a key
15022          * @param {String} name The key name
15023          * @param {Mixed} value The state data
15024          */
15025          set : function(key, value){
15026             provider.set(key, value);
15027         },
15028         
15029         /**
15030          * Clears a value from the state
15031          * @param {String} name The key name
15032          */
15033         clear : function(key){
15034             provider.clear(key);
15035         },
15036         
15037         /**
15038          * Gets the currently configured state provider
15039          * @return {Provider} The state provider
15040          */
15041         getProvider : function(){
15042             return provider;
15043         }
15044     };
15045 }();
15046 /*
15047  * Based on:
15048  * Ext JS Library 1.1.1
15049  * Copyright(c) 2006-2007, Ext JS, LLC.
15050  *
15051  * Originally Released Under LGPL - original licence link has changed is not relivant.
15052  *
15053  * Fork - LGPL
15054  * <script type="text/javascript">
15055  */
15056 /**
15057  * @class Roo.state.CookieProvider
15058  * @extends Roo.state.Provider
15059  * The default Provider implementation which saves state via cookies.
15060  * <br />Usage:
15061  <pre><code>
15062    var cp = new Roo.state.CookieProvider({
15063        path: "/cgi-bin/",
15064        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15065        domain: "roojs.com"
15066    })
15067    Roo.state.Manager.setProvider(cp);
15068  </code></pre>
15069  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15070  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15071  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15072  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15073  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15074  * domain the page is running on including the 'www' like 'www.roojs.com')
15075  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15076  * @constructor
15077  * Create a new CookieProvider
15078  * @param {Object} config The configuration object
15079  */
15080 Roo.state.CookieProvider = function(config){
15081     Roo.state.CookieProvider.superclass.constructor.call(this);
15082     this.path = "/";
15083     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15084     this.domain = null;
15085     this.secure = false;
15086     Roo.apply(this, config);
15087     this.state = this.readCookies();
15088 };
15089
15090 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15091     // private
15092     set : function(name, value){
15093         if(typeof value == "undefined" || value === null){
15094             this.clear(name);
15095             return;
15096         }
15097         this.setCookie(name, value);
15098         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15099     },
15100
15101     // private
15102     clear : function(name){
15103         this.clearCookie(name);
15104         Roo.state.CookieProvider.superclass.clear.call(this, name);
15105     },
15106
15107     // private
15108     readCookies : function(){
15109         var cookies = {};
15110         var c = document.cookie + ";";
15111         var re = /\s?(.*?)=(.*?);/g;
15112         var matches;
15113         while((matches = re.exec(c)) != null){
15114             var name = matches[1];
15115             var value = matches[2];
15116             if(name && name.substring(0,3) == "ys-"){
15117                 cookies[name.substr(3)] = this.decodeValue(value);
15118             }
15119         }
15120         return cookies;
15121     },
15122
15123     // private
15124     setCookie : function(name, value){
15125         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15126            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15127            ((this.path == null) ? "" : ("; path=" + this.path)) +
15128            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15129            ((this.secure == true) ? "; secure" : "");
15130     },
15131
15132     // private
15133     clearCookie : function(name){
15134         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15135            ((this.path == null) ? "" : ("; path=" + this.path)) +
15136            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15137            ((this.secure == true) ? "; secure" : "");
15138     }
15139 });/*
15140  * Based on:
15141  * Ext JS Library 1.1.1
15142  * Copyright(c) 2006-2007, Ext JS, LLC.
15143  *
15144  * Originally Released Under LGPL - original licence link has changed is not relivant.
15145  *
15146  * Fork - LGPL
15147  * <script type="text/javascript">
15148  */
15149  
15150
15151 /**
15152  * @class Roo.ComponentMgr
15153  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15154  * @singleton
15155  */
15156 Roo.ComponentMgr = function(){
15157     var all = new Roo.util.MixedCollection();
15158
15159     return {
15160         /**
15161          * Registers a component.
15162          * @param {Roo.Component} c The component
15163          */
15164         register : function(c){
15165             all.add(c);
15166         },
15167
15168         /**
15169          * Unregisters a component.
15170          * @param {Roo.Component} c The component
15171          */
15172         unregister : function(c){
15173             all.remove(c);
15174         },
15175
15176         /**
15177          * Returns a component by id
15178          * @param {String} id The component id
15179          */
15180         get : function(id){
15181             return all.get(id);
15182         },
15183
15184         /**
15185          * Registers a function that will be called when a specified component is added to ComponentMgr
15186          * @param {String} id The component id
15187          * @param {Funtction} fn The callback function
15188          * @param {Object} scope The scope of the callback
15189          */
15190         onAvailable : function(id, fn, scope){
15191             all.on("add", function(index, o){
15192                 if(o.id == id){
15193                     fn.call(scope || o, o);
15194                     all.un("add", fn, scope);
15195                 }
15196             });
15197         }
15198     };
15199 }();/*
15200  * Based on:
15201  * Ext JS Library 1.1.1
15202  * Copyright(c) 2006-2007, Ext JS, LLC.
15203  *
15204  * Originally Released Under LGPL - original licence link has changed is not relivant.
15205  *
15206  * Fork - LGPL
15207  * <script type="text/javascript">
15208  */
15209  
15210 /**
15211  * @class Roo.Component
15212  * @extends Roo.util.Observable
15213  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15214  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15215  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15216  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15217  * All visual components (widgets) that require rendering into a layout should subclass Component.
15218  * @constructor
15219  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15220  * 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
15221  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15222  */
15223 Roo.Component = function(config){
15224     config = config || {};
15225     if(config.tagName || config.dom || typeof config == "string"){ // element object
15226         config = {el: config, id: config.id || config};
15227     }
15228     this.initialConfig = config;
15229
15230     Roo.apply(this, config);
15231     this.addEvents({
15232         /**
15233          * @event disable
15234          * Fires after the component is disabled.
15235              * @param {Roo.Component} this
15236              */
15237         disable : true,
15238         /**
15239          * @event enable
15240          * Fires after the component is enabled.
15241              * @param {Roo.Component} this
15242              */
15243         enable : true,
15244         /**
15245          * @event beforeshow
15246          * Fires before the component is shown.  Return false to stop the show.
15247              * @param {Roo.Component} this
15248              */
15249         beforeshow : true,
15250         /**
15251          * @event show
15252          * Fires after the component is shown.
15253              * @param {Roo.Component} this
15254              */
15255         show : true,
15256         /**
15257          * @event beforehide
15258          * Fires before the component is hidden. Return false to stop the hide.
15259              * @param {Roo.Component} this
15260              */
15261         beforehide : true,
15262         /**
15263          * @event hide
15264          * Fires after the component is hidden.
15265              * @param {Roo.Component} this
15266              */
15267         hide : true,
15268         /**
15269          * @event beforerender
15270          * Fires before the component is rendered. Return false to stop the render.
15271              * @param {Roo.Component} this
15272              */
15273         beforerender : true,
15274         /**
15275          * @event render
15276          * Fires after the component is rendered.
15277              * @param {Roo.Component} this
15278              */
15279         render : true,
15280         /**
15281          * @event beforedestroy
15282          * Fires before the component is destroyed. Return false to stop the destroy.
15283              * @param {Roo.Component} this
15284              */
15285         beforedestroy : true,
15286         /**
15287          * @event destroy
15288          * Fires after the component is destroyed.
15289              * @param {Roo.Component} this
15290              */
15291         destroy : true
15292     });
15293     if(!this.id){
15294         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15295     }
15296     Roo.ComponentMgr.register(this);
15297     Roo.Component.superclass.constructor.call(this);
15298     this.initComponent();
15299     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15300         this.render(this.renderTo);
15301         delete this.renderTo;
15302     }
15303 };
15304
15305 /** @private */
15306 Roo.Component.AUTO_ID = 1000;
15307
15308 Roo.extend(Roo.Component, Roo.util.Observable, {
15309     /**
15310      * @scope Roo.Component.prototype
15311      * @type {Boolean}
15312      * true if this component is hidden. Read-only.
15313      */
15314     hidden : false,
15315     /**
15316      * @type {Boolean}
15317      * true if this component is disabled. Read-only.
15318      */
15319     disabled : false,
15320     /**
15321      * @type {Boolean}
15322      * true if this component has been rendered. Read-only.
15323      */
15324     rendered : false,
15325     
15326     /** @cfg {String} disableClass
15327      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15328      */
15329     disabledClass : "x-item-disabled",
15330         /** @cfg {Boolean} allowDomMove
15331          * Whether the component can move the Dom node when rendering (defaults to true).
15332          */
15333     allowDomMove : true,
15334     /** @cfg {String} hideMode (display|visibility)
15335      * How this component should hidden. Supported values are
15336      * "visibility" (css visibility), "offsets" (negative offset position) and
15337      * "display" (css display) - defaults to "display".
15338      */
15339     hideMode: 'display',
15340
15341     /** @private */
15342     ctype : "Roo.Component",
15343
15344     /**
15345      * @cfg {String} actionMode 
15346      * which property holds the element that used for  hide() / show() / disable() / enable()
15347      * default is 'el' 
15348      */
15349     actionMode : "el",
15350
15351     /** @private */
15352     getActionEl : function(){
15353         return this[this.actionMode];
15354     },
15355
15356     initComponent : Roo.emptyFn,
15357     /**
15358      * If this is a lazy rendering component, render it to its container element.
15359      * @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.
15360      */
15361     render : function(container, position){
15362         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15363             if(!container && this.el){
15364                 this.el = Roo.get(this.el);
15365                 container = this.el.dom.parentNode;
15366                 this.allowDomMove = false;
15367             }
15368             this.container = Roo.get(container);
15369             this.rendered = true;
15370             if(position !== undefined){
15371                 if(typeof position == 'number'){
15372                     position = this.container.dom.childNodes[position];
15373                 }else{
15374                     position = Roo.getDom(position);
15375                 }
15376             }
15377             this.onRender(this.container, position || null);
15378             if(this.cls){
15379                 this.el.addClass(this.cls);
15380                 delete this.cls;
15381             }
15382             if(this.style){
15383                 this.el.applyStyles(this.style);
15384                 delete this.style;
15385             }
15386             this.fireEvent("render", this);
15387             this.afterRender(this.container);
15388             if(this.hidden){
15389                 this.hide();
15390             }
15391             if(this.disabled){
15392                 this.disable();
15393             }
15394         }
15395         return this;
15396     },
15397
15398     /** @private */
15399     // default function is not really useful
15400     onRender : function(ct, position){
15401         if(this.el){
15402             this.el = Roo.get(this.el);
15403             if(this.allowDomMove !== false){
15404                 ct.dom.insertBefore(this.el.dom, position);
15405             }
15406         }
15407     },
15408
15409     /** @private */
15410     getAutoCreate : function(){
15411         var cfg = typeof this.autoCreate == "object" ?
15412                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15413         if(this.id && !cfg.id){
15414             cfg.id = this.id;
15415         }
15416         return cfg;
15417     },
15418
15419     /** @private */
15420     afterRender : Roo.emptyFn,
15421
15422     /**
15423      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15424      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15425      */
15426     destroy : function(){
15427         if(this.fireEvent("beforedestroy", this) !== false){
15428             this.purgeListeners();
15429             this.beforeDestroy();
15430             if(this.rendered){
15431                 this.el.removeAllListeners();
15432                 this.el.remove();
15433                 if(this.actionMode == "container"){
15434                     this.container.remove();
15435                 }
15436             }
15437             this.onDestroy();
15438             Roo.ComponentMgr.unregister(this);
15439             this.fireEvent("destroy", this);
15440         }
15441     },
15442
15443         /** @private */
15444     beforeDestroy : function(){
15445
15446     },
15447
15448         /** @private */
15449         onDestroy : function(){
15450
15451     },
15452
15453     /**
15454      * Returns the underlying {@link Roo.Element}.
15455      * @return {Roo.Element} The element
15456      */
15457     getEl : function(){
15458         return this.el;
15459     },
15460
15461     /**
15462      * Returns the id of this component.
15463      * @return {String}
15464      */
15465     getId : function(){
15466         return this.id;
15467     },
15468
15469     /**
15470      * Try to focus this component.
15471      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15472      * @return {Roo.Component} this
15473      */
15474     focus : function(selectText){
15475         if(this.rendered){
15476             this.el.focus();
15477             if(selectText === true){
15478                 this.el.dom.select();
15479             }
15480         }
15481         return this;
15482     },
15483
15484     /** @private */
15485     blur : function(){
15486         if(this.rendered){
15487             this.el.blur();
15488         }
15489         return this;
15490     },
15491
15492     /**
15493      * Disable this component.
15494      * @return {Roo.Component} this
15495      */
15496     disable : function(){
15497         if(this.rendered){
15498             this.onDisable();
15499         }
15500         this.disabled = true;
15501         this.fireEvent("disable", this);
15502         return this;
15503     },
15504
15505         // private
15506     onDisable : function(){
15507         this.getActionEl().addClass(this.disabledClass);
15508         this.el.dom.disabled = true;
15509     },
15510
15511     /**
15512      * Enable this component.
15513      * @return {Roo.Component} this
15514      */
15515     enable : function(){
15516         if(this.rendered){
15517             this.onEnable();
15518         }
15519         this.disabled = false;
15520         this.fireEvent("enable", this);
15521         return this;
15522     },
15523
15524         // private
15525     onEnable : function(){
15526         this.getActionEl().removeClass(this.disabledClass);
15527         this.el.dom.disabled = false;
15528     },
15529
15530     /**
15531      * Convenience function for setting disabled/enabled by boolean.
15532      * @param {Boolean} disabled
15533      */
15534     setDisabled : function(disabled){
15535         this[disabled ? "disable" : "enable"]();
15536     },
15537
15538     /**
15539      * Show this component.
15540      * @return {Roo.Component} this
15541      */
15542     show: function(){
15543         if(this.fireEvent("beforeshow", this) !== false){
15544             this.hidden = false;
15545             if(this.rendered){
15546                 this.onShow();
15547             }
15548             this.fireEvent("show", this);
15549         }
15550         return this;
15551     },
15552
15553     // private
15554     onShow : function(){
15555         var ae = this.getActionEl();
15556         if(this.hideMode == 'visibility'){
15557             ae.dom.style.visibility = "visible";
15558         }else if(this.hideMode == 'offsets'){
15559             ae.removeClass('x-hidden');
15560         }else{
15561             ae.dom.style.display = "";
15562         }
15563     },
15564
15565     /**
15566      * Hide this component.
15567      * @return {Roo.Component} this
15568      */
15569     hide: function(){
15570         if(this.fireEvent("beforehide", this) !== false){
15571             this.hidden = true;
15572             if(this.rendered){
15573                 this.onHide();
15574             }
15575             this.fireEvent("hide", this);
15576         }
15577         return this;
15578     },
15579
15580     // private
15581     onHide : function(){
15582         var ae = this.getActionEl();
15583         if(this.hideMode == 'visibility'){
15584             ae.dom.style.visibility = "hidden";
15585         }else if(this.hideMode == 'offsets'){
15586             ae.addClass('x-hidden');
15587         }else{
15588             ae.dom.style.display = "none";
15589         }
15590     },
15591
15592     /**
15593      * Convenience function to hide or show this component by boolean.
15594      * @param {Boolean} visible True to show, false to hide
15595      * @return {Roo.Component} this
15596      */
15597     setVisible: function(visible){
15598         if(visible) {
15599             this.show();
15600         }else{
15601             this.hide();
15602         }
15603         return this;
15604     },
15605
15606     /**
15607      * Returns true if this component is visible.
15608      */
15609     isVisible : function(){
15610         return this.getActionEl().isVisible();
15611     },
15612
15613     cloneConfig : function(overrides){
15614         overrides = overrides || {};
15615         var id = overrides.id || Roo.id();
15616         var cfg = Roo.applyIf(overrides, this.initialConfig);
15617         cfg.id = id; // prevent dup id
15618         return new this.constructor(cfg);
15619     }
15620 });/*
15621  * Based on:
15622  * Ext JS Library 1.1.1
15623  * Copyright(c) 2006-2007, Ext JS, LLC.
15624  *
15625  * Originally Released Under LGPL - original licence link has changed is not relivant.
15626  *
15627  * Fork - LGPL
15628  * <script type="text/javascript">
15629  */
15630
15631 /**
15632  * @class Roo.BoxComponent
15633  * @extends Roo.Component
15634  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15635  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15636  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15637  * layout containers.
15638  * @constructor
15639  * @param {Roo.Element/String/Object} config The configuration options.
15640  */
15641 Roo.BoxComponent = function(config){
15642     Roo.Component.call(this, config);
15643     this.addEvents({
15644         /**
15645          * @event resize
15646          * Fires after the component is resized.
15647              * @param {Roo.Component} this
15648              * @param {Number} adjWidth The box-adjusted width that was set
15649              * @param {Number} adjHeight The box-adjusted height that was set
15650              * @param {Number} rawWidth The width that was originally specified
15651              * @param {Number} rawHeight The height that was originally specified
15652              */
15653         resize : true,
15654         /**
15655          * @event move
15656          * Fires after the component is moved.
15657              * @param {Roo.Component} this
15658              * @param {Number} x The new x position
15659              * @param {Number} y The new y position
15660              */
15661         move : true
15662     });
15663 };
15664
15665 Roo.extend(Roo.BoxComponent, Roo.Component, {
15666     // private, set in afterRender to signify that the component has been rendered
15667     boxReady : false,
15668     // private, used to defer height settings to subclasses
15669     deferHeight: false,
15670     /** @cfg {Number} width
15671      * width (optional) size of component
15672      */
15673      /** @cfg {Number} height
15674      * height (optional) size of component
15675      */
15676      
15677     /**
15678      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15679      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15680      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15681      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15682      * @return {Roo.BoxComponent} this
15683      */
15684     setSize : function(w, h){
15685         // support for standard size objects
15686         if(typeof w == 'object'){
15687             h = w.height;
15688             w = w.width;
15689         }
15690         // not rendered
15691         if(!this.boxReady){
15692             this.width = w;
15693             this.height = h;
15694             return this;
15695         }
15696
15697         // prevent recalcs when not needed
15698         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15699             return this;
15700         }
15701         this.lastSize = {width: w, height: h};
15702
15703         var adj = this.adjustSize(w, h);
15704         var aw = adj.width, ah = adj.height;
15705         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15706             var rz = this.getResizeEl();
15707             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15708                 rz.setSize(aw, ah);
15709             }else if(!this.deferHeight && ah !== undefined){
15710                 rz.setHeight(ah);
15711             }else if(aw !== undefined){
15712                 rz.setWidth(aw);
15713             }
15714             this.onResize(aw, ah, w, h);
15715             this.fireEvent('resize', this, aw, ah, w, h);
15716         }
15717         return this;
15718     },
15719
15720     /**
15721      * Gets the current size of the component's underlying element.
15722      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15723      */
15724     getSize : function(){
15725         return this.el.getSize();
15726     },
15727
15728     /**
15729      * Gets the current XY position of the component's underlying element.
15730      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15731      * @return {Array} The XY position of the element (e.g., [100, 200])
15732      */
15733     getPosition : function(local){
15734         if(local === true){
15735             return [this.el.getLeft(true), this.el.getTop(true)];
15736         }
15737         return this.xy || this.el.getXY();
15738     },
15739
15740     /**
15741      * Gets the current box measurements of the component's underlying element.
15742      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15743      * @returns {Object} box An object in the format {x, y, width, height}
15744      */
15745     getBox : function(local){
15746         var s = this.el.getSize();
15747         if(local){
15748             s.x = this.el.getLeft(true);
15749             s.y = this.el.getTop(true);
15750         }else{
15751             var xy = this.xy || this.el.getXY();
15752             s.x = xy[0];
15753             s.y = xy[1];
15754         }
15755         return s;
15756     },
15757
15758     /**
15759      * Sets the current box measurements of the component's underlying element.
15760      * @param {Object} box An object in the format {x, y, width, height}
15761      * @returns {Roo.BoxComponent} this
15762      */
15763     updateBox : function(box){
15764         this.setSize(box.width, box.height);
15765         this.setPagePosition(box.x, box.y);
15766         return this;
15767     },
15768
15769     // protected
15770     getResizeEl : function(){
15771         return this.resizeEl || this.el;
15772     },
15773
15774     // protected
15775     getPositionEl : function(){
15776         return this.positionEl || this.el;
15777     },
15778
15779     /**
15780      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15781      * This method fires the move event.
15782      * @param {Number} left The new left
15783      * @param {Number} top The new top
15784      * @returns {Roo.BoxComponent} this
15785      */
15786     setPosition : function(x, y){
15787         this.x = x;
15788         this.y = y;
15789         if(!this.boxReady){
15790             return this;
15791         }
15792         var adj = this.adjustPosition(x, y);
15793         var ax = adj.x, ay = adj.y;
15794
15795         var el = this.getPositionEl();
15796         if(ax !== undefined || ay !== undefined){
15797             if(ax !== undefined && ay !== undefined){
15798                 el.setLeftTop(ax, ay);
15799             }else if(ax !== undefined){
15800                 el.setLeft(ax);
15801             }else if(ay !== undefined){
15802                 el.setTop(ay);
15803             }
15804             this.onPosition(ax, ay);
15805             this.fireEvent('move', this, ax, ay);
15806         }
15807         return this;
15808     },
15809
15810     /**
15811      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15812      * This method fires the move event.
15813      * @param {Number} x The new x position
15814      * @param {Number} y The new y position
15815      * @returns {Roo.BoxComponent} this
15816      */
15817     setPagePosition : function(x, y){
15818         this.pageX = x;
15819         this.pageY = y;
15820         if(!this.boxReady){
15821             return;
15822         }
15823         if(x === undefined || y === undefined){ // cannot translate undefined points
15824             return;
15825         }
15826         var p = this.el.translatePoints(x, y);
15827         this.setPosition(p.left, p.top);
15828         return this;
15829     },
15830
15831     // private
15832     onRender : function(ct, position){
15833         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15834         if(this.resizeEl){
15835             this.resizeEl = Roo.get(this.resizeEl);
15836         }
15837         if(this.positionEl){
15838             this.positionEl = Roo.get(this.positionEl);
15839         }
15840     },
15841
15842     // private
15843     afterRender : function(){
15844         Roo.BoxComponent.superclass.afterRender.call(this);
15845         this.boxReady = true;
15846         this.setSize(this.width, this.height);
15847         if(this.x || this.y){
15848             this.setPosition(this.x, this.y);
15849         }
15850         if(this.pageX || this.pageY){
15851             this.setPagePosition(this.pageX, this.pageY);
15852         }
15853     },
15854
15855     /**
15856      * Force the component's size to recalculate based on the underlying element's current height and width.
15857      * @returns {Roo.BoxComponent} this
15858      */
15859     syncSize : function(){
15860         delete this.lastSize;
15861         this.setSize(this.el.getWidth(), this.el.getHeight());
15862         return this;
15863     },
15864
15865     /**
15866      * Called after the component is resized, this method is empty by default but can be implemented by any
15867      * subclass that needs to perform custom logic after a resize occurs.
15868      * @param {Number} adjWidth The box-adjusted width that was set
15869      * @param {Number} adjHeight The box-adjusted height that was set
15870      * @param {Number} rawWidth The width that was originally specified
15871      * @param {Number} rawHeight The height that was originally specified
15872      */
15873     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15874
15875     },
15876
15877     /**
15878      * Called after the component is moved, this method is empty by default but can be implemented by any
15879      * subclass that needs to perform custom logic after a move occurs.
15880      * @param {Number} x The new x position
15881      * @param {Number} y The new y position
15882      */
15883     onPosition : function(x, y){
15884
15885     },
15886
15887     // private
15888     adjustSize : function(w, h){
15889         if(this.autoWidth){
15890             w = 'auto';
15891         }
15892         if(this.autoHeight){
15893             h = 'auto';
15894         }
15895         return {width : w, height: h};
15896     },
15897
15898     // private
15899     adjustPosition : function(x, y){
15900         return {x : x, y: y};
15901     }
15902 });/*
15903  * Original code for Roojs - LGPL
15904  * <script type="text/javascript">
15905  */
15906  
15907 /**
15908  * @class Roo.XComponent
15909  * A delayed Element creator...
15910  * Or a way to group chunks of interface together.
15911  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15912  *  used in conjunction with XComponent.build() it will create an instance of each element,
15913  *  then call addxtype() to build the User interface.
15914  * 
15915  * Mypart.xyx = new Roo.XComponent({
15916
15917     parent : 'Mypart.xyz', // empty == document.element.!!
15918     order : '001',
15919     name : 'xxxx'
15920     region : 'xxxx'
15921     disabled : function() {} 
15922      
15923     tree : function() { // return an tree of xtype declared components
15924         var MODULE = this;
15925         return 
15926         {
15927             xtype : 'NestedLayoutPanel',
15928             // technicall
15929         }
15930      ]
15931  *})
15932  *
15933  *
15934  * It can be used to build a big heiracy, with parent etc.
15935  * or you can just use this to render a single compoent to a dom element
15936  * MYPART.render(Roo.Element | String(id) | dom_element )
15937  *
15938  *
15939  * Usage patterns.
15940  *
15941  * Classic Roo
15942  *
15943  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15944  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15945  *
15946  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15947  *
15948  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15949  * - if mulitple topModules exist, the last one is defined as the top module.
15950  *
15951  * Embeded Roo
15952  * 
15953  * When the top level or multiple modules are to embedded into a existing HTML page,
15954  * the parent element can container '#id' of the element where the module will be drawn.
15955  *
15956  * Bootstrap Roo
15957  *
15958  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15959  * it relies more on a include mechanism, where sub modules are included into an outer page.
15960  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15961  * 
15962  * Bootstrap Roo Included elements
15963  *
15964  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15965  * hence confusing the component builder as it thinks there are multiple top level elements. 
15966  *
15967  * 
15968  * 
15969  * @extends Roo.util.Observable
15970  * @constructor
15971  * @param cfg {Object} configuration of component
15972  * 
15973  */
15974 Roo.XComponent = function(cfg) {
15975     Roo.apply(this, cfg);
15976     this.addEvents({ 
15977         /**
15978              * @event built
15979              * Fires when this the componnt is built
15980              * @param {Roo.XComponent} c the component
15981              */
15982         'built' : true
15983         
15984     });
15985     this.region = this.region || 'center'; // default..
15986     Roo.XComponent.register(this);
15987     this.modules = false;
15988     this.el = false; // where the layout goes..
15989     
15990     
15991 }
15992 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15993     /**
15994      * @property el
15995      * The created element (with Roo.factory())
15996      * @type {Roo.Layout}
15997      */
15998     el  : false,
15999     
16000     /**
16001      * @property el
16002      * for BC  - use el in new code
16003      * @type {Roo.Layout}
16004      */
16005     panel : false,
16006     
16007     /**
16008      * @property layout
16009      * for BC  - use el in new code
16010      * @type {Roo.Layout}
16011      */
16012     layout : false,
16013     
16014      /**
16015      * @cfg {Function|boolean} disabled
16016      * If this module is disabled by some rule, return true from the funtion
16017      */
16018     disabled : false,
16019     
16020     /**
16021      * @cfg {String} parent 
16022      * Name of parent element which it get xtype added to..
16023      */
16024     parent: false,
16025     
16026     /**
16027      * @cfg {String} order
16028      * Used to set the order in which elements are created (usefull for multiple tabs)
16029      */
16030     
16031     order : false,
16032     /**
16033      * @cfg {String} name
16034      * String to display while loading.
16035      */
16036     name : false,
16037     /**
16038      * @cfg {String} region
16039      * Region to render component to (defaults to center)
16040      */
16041     region : 'center',
16042     
16043     /**
16044      * @cfg {Array} items
16045      * A single item array - the first element is the root of the tree..
16046      * It's done this way to stay compatible with the Xtype system...
16047      */
16048     items : false,
16049     
16050     /**
16051      * @property _tree
16052      * The method that retuns the tree of parts that make up this compoennt 
16053      * @type {function}
16054      */
16055     _tree  : false,
16056     
16057      /**
16058      * render
16059      * render element to dom or tree
16060      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16061      */
16062     
16063     render : function(el)
16064     {
16065         
16066         el = el || false;
16067         var hp = this.parent ? 1 : 0;
16068         Roo.debug &&  Roo.log(this);
16069         
16070         var tree = this._tree ? this._tree() : this.tree();
16071
16072         
16073         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16074             // if parent is a '#.....' string, then let's use that..
16075             var ename = this.parent.substr(1);
16076             this.parent = false;
16077             Roo.debug && Roo.log(ename);
16078             switch (ename) {
16079                 case 'bootstrap-body':
16080                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16081                         // this is the BorderLayout standard?
16082                        this.parent = { el : true };
16083                        break;
16084                     }
16085                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16086                         // need to insert stuff...
16087                         this.parent =  {
16088                              el : new Roo.bootstrap.layout.Border({
16089                                  el : document.body, 
16090                      
16091                                  center: {
16092                                     titlebar: false,
16093                                     autoScroll:false,
16094                                     closeOnTab: true,
16095                                     tabPosition: 'top',
16096                                       //resizeTabs: true,
16097                                     alwaysShowTabs: true,
16098                                     hideTabs: false
16099                                      //minTabWidth: 140
16100                                  }
16101                              })
16102                         
16103                          };
16104                          break;
16105                     }
16106                          
16107                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16108                         this.parent = { el :  new  Roo.bootstrap.Body() };
16109                         Roo.debug && Roo.log("setting el to doc body");
16110                          
16111                     } else {
16112                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16113                     }
16114                     break;
16115                 case 'bootstrap':
16116                     this.parent = { el : true};
16117                     // fall through
16118                 default:
16119                     el = Roo.get(ename);
16120                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16121                         this.parent = { el : true};
16122                     }
16123                     
16124                     break;
16125             }
16126                 
16127             
16128             if (!el && !this.parent) {
16129                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16130                 return;
16131             }
16132         }
16133         
16134         Roo.debug && Roo.log("EL:");
16135         Roo.debug && Roo.log(el);
16136         Roo.debug && Roo.log("this.parent.el:");
16137         Roo.debug && Roo.log(this.parent.el);
16138         
16139
16140         // altertive root elements ??? - we need a better way to indicate these.
16141         var is_alt = Roo.XComponent.is_alt ||
16142                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16143                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16144                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16145         
16146         
16147         
16148         if (!this.parent && is_alt) {
16149             //el = Roo.get(document.body);
16150             this.parent = { el : true };
16151         }
16152             
16153             
16154         
16155         if (!this.parent) {
16156             
16157             Roo.debug && Roo.log("no parent - creating one");
16158             
16159             el = el ? Roo.get(el) : false;      
16160             
16161             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16162                 
16163                 this.parent =  {
16164                     el : new Roo.bootstrap.layout.Border({
16165                         el: el || document.body,
16166                     
16167                         center: {
16168                             titlebar: false,
16169                             autoScroll:false,
16170                             closeOnTab: true,
16171                             tabPosition: 'top',
16172                              //resizeTabs: true,
16173                             alwaysShowTabs: false,
16174                             hideTabs: true,
16175                             minTabWidth: 140,
16176                             overflow: 'visible'
16177                          }
16178                      })
16179                 };
16180             } else {
16181             
16182                 // it's a top level one..
16183                 this.parent =  {
16184                     el : new Roo.BorderLayout(el || document.body, {
16185                         center: {
16186                             titlebar: false,
16187                             autoScroll:false,
16188                             closeOnTab: true,
16189                             tabPosition: 'top',
16190                              //resizeTabs: true,
16191                             alwaysShowTabs: el && hp? false :  true,
16192                             hideTabs: el || !hp ? true :  false,
16193                             minTabWidth: 140
16194                          }
16195                     })
16196                 };
16197             }
16198         }
16199         
16200         if (!this.parent.el) {
16201                 // probably an old style ctor, which has been disabled.
16202                 return;
16203
16204         }
16205                 // The 'tree' method is  '_tree now' 
16206             
16207         tree.region = tree.region || this.region;
16208         var is_body = false;
16209         if (this.parent.el === true) {
16210             // bootstrap... - body..
16211             if (el) {
16212                 tree.el = el;
16213             }
16214             this.parent.el = Roo.factory(tree);
16215             is_body = true;
16216         }
16217         
16218         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16219         this.fireEvent('built', this);
16220         
16221         this.panel = this.el;
16222         this.layout = this.panel.layout;
16223         this.parentLayout = this.parent.layout  || false;  
16224          
16225     }
16226     
16227 });
16228
16229 Roo.apply(Roo.XComponent, {
16230     /**
16231      * @property  hideProgress
16232      * true to disable the building progress bar.. usefull on single page renders.
16233      * @type Boolean
16234      */
16235     hideProgress : false,
16236     /**
16237      * @property  buildCompleted
16238      * True when the builder has completed building the interface.
16239      * @type Boolean
16240      */
16241     buildCompleted : false,
16242      
16243     /**
16244      * @property  topModule
16245      * the upper most module - uses document.element as it's constructor.
16246      * @type Object
16247      */
16248      
16249     topModule  : false,
16250       
16251     /**
16252      * @property  modules
16253      * array of modules to be created by registration system.
16254      * @type {Array} of Roo.XComponent
16255      */
16256     
16257     modules : [],
16258     /**
16259      * @property  elmodules
16260      * array of modules to be created by which use #ID 
16261      * @type {Array} of Roo.XComponent
16262      */
16263      
16264     elmodules : [],
16265
16266      /**
16267      * @property  is_alt
16268      * Is an alternative Root - normally used by bootstrap or other systems,
16269      *    where the top element in the tree can wrap 'body' 
16270      * @type {boolean}  (default false)
16271      */
16272      
16273     is_alt : false,
16274     /**
16275      * @property  build_from_html
16276      * Build elements from html - used by bootstrap HTML stuff 
16277      *    - this is cleared after build is completed
16278      * @type {boolean}    (default false)
16279      */
16280      
16281     build_from_html : false,
16282     /**
16283      * Register components to be built later.
16284      *
16285      * This solves the following issues
16286      * - Building is not done on page load, but after an authentication process has occured.
16287      * - Interface elements are registered on page load
16288      * - Parent Interface elements may not be loaded before child, so this handles that..
16289      * 
16290      *
16291      * example:
16292      * 
16293      * MyApp.register({
16294           order : '000001',
16295           module : 'Pman.Tab.projectMgr',
16296           region : 'center',
16297           parent : 'Pman.layout',
16298           disabled : false,  // or use a function..
16299         })
16300      
16301      * * @param {Object} details about module
16302      */
16303     register : function(obj) {
16304                 
16305         Roo.XComponent.event.fireEvent('register', obj);
16306         switch(typeof(obj.disabled) ) {
16307                 
16308             case 'undefined':
16309                 break;
16310             
16311             case 'function':
16312                 if ( obj.disabled() ) {
16313                         return;
16314                 }
16315                 break;
16316             
16317             default:
16318                 if (obj.disabled) {
16319                         return;
16320                 }
16321                 break;
16322         }
16323                 
16324         this.modules.push(obj);
16325          
16326     },
16327     /**
16328      * convert a string to an object..
16329      * eg. 'AAA.BBB' -> finds AAA.BBB
16330
16331      */
16332     
16333     toObject : function(str)
16334     {
16335         if (!str || typeof(str) == 'object') {
16336             return str;
16337         }
16338         if (str.substring(0,1) == '#') {
16339             return str;
16340         }
16341
16342         var ar = str.split('.');
16343         var rt, o;
16344         rt = ar.shift();
16345             /** eval:var:o */
16346         try {
16347             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16348         } catch (e) {
16349             throw "Module not found : " + str;
16350         }
16351         
16352         if (o === false) {
16353             throw "Module not found : " + str;
16354         }
16355         Roo.each(ar, function(e) {
16356             if (typeof(o[e]) == 'undefined') {
16357                 throw "Module not found : " + str;
16358             }
16359             o = o[e];
16360         });
16361         
16362         return o;
16363         
16364     },
16365     
16366     
16367     /**
16368      * move modules into their correct place in the tree..
16369      * 
16370      */
16371     preBuild : function ()
16372     {
16373         var _t = this;
16374         Roo.each(this.modules , function (obj)
16375         {
16376             Roo.XComponent.event.fireEvent('beforebuild', obj);
16377             
16378             var opar = obj.parent;
16379             try { 
16380                 obj.parent = this.toObject(opar);
16381             } catch(e) {
16382                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16383                 return;
16384             }
16385             
16386             if (!obj.parent) {
16387                 Roo.debug && Roo.log("GOT top level module");
16388                 Roo.debug && Roo.log(obj);
16389                 obj.modules = new Roo.util.MixedCollection(false, 
16390                     function(o) { return o.order + '' }
16391                 );
16392                 this.topModule = obj;
16393                 return;
16394             }
16395                         // parent is a string (usually a dom element name..)
16396             if (typeof(obj.parent) == 'string') {
16397                 this.elmodules.push(obj);
16398                 return;
16399             }
16400             if (obj.parent.constructor != Roo.XComponent) {
16401                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16402             }
16403             if (!obj.parent.modules) {
16404                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16405                     function(o) { return o.order + '' }
16406                 );
16407             }
16408             if (obj.parent.disabled) {
16409                 obj.disabled = true;
16410             }
16411             obj.parent.modules.add(obj);
16412         }, this);
16413     },
16414     
16415      /**
16416      * make a list of modules to build.
16417      * @return {Array} list of modules. 
16418      */ 
16419     
16420     buildOrder : function()
16421     {
16422         var _this = this;
16423         var cmp = function(a,b) {   
16424             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16425         };
16426         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16427             throw "No top level modules to build";
16428         }
16429         
16430         // make a flat list in order of modules to build.
16431         var mods = this.topModule ? [ this.topModule ] : [];
16432                 
16433         
16434         // elmodules (is a list of DOM based modules )
16435         Roo.each(this.elmodules, function(e) {
16436             mods.push(e);
16437             if (!this.topModule &&
16438                 typeof(e.parent) == 'string' &&
16439                 e.parent.substring(0,1) == '#' &&
16440                 Roo.get(e.parent.substr(1))
16441                ) {
16442                 
16443                 _this.topModule = e;
16444             }
16445             
16446         });
16447
16448         
16449         // add modules to their parents..
16450         var addMod = function(m) {
16451             Roo.debug && Roo.log("build Order: add: " + m.name);
16452                 
16453             mods.push(m);
16454             if (m.modules && !m.disabled) {
16455                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16456                 m.modules.keySort('ASC',  cmp );
16457                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16458     
16459                 m.modules.each(addMod);
16460             } else {
16461                 Roo.debug && Roo.log("build Order: no child modules");
16462             }
16463             // not sure if this is used any more..
16464             if (m.finalize) {
16465                 m.finalize.name = m.name + " (clean up) ";
16466                 mods.push(m.finalize);
16467             }
16468             
16469         }
16470         if (this.topModule && this.topModule.modules) { 
16471             this.topModule.modules.keySort('ASC',  cmp );
16472             this.topModule.modules.each(addMod);
16473         } 
16474         return mods;
16475     },
16476     
16477      /**
16478      * Build the registered modules.
16479      * @param {Object} parent element.
16480      * @param {Function} optional method to call after module has been added.
16481      * 
16482      */ 
16483    
16484     build : function(opts) 
16485     {
16486         
16487         if (typeof(opts) != 'undefined') {
16488             Roo.apply(this,opts);
16489         }
16490         
16491         this.preBuild();
16492         var mods = this.buildOrder();
16493       
16494         //this.allmods = mods;
16495         //Roo.debug && Roo.log(mods);
16496         //return;
16497         if (!mods.length) { // should not happen
16498             throw "NO modules!!!";
16499         }
16500         
16501         
16502         var msg = "Building Interface...";
16503         // flash it up as modal - so we store the mask!?
16504         if (!this.hideProgress && Roo.MessageBox) {
16505             Roo.MessageBox.show({ title: 'loading' });
16506             Roo.MessageBox.show({
16507                title: "Please wait...",
16508                msg: msg,
16509                width:450,
16510                progress:true,
16511                closable:false,
16512                modal: false
16513               
16514             });
16515         }
16516         var total = mods.length;
16517         
16518         var _this = this;
16519         var progressRun = function() {
16520             if (!mods.length) {
16521                 Roo.debug && Roo.log('hide?');
16522                 if (!this.hideProgress && Roo.MessageBox) {
16523                     Roo.MessageBox.hide();
16524                 }
16525                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16526                 
16527                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16528                 
16529                 // THE END...
16530                 return false;   
16531             }
16532             
16533             var m = mods.shift();
16534             
16535             
16536             Roo.debug && Roo.log(m);
16537             // not sure if this is supported any more.. - modules that are are just function
16538             if (typeof(m) == 'function') { 
16539                 m.call(this);
16540                 return progressRun.defer(10, _this);
16541             } 
16542             
16543             
16544             msg = "Building Interface " + (total  - mods.length) + 
16545                     " of " + total + 
16546                     (m.name ? (' - ' + m.name) : '');
16547                         Roo.debug && Roo.log(msg);
16548             if (!_this.hideProgress &&  Roo.MessageBox) { 
16549                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16550             }
16551             
16552          
16553             // is the module disabled?
16554             var disabled = (typeof(m.disabled) == 'function') ?
16555                 m.disabled.call(m.module.disabled) : m.disabled;    
16556             
16557             
16558             if (disabled) {
16559                 return progressRun(); // we do not update the display!
16560             }
16561             
16562             // now build 
16563             
16564                         
16565                         
16566             m.render();
16567             // it's 10 on top level, and 1 on others??? why...
16568             return progressRun.defer(10, _this);
16569              
16570         }
16571         progressRun.defer(1, _this);
16572      
16573         
16574         
16575     },
16576         
16577         
16578         /**
16579          * Event Object.
16580          *
16581          *
16582          */
16583         event: false, 
16584     /**
16585          * wrapper for event.on - aliased later..  
16586          * Typically use to register a event handler for register:
16587          *
16588          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16589          *
16590          */
16591     on : false
16592    
16593     
16594     
16595 });
16596
16597 Roo.XComponent.event = new Roo.util.Observable({
16598                 events : { 
16599                         /**
16600                          * @event register
16601                          * Fires when an Component is registered,
16602                          * set the disable property on the Component to stop registration.
16603                          * @param {Roo.XComponent} c the component being registerd.
16604                          * 
16605                          */
16606                         'register' : true,
16607             /**
16608                          * @event beforebuild
16609                          * Fires before each Component is built
16610                          * can be used to apply permissions.
16611                          * @param {Roo.XComponent} c the component being registerd.
16612                          * 
16613                          */
16614                         'beforebuild' : true,
16615                         /**
16616                          * @event buildcomplete
16617                          * Fires on the top level element when all elements have been built
16618                          * @param {Roo.XComponent} the top level component.
16619                          */
16620                         'buildcomplete' : true
16621                         
16622                 }
16623 });
16624
16625 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16626  //
16627  /**
16628  * marked - a markdown parser
16629  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16630  * https://github.com/chjj/marked
16631  */
16632
16633
16634 /**
16635  *
16636  * Roo.Markdown - is a very crude wrapper around marked..
16637  *
16638  * usage:
16639  * 
16640  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16641  * 
16642  * Note: move the sample code to the bottom of this
16643  * file before uncommenting it.
16644  *
16645  */
16646
16647 Roo.Markdown = {};
16648 Roo.Markdown.toHtml = function(text) {
16649     
16650     var c = new Roo.Markdown.marked.setOptions({
16651             renderer: new Roo.Markdown.marked.Renderer(),
16652             gfm: true,
16653             tables: true,
16654             breaks: false,
16655             pedantic: false,
16656             sanitize: false,
16657             smartLists: true,
16658             smartypants: false
16659           });
16660     // A FEW HACKS!!?
16661     
16662     text = text.replace(/\\\n/g,' ');
16663     return Roo.Markdown.marked(text);
16664 };
16665 //
16666 // converter
16667 //
16668 // Wraps all "globals" so that the only thing
16669 // exposed is makeHtml().
16670 //
16671 (function() {
16672     
16673     /**
16674      * Block-Level Grammar
16675      */
16676     
16677     var block = {
16678       newline: /^\n+/,
16679       code: /^( {4}[^\n]+\n*)+/,
16680       fences: noop,
16681       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16682       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16683       nptable: noop,
16684       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16685       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16686       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16687       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16688       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16689       table: noop,
16690       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16691       text: /^[^\n]+/
16692     };
16693     
16694     block.bullet = /(?:[*+-]|\d+\.)/;
16695     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16696     block.item = replace(block.item, 'gm')
16697       (/bull/g, block.bullet)
16698       ();
16699     
16700     block.list = replace(block.list)
16701       (/bull/g, block.bullet)
16702       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16703       ('def', '\\n+(?=' + block.def.source + ')')
16704       ();
16705     
16706     block.blockquote = replace(block.blockquote)
16707       ('def', block.def)
16708       ();
16709     
16710     block._tag = '(?!(?:'
16711       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16712       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16713       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16714     
16715     block.html = replace(block.html)
16716       ('comment', /<!--[\s\S]*?-->/)
16717       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16718       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16719       (/tag/g, block._tag)
16720       ();
16721     
16722     block.paragraph = replace(block.paragraph)
16723       ('hr', block.hr)
16724       ('heading', block.heading)
16725       ('lheading', block.lheading)
16726       ('blockquote', block.blockquote)
16727       ('tag', '<' + block._tag)
16728       ('def', block.def)
16729       ();
16730     
16731     /**
16732      * Normal Block Grammar
16733      */
16734     
16735     block.normal = merge({}, block);
16736     
16737     /**
16738      * GFM Block Grammar
16739      */
16740     
16741     block.gfm = merge({}, block.normal, {
16742       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16743       paragraph: /^/,
16744       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16745     });
16746     
16747     block.gfm.paragraph = replace(block.paragraph)
16748       ('(?!', '(?!'
16749         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16750         + block.list.source.replace('\\1', '\\3') + '|')
16751       ();
16752     
16753     /**
16754      * GFM + Tables Block Grammar
16755      */
16756     
16757     block.tables = merge({}, block.gfm, {
16758       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16759       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16760     });
16761     
16762     /**
16763      * Block Lexer
16764      */
16765     
16766     function Lexer(options) {
16767       this.tokens = [];
16768       this.tokens.links = {};
16769       this.options = options || marked.defaults;
16770       this.rules = block.normal;
16771     
16772       if (this.options.gfm) {
16773         if (this.options.tables) {
16774           this.rules = block.tables;
16775         } else {
16776           this.rules = block.gfm;
16777         }
16778       }
16779     }
16780     
16781     /**
16782      * Expose Block Rules
16783      */
16784     
16785     Lexer.rules = block;
16786     
16787     /**
16788      * Static Lex Method
16789      */
16790     
16791     Lexer.lex = function(src, options) {
16792       var lexer = new Lexer(options);
16793       return lexer.lex(src);
16794     };
16795     
16796     /**
16797      * Preprocessing
16798      */
16799     
16800     Lexer.prototype.lex = function(src) {
16801       src = src
16802         .replace(/\r\n|\r/g, '\n')
16803         .replace(/\t/g, '    ')
16804         .replace(/\u00a0/g, ' ')
16805         .replace(/\u2424/g, '\n');
16806     
16807       return this.token(src, true);
16808     };
16809     
16810     /**
16811      * Lexing
16812      */
16813     
16814     Lexer.prototype.token = function(src, top, bq) {
16815       var src = src.replace(/^ +$/gm, '')
16816         , next
16817         , loose
16818         , cap
16819         , bull
16820         , b
16821         , item
16822         , space
16823         , i
16824         , l;
16825     
16826       while (src) {
16827         // newline
16828         if (cap = this.rules.newline.exec(src)) {
16829           src = src.substring(cap[0].length);
16830           if (cap[0].length > 1) {
16831             this.tokens.push({
16832               type: 'space'
16833             });
16834           }
16835         }
16836     
16837         // code
16838         if (cap = this.rules.code.exec(src)) {
16839           src = src.substring(cap[0].length);
16840           cap = cap[0].replace(/^ {4}/gm, '');
16841           this.tokens.push({
16842             type: 'code',
16843             text: !this.options.pedantic
16844               ? cap.replace(/\n+$/, '')
16845               : cap
16846           });
16847           continue;
16848         }
16849     
16850         // fences (gfm)
16851         if (cap = this.rules.fences.exec(src)) {
16852           src = src.substring(cap[0].length);
16853           this.tokens.push({
16854             type: 'code',
16855             lang: cap[2],
16856             text: cap[3] || ''
16857           });
16858           continue;
16859         }
16860     
16861         // heading
16862         if (cap = this.rules.heading.exec(src)) {
16863           src = src.substring(cap[0].length);
16864           this.tokens.push({
16865             type: 'heading',
16866             depth: cap[1].length,
16867             text: cap[2]
16868           });
16869           continue;
16870         }
16871     
16872         // table no leading pipe (gfm)
16873         if (top && (cap = this.rules.nptable.exec(src))) {
16874           src = src.substring(cap[0].length);
16875     
16876           item = {
16877             type: 'table',
16878             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16879             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16880             cells: cap[3].replace(/\n$/, '').split('\n')
16881           };
16882     
16883           for (i = 0; i < item.align.length; i++) {
16884             if (/^ *-+: *$/.test(item.align[i])) {
16885               item.align[i] = 'right';
16886             } else if (/^ *:-+: *$/.test(item.align[i])) {
16887               item.align[i] = 'center';
16888             } else if (/^ *:-+ *$/.test(item.align[i])) {
16889               item.align[i] = 'left';
16890             } else {
16891               item.align[i] = null;
16892             }
16893           }
16894     
16895           for (i = 0; i < item.cells.length; i++) {
16896             item.cells[i] = item.cells[i].split(/ *\| */);
16897           }
16898     
16899           this.tokens.push(item);
16900     
16901           continue;
16902         }
16903     
16904         // lheading
16905         if (cap = this.rules.lheading.exec(src)) {
16906           src = src.substring(cap[0].length);
16907           this.tokens.push({
16908             type: 'heading',
16909             depth: cap[2] === '=' ? 1 : 2,
16910             text: cap[1]
16911           });
16912           continue;
16913         }
16914     
16915         // hr
16916         if (cap = this.rules.hr.exec(src)) {
16917           src = src.substring(cap[0].length);
16918           this.tokens.push({
16919             type: 'hr'
16920           });
16921           continue;
16922         }
16923     
16924         // blockquote
16925         if (cap = this.rules.blockquote.exec(src)) {
16926           src = src.substring(cap[0].length);
16927     
16928           this.tokens.push({
16929             type: 'blockquote_start'
16930           });
16931     
16932           cap = cap[0].replace(/^ *> ?/gm, '');
16933     
16934           // Pass `top` to keep the current
16935           // "toplevel" state. This is exactly
16936           // how markdown.pl works.
16937           this.token(cap, top, true);
16938     
16939           this.tokens.push({
16940             type: 'blockquote_end'
16941           });
16942     
16943           continue;
16944         }
16945     
16946         // list
16947         if (cap = this.rules.list.exec(src)) {
16948           src = src.substring(cap[0].length);
16949           bull = cap[2];
16950     
16951           this.tokens.push({
16952             type: 'list_start',
16953             ordered: bull.length > 1
16954           });
16955     
16956           // Get each top-level item.
16957           cap = cap[0].match(this.rules.item);
16958     
16959           next = false;
16960           l = cap.length;
16961           i = 0;
16962     
16963           for (; i < l; i++) {
16964             item = cap[i];
16965     
16966             // Remove the list item's bullet
16967             // so it is seen as the next token.
16968             space = item.length;
16969             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16970     
16971             // Outdent whatever the
16972             // list item contains. Hacky.
16973             if (~item.indexOf('\n ')) {
16974               space -= item.length;
16975               item = !this.options.pedantic
16976                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16977                 : item.replace(/^ {1,4}/gm, '');
16978             }
16979     
16980             // Determine whether the next list item belongs here.
16981             // Backpedal if it does not belong in this list.
16982             if (this.options.smartLists && i !== l - 1) {
16983               b = block.bullet.exec(cap[i + 1])[0];
16984               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16985                 src = cap.slice(i + 1).join('\n') + src;
16986                 i = l - 1;
16987               }
16988             }
16989     
16990             // Determine whether item is loose or not.
16991             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16992             // for discount behavior.
16993             loose = next || /\n\n(?!\s*$)/.test(item);
16994             if (i !== l - 1) {
16995               next = item.charAt(item.length - 1) === '\n';
16996               if (!loose) { loose = next; }
16997             }
16998     
16999             this.tokens.push({
17000               type: loose
17001                 ? 'loose_item_start'
17002                 : 'list_item_start'
17003             });
17004     
17005             // Recurse.
17006             this.token(item, false, bq);
17007     
17008             this.tokens.push({
17009               type: 'list_item_end'
17010             });
17011           }
17012     
17013           this.tokens.push({
17014             type: 'list_end'
17015           });
17016     
17017           continue;
17018         }
17019     
17020         // html
17021         if (cap = this.rules.html.exec(src)) {
17022           src = src.substring(cap[0].length);
17023           this.tokens.push({
17024             type: this.options.sanitize
17025               ? 'paragraph'
17026               : 'html',
17027             pre: !this.options.sanitizer
17028               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17029             text: cap[0]
17030           });
17031           continue;
17032         }
17033     
17034         // def
17035         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17036           src = src.substring(cap[0].length);
17037           this.tokens.links[cap[1].toLowerCase()] = {
17038             href: cap[2],
17039             title: cap[3]
17040           };
17041           continue;
17042         }
17043     
17044         // table (gfm)
17045         if (top && (cap = this.rules.table.exec(src))) {
17046           src = src.substring(cap[0].length);
17047     
17048           item = {
17049             type: 'table',
17050             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17051             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17052             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17053           };
17054     
17055           for (i = 0; i < item.align.length; i++) {
17056             if (/^ *-+: *$/.test(item.align[i])) {
17057               item.align[i] = 'right';
17058             } else if (/^ *:-+: *$/.test(item.align[i])) {
17059               item.align[i] = 'center';
17060             } else if (/^ *:-+ *$/.test(item.align[i])) {
17061               item.align[i] = 'left';
17062             } else {
17063               item.align[i] = null;
17064             }
17065           }
17066     
17067           for (i = 0; i < item.cells.length; i++) {
17068             item.cells[i] = item.cells[i]
17069               .replace(/^ *\| *| *\| *$/g, '')
17070               .split(/ *\| */);
17071           }
17072     
17073           this.tokens.push(item);
17074     
17075           continue;
17076         }
17077     
17078         // top-level paragraph
17079         if (top && (cap = this.rules.paragraph.exec(src))) {
17080           src = src.substring(cap[0].length);
17081           this.tokens.push({
17082             type: 'paragraph',
17083             text: cap[1].charAt(cap[1].length - 1) === '\n'
17084               ? cap[1].slice(0, -1)
17085               : cap[1]
17086           });
17087           continue;
17088         }
17089     
17090         // text
17091         if (cap = this.rules.text.exec(src)) {
17092           // Top-level should never reach here.
17093           src = src.substring(cap[0].length);
17094           this.tokens.push({
17095             type: 'text',
17096             text: cap[0]
17097           });
17098           continue;
17099         }
17100     
17101         if (src) {
17102           throw new
17103             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17104         }
17105       }
17106     
17107       return this.tokens;
17108     };
17109     
17110     /**
17111      * Inline-Level Grammar
17112      */
17113     
17114     var inline = {
17115       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17116       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17117       url: noop,
17118       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17119       link: /^!?\[(inside)\]\(href\)/,
17120       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17121       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17122       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17123       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17124       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17125       br: /^ {2,}\n(?!\s*$)/,
17126       del: noop,
17127       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17128     };
17129     
17130     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17131     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17132     
17133     inline.link = replace(inline.link)
17134       ('inside', inline._inside)
17135       ('href', inline._href)
17136       ();
17137     
17138     inline.reflink = replace(inline.reflink)
17139       ('inside', inline._inside)
17140       ();
17141     
17142     /**
17143      * Normal Inline Grammar
17144      */
17145     
17146     inline.normal = merge({}, inline);
17147     
17148     /**
17149      * Pedantic Inline Grammar
17150      */
17151     
17152     inline.pedantic = merge({}, inline.normal, {
17153       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17154       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17155     });
17156     
17157     /**
17158      * GFM Inline Grammar
17159      */
17160     
17161     inline.gfm = merge({}, inline.normal, {
17162       escape: replace(inline.escape)('])', '~|])')(),
17163       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17164       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17165       text: replace(inline.text)
17166         (']|', '~]|')
17167         ('|', '|https?://|')
17168         ()
17169     });
17170     
17171     /**
17172      * GFM + Line Breaks Inline Grammar
17173      */
17174     
17175     inline.breaks = merge({}, inline.gfm, {
17176       br: replace(inline.br)('{2,}', '*')(),
17177       text: replace(inline.gfm.text)('{2,}', '*')()
17178     });
17179     
17180     /**
17181      * Inline Lexer & Compiler
17182      */
17183     
17184     function InlineLexer(links, options) {
17185       this.options = options || marked.defaults;
17186       this.links = links;
17187       this.rules = inline.normal;
17188       this.renderer = this.options.renderer || new Renderer;
17189       this.renderer.options = this.options;
17190     
17191       if (!this.links) {
17192         throw new
17193           Error('Tokens array requires a `links` property.');
17194       }
17195     
17196       if (this.options.gfm) {
17197         if (this.options.breaks) {
17198           this.rules = inline.breaks;
17199         } else {
17200           this.rules = inline.gfm;
17201         }
17202       } else if (this.options.pedantic) {
17203         this.rules = inline.pedantic;
17204       }
17205     }
17206     
17207     /**
17208      * Expose Inline Rules
17209      */
17210     
17211     InlineLexer.rules = inline;
17212     
17213     /**
17214      * Static Lexing/Compiling Method
17215      */
17216     
17217     InlineLexer.output = function(src, links, options) {
17218       var inline = new InlineLexer(links, options);
17219       return inline.output(src);
17220     };
17221     
17222     /**
17223      * Lexing/Compiling
17224      */
17225     
17226     InlineLexer.prototype.output = function(src) {
17227       var out = ''
17228         , link
17229         , text
17230         , href
17231         , cap;
17232     
17233       while (src) {
17234         // escape
17235         if (cap = this.rules.escape.exec(src)) {
17236           src = src.substring(cap[0].length);
17237           out += cap[1];
17238           continue;
17239         }
17240     
17241         // autolink
17242         if (cap = this.rules.autolink.exec(src)) {
17243           src = src.substring(cap[0].length);
17244           if (cap[2] === '@') {
17245             text = cap[1].charAt(6) === ':'
17246               ? this.mangle(cap[1].substring(7))
17247               : this.mangle(cap[1]);
17248             href = this.mangle('mailto:') + text;
17249           } else {
17250             text = escape(cap[1]);
17251             href = text;
17252           }
17253           out += this.renderer.link(href, null, text);
17254           continue;
17255         }
17256     
17257         // url (gfm)
17258         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17259           src = src.substring(cap[0].length);
17260           text = escape(cap[1]);
17261           href = text;
17262           out += this.renderer.link(href, null, text);
17263           continue;
17264         }
17265     
17266         // tag
17267         if (cap = this.rules.tag.exec(src)) {
17268           if (!this.inLink && /^<a /i.test(cap[0])) {
17269             this.inLink = true;
17270           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17271             this.inLink = false;
17272           }
17273           src = src.substring(cap[0].length);
17274           out += this.options.sanitize
17275             ? this.options.sanitizer
17276               ? this.options.sanitizer(cap[0])
17277               : escape(cap[0])
17278             : cap[0];
17279           continue;
17280         }
17281     
17282         // link
17283         if (cap = this.rules.link.exec(src)) {
17284           src = src.substring(cap[0].length);
17285           this.inLink = true;
17286           out += this.outputLink(cap, {
17287             href: cap[2],
17288             title: cap[3]
17289           });
17290           this.inLink = false;
17291           continue;
17292         }
17293     
17294         // reflink, nolink
17295         if ((cap = this.rules.reflink.exec(src))
17296             || (cap = this.rules.nolink.exec(src))) {
17297           src = src.substring(cap[0].length);
17298           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17299           link = this.links[link.toLowerCase()];
17300           if (!link || !link.href) {
17301             out += cap[0].charAt(0);
17302             src = cap[0].substring(1) + src;
17303             continue;
17304           }
17305           this.inLink = true;
17306           out += this.outputLink(cap, link);
17307           this.inLink = false;
17308           continue;
17309         }
17310     
17311         // strong
17312         if (cap = this.rules.strong.exec(src)) {
17313           src = src.substring(cap[0].length);
17314           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17315           continue;
17316         }
17317     
17318         // em
17319         if (cap = this.rules.em.exec(src)) {
17320           src = src.substring(cap[0].length);
17321           out += this.renderer.em(this.output(cap[2] || cap[1]));
17322           continue;
17323         }
17324     
17325         // code
17326         if (cap = this.rules.code.exec(src)) {
17327           src = src.substring(cap[0].length);
17328           out += this.renderer.codespan(escape(cap[2], true));
17329           continue;
17330         }
17331     
17332         // br
17333         if (cap = this.rules.br.exec(src)) {
17334           src = src.substring(cap[0].length);
17335           out += this.renderer.br();
17336           continue;
17337         }
17338     
17339         // del (gfm)
17340         if (cap = this.rules.del.exec(src)) {
17341           src = src.substring(cap[0].length);
17342           out += this.renderer.del(this.output(cap[1]));
17343           continue;
17344         }
17345     
17346         // text
17347         if (cap = this.rules.text.exec(src)) {
17348           src = src.substring(cap[0].length);
17349           out += this.renderer.text(escape(this.smartypants(cap[0])));
17350           continue;
17351         }
17352     
17353         if (src) {
17354           throw new
17355             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17356         }
17357       }
17358     
17359       return out;
17360     };
17361     
17362     /**
17363      * Compile Link
17364      */
17365     
17366     InlineLexer.prototype.outputLink = function(cap, link) {
17367       var href = escape(link.href)
17368         , title = link.title ? escape(link.title) : null;
17369     
17370       return cap[0].charAt(0) !== '!'
17371         ? this.renderer.link(href, title, this.output(cap[1]))
17372         : this.renderer.image(href, title, escape(cap[1]));
17373     };
17374     
17375     /**
17376      * Smartypants Transformations
17377      */
17378     
17379     InlineLexer.prototype.smartypants = function(text) {
17380       if (!this.options.smartypants)  { return text; }
17381       return text
17382         // em-dashes
17383         .replace(/---/g, '\u2014')
17384         // en-dashes
17385         .replace(/--/g, '\u2013')
17386         // opening singles
17387         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17388         // closing singles & apostrophes
17389         .replace(/'/g, '\u2019')
17390         // opening doubles
17391         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17392         // closing doubles
17393         .replace(/"/g, '\u201d')
17394         // ellipses
17395         .replace(/\.{3}/g, '\u2026');
17396     };
17397     
17398     /**
17399      * Mangle Links
17400      */
17401     
17402     InlineLexer.prototype.mangle = function(text) {
17403       if (!this.options.mangle) { return text; }
17404       var out = ''
17405         , l = text.length
17406         , i = 0
17407         , ch;
17408     
17409       for (; i < l; i++) {
17410         ch = text.charCodeAt(i);
17411         if (Math.random() > 0.5) {
17412           ch = 'x' + ch.toString(16);
17413         }
17414         out += '&#' + ch + ';';
17415       }
17416     
17417       return out;
17418     };
17419     
17420     /**
17421      * Renderer
17422      */
17423     
17424     function Renderer(options) {
17425       this.options = options || {};
17426     }
17427     
17428     Renderer.prototype.code = function(code, lang, escaped) {
17429       if (this.options.highlight) {
17430         var out = this.options.highlight(code, lang);
17431         if (out != null && out !== code) {
17432           escaped = true;
17433           code = out;
17434         }
17435       } else {
17436             // hack!!! - it's already escapeD?
17437             escaped = true;
17438       }
17439     
17440       if (!lang) {
17441         return '<pre><code>'
17442           + (escaped ? code : escape(code, true))
17443           + '\n</code></pre>';
17444       }
17445     
17446       return '<pre><code class="'
17447         + this.options.langPrefix
17448         + escape(lang, true)
17449         + '">'
17450         + (escaped ? code : escape(code, true))
17451         + '\n</code></pre>\n';
17452     };
17453     
17454     Renderer.prototype.blockquote = function(quote) {
17455       return '<blockquote>\n' + quote + '</blockquote>\n';
17456     };
17457     
17458     Renderer.prototype.html = function(html) {
17459       return html;
17460     };
17461     
17462     Renderer.prototype.heading = function(text, level, raw) {
17463       return '<h'
17464         + level
17465         + ' id="'
17466         + this.options.headerPrefix
17467         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17468         + '">'
17469         + text
17470         + '</h'
17471         + level
17472         + '>\n';
17473     };
17474     
17475     Renderer.prototype.hr = function() {
17476       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17477     };
17478     
17479     Renderer.prototype.list = function(body, ordered) {
17480       var type = ordered ? 'ol' : 'ul';
17481       return '<' + type + '>\n' + body + '</' + type + '>\n';
17482     };
17483     
17484     Renderer.prototype.listitem = function(text) {
17485       return '<li>' + text + '</li>\n';
17486     };
17487     
17488     Renderer.prototype.paragraph = function(text) {
17489       return '<p>' + text + '</p>\n';
17490     };
17491     
17492     Renderer.prototype.table = function(header, body) {
17493       return '<table class="table table-striped">\n'
17494         + '<thead>\n'
17495         + header
17496         + '</thead>\n'
17497         + '<tbody>\n'
17498         + body
17499         + '</tbody>\n'
17500         + '</table>\n';
17501     };
17502     
17503     Renderer.prototype.tablerow = function(content) {
17504       return '<tr>\n' + content + '</tr>\n';
17505     };
17506     
17507     Renderer.prototype.tablecell = function(content, flags) {
17508       var type = flags.header ? 'th' : 'td';
17509       var tag = flags.align
17510         ? '<' + type + ' style="text-align:' + flags.align + '">'
17511         : '<' + type + '>';
17512       return tag + content + '</' + type + '>\n';
17513     };
17514     
17515     // span level renderer
17516     Renderer.prototype.strong = function(text) {
17517       return '<strong>' + text + '</strong>';
17518     };
17519     
17520     Renderer.prototype.em = function(text) {
17521       return '<em>' + text + '</em>';
17522     };
17523     
17524     Renderer.prototype.codespan = function(text) {
17525       return '<code>' + text + '</code>';
17526     };
17527     
17528     Renderer.prototype.br = function() {
17529       return this.options.xhtml ? '<br/>' : '<br>';
17530     };
17531     
17532     Renderer.prototype.del = function(text) {
17533       return '<del>' + text + '</del>';
17534     };
17535     
17536     Renderer.prototype.link = function(href, title, text) {
17537       if (this.options.sanitize) {
17538         try {
17539           var prot = decodeURIComponent(unescape(href))
17540             .replace(/[^\w:]/g, '')
17541             .toLowerCase();
17542         } catch (e) {
17543           return '';
17544         }
17545         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17546           return '';
17547         }
17548       }
17549       var out = '<a href="' + href + '"';
17550       if (title) {
17551         out += ' title="' + title + '"';
17552       }
17553       out += '>' + text + '</a>';
17554       return out;
17555     };
17556     
17557     Renderer.prototype.image = function(href, title, text) {
17558       var out = '<img src="' + href + '" alt="' + text + '"';
17559       if (title) {
17560         out += ' title="' + title + '"';
17561       }
17562       out += this.options.xhtml ? '/>' : '>';
17563       return out;
17564     };
17565     
17566     Renderer.prototype.text = function(text) {
17567       return text;
17568     };
17569     
17570     /**
17571      * Parsing & Compiling
17572      */
17573     
17574     function Parser(options) {
17575       this.tokens = [];
17576       this.token = null;
17577       this.options = options || marked.defaults;
17578       this.options.renderer = this.options.renderer || new Renderer;
17579       this.renderer = this.options.renderer;
17580       this.renderer.options = this.options;
17581     }
17582     
17583     /**
17584      * Static Parse Method
17585      */
17586     
17587     Parser.parse = function(src, options, renderer) {
17588       var parser = new Parser(options, renderer);
17589       return parser.parse(src);
17590     };
17591     
17592     /**
17593      * Parse Loop
17594      */
17595     
17596     Parser.prototype.parse = function(src) {
17597       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17598       this.tokens = src.reverse();
17599     
17600       var out = '';
17601       while (this.next()) {
17602         out += this.tok();
17603       }
17604     
17605       return out;
17606     };
17607     
17608     /**
17609      * Next Token
17610      */
17611     
17612     Parser.prototype.next = function() {
17613       return this.token = this.tokens.pop();
17614     };
17615     
17616     /**
17617      * Preview Next Token
17618      */
17619     
17620     Parser.prototype.peek = function() {
17621       return this.tokens[this.tokens.length - 1] || 0;
17622     };
17623     
17624     /**
17625      * Parse Text Tokens
17626      */
17627     
17628     Parser.prototype.parseText = function() {
17629       var body = this.token.text;
17630     
17631       while (this.peek().type === 'text') {
17632         body += '\n' + this.next().text;
17633       }
17634     
17635       return this.inline.output(body);
17636     };
17637     
17638     /**
17639      * Parse Current Token
17640      */
17641     
17642     Parser.prototype.tok = function() {
17643       switch (this.token.type) {
17644         case 'space': {
17645           return '';
17646         }
17647         case 'hr': {
17648           return this.renderer.hr();
17649         }
17650         case 'heading': {
17651           return this.renderer.heading(
17652             this.inline.output(this.token.text),
17653             this.token.depth,
17654             this.token.text);
17655         }
17656         case 'code': {
17657           return this.renderer.code(this.token.text,
17658             this.token.lang,
17659             this.token.escaped);
17660         }
17661         case 'table': {
17662           var header = ''
17663             , body = ''
17664             , i
17665             , row
17666             , cell
17667             , flags
17668             , j;
17669     
17670           // header
17671           cell = '';
17672           for (i = 0; i < this.token.header.length; i++) {
17673             flags = { header: true, align: this.token.align[i] };
17674             cell += this.renderer.tablecell(
17675               this.inline.output(this.token.header[i]),
17676               { header: true, align: this.token.align[i] }
17677             );
17678           }
17679           header += this.renderer.tablerow(cell);
17680     
17681           for (i = 0; i < this.token.cells.length; i++) {
17682             row = this.token.cells[i];
17683     
17684             cell = '';
17685             for (j = 0; j < row.length; j++) {
17686               cell += this.renderer.tablecell(
17687                 this.inline.output(row[j]),
17688                 { header: false, align: this.token.align[j] }
17689               );
17690             }
17691     
17692             body += this.renderer.tablerow(cell);
17693           }
17694           return this.renderer.table(header, body);
17695         }
17696         case 'blockquote_start': {
17697           var body = '';
17698     
17699           while (this.next().type !== 'blockquote_end') {
17700             body += this.tok();
17701           }
17702     
17703           return this.renderer.blockquote(body);
17704         }
17705         case 'list_start': {
17706           var body = ''
17707             , ordered = this.token.ordered;
17708     
17709           while (this.next().type !== 'list_end') {
17710             body += this.tok();
17711           }
17712     
17713           return this.renderer.list(body, ordered);
17714         }
17715         case 'list_item_start': {
17716           var body = '';
17717     
17718           while (this.next().type !== 'list_item_end') {
17719             body += this.token.type === 'text'
17720               ? this.parseText()
17721               : this.tok();
17722           }
17723     
17724           return this.renderer.listitem(body);
17725         }
17726         case 'loose_item_start': {
17727           var body = '';
17728     
17729           while (this.next().type !== 'list_item_end') {
17730             body += this.tok();
17731           }
17732     
17733           return this.renderer.listitem(body);
17734         }
17735         case 'html': {
17736           var html = !this.token.pre && !this.options.pedantic
17737             ? this.inline.output(this.token.text)
17738             : this.token.text;
17739           return this.renderer.html(html);
17740         }
17741         case 'paragraph': {
17742           return this.renderer.paragraph(this.inline.output(this.token.text));
17743         }
17744         case 'text': {
17745           return this.renderer.paragraph(this.parseText());
17746         }
17747       }
17748     };
17749     
17750     /**
17751      * Helpers
17752      */
17753     
17754     function escape(html, encode) {
17755       return html
17756         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17757         .replace(/</g, '&lt;')
17758         .replace(/>/g, '&gt;')
17759         .replace(/"/g, '&quot;')
17760         .replace(/'/g, '&#39;');
17761     }
17762     
17763     function unescape(html) {
17764         // explicitly match decimal, hex, and named HTML entities 
17765       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17766         n = n.toLowerCase();
17767         if (n === 'colon') { return ':'; }
17768         if (n.charAt(0) === '#') {
17769           return n.charAt(1) === 'x'
17770             ? String.fromCharCode(parseInt(n.substring(2), 16))
17771             : String.fromCharCode(+n.substring(1));
17772         }
17773         return '';
17774       });
17775     }
17776     
17777     function replace(regex, opt) {
17778       regex = regex.source;
17779       opt = opt || '';
17780       return function self(name, val) {
17781         if (!name) { return new RegExp(regex, opt); }
17782         val = val.source || val;
17783         val = val.replace(/(^|[^\[])\^/g, '$1');
17784         regex = regex.replace(name, val);
17785         return self;
17786       };
17787     }
17788     
17789     function noop() {}
17790     noop.exec = noop;
17791     
17792     function merge(obj) {
17793       var i = 1
17794         , target
17795         , key;
17796     
17797       for (; i < arguments.length; i++) {
17798         target = arguments[i];
17799         for (key in target) {
17800           if (Object.prototype.hasOwnProperty.call(target, key)) {
17801             obj[key] = target[key];
17802           }
17803         }
17804       }
17805     
17806       return obj;
17807     }
17808     
17809     
17810     /**
17811      * Marked
17812      */
17813     
17814     function marked(src, opt, callback) {
17815       if (callback || typeof opt === 'function') {
17816         if (!callback) {
17817           callback = opt;
17818           opt = null;
17819         }
17820     
17821         opt = merge({}, marked.defaults, opt || {});
17822     
17823         var highlight = opt.highlight
17824           , tokens
17825           , pending
17826           , i = 0;
17827     
17828         try {
17829           tokens = Lexer.lex(src, opt)
17830         } catch (e) {
17831           return callback(e);
17832         }
17833     
17834         pending = tokens.length;
17835     
17836         var done = function(err) {
17837           if (err) {
17838             opt.highlight = highlight;
17839             return callback(err);
17840           }
17841     
17842           var out;
17843     
17844           try {
17845             out = Parser.parse(tokens, opt);
17846           } catch (e) {
17847             err = e;
17848           }
17849     
17850           opt.highlight = highlight;
17851     
17852           return err
17853             ? callback(err)
17854             : callback(null, out);
17855         };
17856     
17857         if (!highlight || highlight.length < 3) {
17858           return done();
17859         }
17860     
17861         delete opt.highlight;
17862     
17863         if (!pending) { return done(); }
17864     
17865         for (; i < tokens.length; i++) {
17866           (function(token) {
17867             if (token.type !== 'code') {
17868               return --pending || done();
17869             }
17870             return highlight(token.text, token.lang, function(err, code) {
17871               if (err) { return done(err); }
17872               if (code == null || code === token.text) {
17873                 return --pending || done();
17874               }
17875               token.text = code;
17876               token.escaped = true;
17877               --pending || done();
17878             });
17879           })(tokens[i]);
17880         }
17881     
17882         return;
17883       }
17884       try {
17885         if (opt) { opt = merge({}, marked.defaults, opt); }
17886         return Parser.parse(Lexer.lex(src, opt), opt);
17887       } catch (e) {
17888         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17889         if ((opt || marked.defaults).silent) {
17890           return '<p>An error occured:</p><pre>'
17891             + escape(e.message + '', true)
17892             + '</pre>';
17893         }
17894         throw e;
17895       }
17896     }
17897     
17898     /**
17899      * Options
17900      */
17901     
17902     marked.options =
17903     marked.setOptions = function(opt) {
17904       merge(marked.defaults, opt);
17905       return marked;
17906     };
17907     
17908     marked.defaults = {
17909       gfm: true,
17910       tables: true,
17911       breaks: false,
17912       pedantic: false,
17913       sanitize: false,
17914       sanitizer: null,
17915       mangle: true,
17916       smartLists: false,
17917       silent: false,
17918       highlight: null,
17919       langPrefix: 'lang-',
17920       smartypants: false,
17921       headerPrefix: '',
17922       renderer: new Renderer,
17923       xhtml: false
17924     };
17925     
17926     /**
17927      * Expose
17928      */
17929     
17930     marked.Parser = Parser;
17931     marked.parser = Parser.parse;
17932     
17933     marked.Renderer = Renderer;
17934     
17935     marked.Lexer = Lexer;
17936     marked.lexer = Lexer.lex;
17937     
17938     marked.InlineLexer = InlineLexer;
17939     marked.inlineLexer = InlineLexer.output;
17940     
17941     marked.parse = marked;
17942     
17943     Roo.Markdown.marked = marked;
17944
17945 })();/*
17946  * Based on:
17947  * Ext JS Library 1.1.1
17948  * Copyright(c) 2006-2007, Ext JS, LLC.
17949  *
17950  * Originally Released Under LGPL - original licence link has changed is not relivant.
17951  *
17952  * Fork - LGPL
17953  * <script type="text/javascript">
17954  */
17955
17956
17957
17958 /*
17959  * These classes are derivatives of the similarly named classes in the YUI Library.
17960  * The original license:
17961  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17962  * Code licensed under the BSD License:
17963  * http://developer.yahoo.net/yui/license.txt
17964  */
17965
17966 (function() {
17967
17968 var Event=Roo.EventManager;
17969 var Dom=Roo.lib.Dom;
17970
17971 /**
17972  * @class Roo.dd.DragDrop
17973  * @extends Roo.util.Observable
17974  * Defines the interface and base operation of items that that can be
17975  * dragged or can be drop targets.  It was designed to be extended, overriding
17976  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17977  * Up to three html elements can be associated with a DragDrop instance:
17978  * <ul>
17979  * <li>linked element: the element that is passed into the constructor.
17980  * This is the element which defines the boundaries for interaction with
17981  * other DragDrop objects.</li>
17982  * <li>handle element(s): The drag operation only occurs if the element that
17983  * was clicked matches a handle element.  By default this is the linked
17984  * element, but there are times that you will want only a portion of the
17985  * linked element to initiate the drag operation, and the setHandleElId()
17986  * method provides a way to define this.</li>
17987  * <li>drag element: this represents the element that would be moved along
17988  * with the cursor during a drag operation.  By default, this is the linked
17989  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17990  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17991  * </li>
17992  * </ul>
17993  * This class should not be instantiated until the onload event to ensure that
17994  * the associated elements are available.
17995  * The following would define a DragDrop obj that would interact with any
17996  * other DragDrop obj in the "group1" group:
17997  * <pre>
17998  *  dd = new Roo.dd.DragDrop("div1", "group1");
17999  * </pre>
18000  * Since none of the event handlers have been implemented, nothing would
18001  * actually happen if you were to run the code above.  Normally you would
18002  * override this class or one of the default implementations, but you can
18003  * also override the methods you want on an instance of the class...
18004  * <pre>
18005  *  dd.onDragDrop = function(e, id) {
18006  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18007  *  }
18008  * </pre>
18009  * @constructor
18010  * @param {String} id of the element that is linked to this instance
18011  * @param {String} sGroup the group of related DragDrop objects
18012  * @param {object} config an object containing configurable attributes
18013  *                Valid properties for DragDrop:
18014  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18015  */
18016 Roo.dd.DragDrop = function(id, sGroup, config) {
18017     if (id) {
18018         this.init(id, sGroup, config);
18019     }
18020     
18021 };
18022
18023 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18024
18025     /**
18026      * The id of the element associated with this object.  This is what we
18027      * refer to as the "linked element" because the size and position of
18028      * this element is used to determine when the drag and drop objects have
18029      * interacted.
18030      * @property id
18031      * @type String
18032      */
18033     id: null,
18034
18035     /**
18036      * Configuration attributes passed into the constructor
18037      * @property config
18038      * @type object
18039      */
18040     config: null,
18041
18042     /**
18043      * The id of the element that will be dragged.  By default this is same
18044      * as the linked element , but could be changed to another element. Ex:
18045      * Roo.dd.DDProxy
18046      * @property dragElId
18047      * @type String
18048      * @private
18049      */
18050     dragElId: null,
18051
18052     /**
18053      * the id of the element that initiates the drag operation.  By default
18054      * this is the linked element, but could be changed to be a child of this
18055      * element.  This lets us do things like only starting the drag when the
18056      * header element within the linked html element is clicked.
18057      * @property handleElId
18058      * @type String
18059      * @private
18060      */
18061     handleElId: null,
18062
18063     /**
18064      * An associative array of HTML tags that will be ignored if clicked.
18065      * @property invalidHandleTypes
18066      * @type {string: string}
18067      */
18068     invalidHandleTypes: null,
18069
18070     /**
18071      * An associative array of ids for elements that will be ignored if clicked
18072      * @property invalidHandleIds
18073      * @type {string: string}
18074      */
18075     invalidHandleIds: null,
18076
18077     /**
18078      * An indexted array of css class names for elements that will be ignored
18079      * if clicked.
18080      * @property invalidHandleClasses
18081      * @type string[]
18082      */
18083     invalidHandleClasses: null,
18084
18085     /**
18086      * The linked element's absolute X position at the time the drag was
18087      * started
18088      * @property startPageX
18089      * @type int
18090      * @private
18091      */
18092     startPageX: 0,
18093
18094     /**
18095      * The linked element's absolute X position at the time the drag was
18096      * started
18097      * @property startPageY
18098      * @type int
18099      * @private
18100      */
18101     startPageY: 0,
18102
18103     /**
18104      * The group defines a logical collection of DragDrop objects that are
18105      * related.  Instances only get events when interacting with other
18106      * DragDrop object in the same group.  This lets us define multiple
18107      * groups using a single DragDrop subclass if we want.
18108      * @property groups
18109      * @type {string: string}
18110      */
18111     groups: null,
18112
18113     /**
18114      * Individual drag/drop instances can be locked.  This will prevent
18115      * onmousedown start drag.
18116      * @property locked
18117      * @type boolean
18118      * @private
18119      */
18120     locked: false,
18121
18122     /**
18123      * Lock this instance
18124      * @method lock
18125      */
18126     lock: function() { this.locked = true; },
18127
18128     /**
18129      * Unlock this instace
18130      * @method unlock
18131      */
18132     unlock: function() { this.locked = false; },
18133
18134     /**
18135      * By default, all insances can be a drop target.  This can be disabled by
18136      * setting isTarget to false.
18137      * @method isTarget
18138      * @type boolean
18139      */
18140     isTarget: true,
18141
18142     /**
18143      * The padding configured for this drag and drop object for calculating
18144      * the drop zone intersection with this object.
18145      * @method padding
18146      * @type int[]
18147      */
18148     padding: null,
18149
18150     /**
18151      * Cached reference to the linked element
18152      * @property _domRef
18153      * @private
18154      */
18155     _domRef: null,
18156
18157     /**
18158      * Internal typeof flag
18159      * @property __ygDragDrop
18160      * @private
18161      */
18162     __ygDragDrop: true,
18163
18164     /**
18165      * Set to true when horizontal contraints are applied
18166      * @property constrainX
18167      * @type boolean
18168      * @private
18169      */
18170     constrainX: false,
18171
18172     /**
18173      * Set to true when vertical contraints are applied
18174      * @property constrainY
18175      * @type boolean
18176      * @private
18177      */
18178     constrainY: false,
18179
18180     /**
18181      * The left constraint
18182      * @property minX
18183      * @type int
18184      * @private
18185      */
18186     minX: 0,
18187
18188     /**
18189      * The right constraint
18190      * @property maxX
18191      * @type int
18192      * @private
18193      */
18194     maxX: 0,
18195
18196     /**
18197      * The up constraint
18198      * @property minY
18199      * @type int
18200      * @type int
18201      * @private
18202      */
18203     minY: 0,
18204
18205     /**
18206      * The down constraint
18207      * @property maxY
18208      * @type int
18209      * @private
18210      */
18211     maxY: 0,
18212
18213     /**
18214      * Maintain offsets when we resetconstraints.  Set to true when you want
18215      * the position of the element relative to its parent to stay the same
18216      * when the page changes
18217      *
18218      * @property maintainOffset
18219      * @type boolean
18220      */
18221     maintainOffset: false,
18222
18223     /**
18224      * Array of pixel locations the element will snap to if we specified a
18225      * horizontal graduation/interval.  This array is generated automatically
18226      * when you define a tick interval.
18227      * @property xTicks
18228      * @type int[]
18229      */
18230     xTicks: null,
18231
18232     /**
18233      * Array of pixel locations the element will snap to if we specified a
18234      * vertical graduation/interval.  This array is generated automatically
18235      * when you define a tick interval.
18236      * @property yTicks
18237      * @type int[]
18238      */
18239     yTicks: null,
18240
18241     /**
18242      * By default the drag and drop instance will only respond to the primary
18243      * button click (left button for a right-handed mouse).  Set to true to
18244      * allow drag and drop to start with any mouse click that is propogated
18245      * by the browser
18246      * @property primaryButtonOnly
18247      * @type boolean
18248      */
18249     primaryButtonOnly: true,
18250
18251     /**
18252      * The availabe property is false until the linked dom element is accessible.
18253      * @property available
18254      * @type boolean
18255      */
18256     available: false,
18257
18258     /**
18259      * By default, drags can only be initiated if the mousedown occurs in the
18260      * region the linked element is.  This is done in part to work around a
18261      * bug in some browsers that mis-report the mousedown if the previous
18262      * mouseup happened outside of the window.  This property is set to true
18263      * if outer handles are defined.
18264      *
18265      * @property hasOuterHandles
18266      * @type boolean
18267      * @default false
18268      */
18269     hasOuterHandles: false,
18270
18271     /**
18272      * Code that executes immediately before the startDrag event
18273      * @method b4StartDrag
18274      * @private
18275      */
18276     b4StartDrag: function(x, y) { },
18277
18278     /**
18279      * Abstract method called after a drag/drop object is clicked
18280      * and the drag or mousedown time thresholds have beeen met.
18281      * @method startDrag
18282      * @param {int} X click location
18283      * @param {int} Y click location
18284      */
18285     startDrag: function(x, y) { /* override this */ },
18286
18287     /**
18288      * Code that executes immediately before the onDrag event
18289      * @method b4Drag
18290      * @private
18291      */
18292     b4Drag: function(e) { },
18293
18294     /**
18295      * Abstract method called during the onMouseMove event while dragging an
18296      * object.
18297      * @method onDrag
18298      * @param {Event} e the mousemove event
18299      */
18300     onDrag: function(e) { /* override this */ },
18301
18302     /**
18303      * Abstract method called when this element fist begins hovering over
18304      * another DragDrop obj
18305      * @method onDragEnter
18306      * @param {Event} e the mousemove event
18307      * @param {String|DragDrop[]} id In POINT mode, the element
18308      * id this is hovering over.  In INTERSECT mode, an array of one or more
18309      * dragdrop items being hovered over.
18310      */
18311     onDragEnter: function(e, id) { /* override this */ },
18312
18313     /**
18314      * Code that executes immediately before the onDragOver event
18315      * @method b4DragOver
18316      * @private
18317      */
18318     b4DragOver: function(e) { },
18319
18320     /**
18321      * Abstract method called when this element is hovering over another
18322      * DragDrop obj
18323      * @method onDragOver
18324      * @param {Event} e the mousemove event
18325      * @param {String|DragDrop[]} id In POINT mode, the element
18326      * id this is hovering over.  In INTERSECT mode, an array of dd items
18327      * being hovered over.
18328      */
18329     onDragOver: function(e, id) { /* override this */ },
18330
18331     /**
18332      * Code that executes immediately before the onDragOut event
18333      * @method b4DragOut
18334      * @private
18335      */
18336     b4DragOut: function(e) { },
18337
18338     /**
18339      * Abstract method called when we are no longer hovering over an element
18340      * @method onDragOut
18341      * @param {Event} e the mousemove event
18342      * @param {String|DragDrop[]} id In POINT mode, the element
18343      * id this was hovering over.  In INTERSECT mode, an array of dd items
18344      * that the mouse is no longer over.
18345      */
18346     onDragOut: function(e, id) { /* override this */ },
18347
18348     /**
18349      * Code that executes immediately before the onDragDrop event
18350      * @method b4DragDrop
18351      * @private
18352      */
18353     b4DragDrop: function(e) { },
18354
18355     /**
18356      * Abstract method called when this item is dropped on another DragDrop
18357      * obj
18358      * @method onDragDrop
18359      * @param {Event} e the mouseup event
18360      * @param {String|DragDrop[]} id In POINT mode, the element
18361      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18362      * was dropped on.
18363      */
18364     onDragDrop: function(e, id) { /* override this */ },
18365
18366     /**
18367      * Abstract method called when this item is dropped on an area with no
18368      * drop target
18369      * @method onInvalidDrop
18370      * @param {Event} e the mouseup event
18371      */
18372     onInvalidDrop: function(e) { /* override this */ },
18373
18374     /**
18375      * Code that executes immediately before the endDrag event
18376      * @method b4EndDrag
18377      * @private
18378      */
18379     b4EndDrag: function(e) { },
18380
18381     /**
18382      * Fired when we are done dragging the object
18383      * @method endDrag
18384      * @param {Event} e the mouseup event
18385      */
18386     endDrag: function(e) { /* override this */ },
18387
18388     /**
18389      * Code executed immediately before the onMouseDown event
18390      * @method b4MouseDown
18391      * @param {Event} e the mousedown event
18392      * @private
18393      */
18394     b4MouseDown: function(e) {  },
18395
18396     /**
18397      * Event handler that fires when a drag/drop obj gets a mousedown
18398      * @method onMouseDown
18399      * @param {Event} e the mousedown event
18400      */
18401     onMouseDown: function(e) { /* override this */ },
18402
18403     /**
18404      * Event handler that fires when a drag/drop obj gets a mouseup
18405      * @method onMouseUp
18406      * @param {Event} e the mouseup event
18407      */
18408     onMouseUp: function(e) { /* override this */ },
18409
18410     /**
18411      * Override the onAvailable method to do what is needed after the initial
18412      * position was determined.
18413      * @method onAvailable
18414      */
18415     onAvailable: function () {
18416     },
18417
18418     /*
18419      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18420      * @type Object
18421      */
18422     defaultPadding : {left:0, right:0, top:0, bottom:0},
18423
18424     /*
18425      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18426  *
18427  * Usage:
18428  <pre><code>
18429  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18430                 { dragElId: "existingProxyDiv" });
18431  dd.startDrag = function(){
18432      this.constrainTo("parent-id");
18433  };
18434  </code></pre>
18435  * Or you can initalize it using the {@link Roo.Element} object:
18436  <pre><code>
18437  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18438      startDrag : function(){
18439          this.constrainTo("parent-id");
18440      }
18441  });
18442  </code></pre>
18443      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18444      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18445      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18446      * an object containing the sides to pad. For example: {right:10, bottom:10}
18447      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18448      */
18449     constrainTo : function(constrainTo, pad, inContent){
18450         if(typeof pad == "number"){
18451             pad = {left: pad, right:pad, top:pad, bottom:pad};
18452         }
18453         pad = pad || this.defaultPadding;
18454         var b = Roo.get(this.getEl()).getBox();
18455         var ce = Roo.get(constrainTo);
18456         var s = ce.getScroll();
18457         var c, cd = ce.dom;
18458         if(cd == document.body){
18459             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18460         }else{
18461             xy = ce.getXY();
18462             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18463         }
18464
18465
18466         var topSpace = b.y - c.y;
18467         var leftSpace = b.x - c.x;
18468
18469         this.resetConstraints();
18470         this.setXConstraint(leftSpace - (pad.left||0), // left
18471                 c.width - leftSpace - b.width - (pad.right||0) //right
18472         );
18473         this.setYConstraint(topSpace - (pad.top||0), //top
18474                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18475         );
18476     },
18477
18478     /**
18479      * Returns a reference to the linked element
18480      * @method getEl
18481      * @return {HTMLElement} the html element
18482      */
18483     getEl: function() {
18484         if (!this._domRef) {
18485             this._domRef = Roo.getDom(this.id);
18486         }
18487
18488         return this._domRef;
18489     },
18490
18491     /**
18492      * Returns a reference to the actual element to drag.  By default this is
18493      * the same as the html element, but it can be assigned to another
18494      * element. An example of this can be found in Roo.dd.DDProxy
18495      * @method getDragEl
18496      * @return {HTMLElement} the html element
18497      */
18498     getDragEl: function() {
18499         return Roo.getDom(this.dragElId);
18500     },
18501
18502     /**
18503      * Sets up the DragDrop object.  Must be called in the constructor of any
18504      * Roo.dd.DragDrop subclass
18505      * @method init
18506      * @param id the id of the linked element
18507      * @param {String} sGroup the group of related items
18508      * @param {object} config configuration attributes
18509      */
18510     init: function(id, sGroup, config) {
18511         this.initTarget(id, sGroup, config);
18512         if (!Roo.isTouch) {
18513             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18514         }
18515         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18516         // Event.on(this.id, "selectstart", Event.preventDefault);
18517     },
18518
18519     /**
18520      * Initializes Targeting functionality only... the object does not
18521      * get a mousedown handler.
18522      * @method initTarget
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     initTarget: function(id, sGroup, config) {
18528
18529         // configuration attributes
18530         this.config = config || {};
18531
18532         // create a local reference to the drag and drop manager
18533         this.DDM = Roo.dd.DDM;
18534         // initialize the groups array
18535         this.groups = {};
18536
18537         // assume that we have an element reference instead of an id if the
18538         // parameter is not a string
18539         if (typeof id !== "string") {
18540             id = Roo.id(id);
18541         }
18542
18543         // set the id
18544         this.id = id;
18545
18546         // add to an interaction group
18547         this.addToGroup((sGroup) ? sGroup : "default");
18548
18549         // We don't want to register this as the handle with the manager
18550         // so we just set the id rather than calling the setter.
18551         this.handleElId = id;
18552
18553         // the linked element is the element that gets dragged by default
18554         this.setDragElId(id);
18555
18556         // by default, clicked anchors will not start drag operations.
18557         this.invalidHandleTypes = { A: "A" };
18558         this.invalidHandleIds = {};
18559         this.invalidHandleClasses = [];
18560
18561         this.applyConfig();
18562
18563         this.handleOnAvailable();
18564     },
18565
18566     /**
18567      * Applies the configuration parameters that were passed into the constructor.
18568      * This is supposed to happen at each level through the inheritance chain.  So
18569      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18570      * DragDrop in order to get all of the parameters that are available in
18571      * each object.
18572      * @method applyConfig
18573      */
18574     applyConfig: function() {
18575
18576         // configurable properties:
18577         //    padding, isTarget, maintainOffset, primaryButtonOnly
18578         this.padding           = this.config.padding || [0, 0, 0, 0];
18579         this.isTarget          = (this.config.isTarget !== false);
18580         this.maintainOffset    = (this.config.maintainOffset);
18581         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18582
18583     },
18584
18585     /**
18586      * Executed when the linked element is available
18587      * @method handleOnAvailable
18588      * @private
18589      */
18590     handleOnAvailable: function() {
18591         this.available = true;
18592         this.resetConstraints();
18593         this.onAvailable();
18594     },
18595
18596      /**
18597      * Configures the padding for the target zone in px.  Effectively expands
18598      * (or reduces) the virtual object size for targeting calculations.
18599      * Supports css-style shorthand; if only one parameter is passed, all sides
18600      * will have that padding, and if only two are passed, the top and bottom
18601      * will have the first param, the left and right the second.
18602      * @method setPadding
18603      * @param {int} iTop    Top pad
18604      * @param {int} iRight  Right pad
18605      * @param {int} iBot    Bot pad
18606      * @param {int} iLeft   Left pad
18607      */
18608     setPadding: function(iTop, iRight, iBot, iLeft) {
18609         // this.padding = [iLeft, iRight, iTop, iBot];
18610         if (!iRight && 0 !== iRight) {
18611             this.padding = [iTop, iTop, iTop, iTop];
18612         } else if (!iBot && 0 !== iBot) {
18613             this.padding = [iTop, iRight, iTop, iRight];
18614         } else {
18615             this.padding = [iTop, iRight, iBot, iLeft];
18616         }
18617     },
18618
18619     /**
18620      * Stores the initial placement of the linked element.
18621      * @method setInitialPosition
18622      * @param {int} diffX   the X offset, default 0
18623      * @param {int} diffY   the Y offset, default 0
18624      */
18625     setInitPosition: function(diffX, diffY) {
18626         var el = this.getEl();
18627
18628         if (!this.DDM.verifyEl(el)) {
18629             return;
18630         }
18631
18632         var dx = diffX || 0;
18633         var dy = diffY || 0;
18634
18635         var p = Dom.getXY( el );
18636
18637         this.initPageX = p[0] - dx;
18638         this.initPageY = p[1] - dy;
18639
18640         this.lastPageX = p[0];
18641         this.lastPageY = p[1];
18642
18643
18644         this.setStartPosition(p);
18645     },
18646
18647     /**
18648      * Sets the start position of the element.  This is set when the obj
18649      * is initialized, the reset when a drag is started.
18650      * @method setStartPosition
18651      * @param pos current position (from previous lookup)
18652      * @private
18653      */
18654     setStartPosition: function(pos) {
18655         var p = pos || Dom.getXY( this.getEl() );
18656         this.deltaSetXY = null;
18657
18658         this.startPageX = p[0];
18659         this.startPageY = p[1];
18660     },
18661
18662     /**
18663      * Add this instance to a group of related drag/drop objects.  All
18664      * instances belong to at least one group, and can belong to as many
18665      * groups as needed.
18666      * @method addToGroup
18667      * @param sGroup {string} the name of the group
18668      */
18669     addToGroup: function(sGroup) {
18670         this.groups[sGroup] = true;
18671         this.DDM.regDragDrop(this, sGroup);
18672     },
18673
18674     /**
18675      * Remove's this instance from the supplied interaction group
18676      * @method removeFromGroup
18677      * @param {string}  sGroup  The group to drop
18678      */
18679     removeFromGroup: function(sGroup) {
18680         if (this.groups[sGroup]) {
18681             delete this.groups[sGroup];
18682         }
18683
18684         this.DDM.removeDDFromGroup(this, sGroup);
18685     },
18686
18687     /**
18688      * Allows you to specify that an element other than the linked element
18689      * will be moved with the cursor during a drag
18690      * @method setDragElId
18691      * @param id {string} the id of the element that will be used to initiate the drag
18692      */
18693     setDragElId: function(id) {
18694         this.dragElId = id;
18695     },
18696
18697     /**
18698      * Allows you to specify a child of the linked element that should be
18699      * used to initiate the drag operation.  An example of this would be if
18700      * you have a content div with text and links.  Clicking anywhere in the
18701      * content area would normally start the drag operation.  Use this method
18702      * to specify that an element inside of the content div is the element
18703      * that starts the drag operation.
18704      * @method setHandleElId
18705      * @param id {string} the id of the element that will be used to
18706      * initiate the drag.
18707      */
18708     setHandleElId: function(id) {
18709         if (typeof id !== "string") {
18710             id = Roo.id(id);
18711         }
18712         this.handleElId = id;
18713         this.DDM.regHandle(this.id, id);
18714     },
18715
18716     /**
18717      * Allows you to set an element outside of the linked element as a drag
18718      * handle
18719      * @method setOuterHandleElId
18720      * @param id the id of the element that will be used to initiate the drag
18721      */
18722     setOuterHandleElId: function(id) {
18723         if (typeof id !== "string") {
18724             id = Roo.id(id);
18725         }
18726         Event.on(id, "mousedown",
18727                 this.handleMouseDown, this);
18728         this.setHandleElId(id);
18729
18730         this.hasOuterHandles = true;
18731     },
18732
18733     /**
18734      * Remove all drag and drop hooks for this element
18735      * @method unreg
18736      */
18737     unreg: function() {
18738         Event.un(this.id, "mousedown",
18739                 this.handleMouseDown);
18740         Event.un(this.id, "touchstart",
18741                 this.handleMouseDown);
18742         this._domRef = null;
18743         this.DDM._remove(this);
18744     },
18745
18746     destroy : function(){
18747         this.unreg();
18748     },
18749
18750     /**
18751      * Returns true if this instance is locked, or the drag drop mgr is locked
18752      * (meaning that all drag/drop is disabled on the page.)
18753      * @method isLocked
18754      * @return {boolean} true if this obj or all drag/drop is locked, else
18755      * false
18756      */
18757     isLocked: function() {
18758         return (this.DDM.isLocked() || this.locked);
18759     },
18760
18761     /**
18762      * Fired when this object is clicked
18763      * @method handleMouseDown
18764      * @param {Event} e
18765      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18766      * @private
18767      */
18768     handleMouseDown: function(e, oDD){
18769      
18770         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18771             //Roo.log('not touch/ button !=0');
18772             return;
18773         }
18774         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18775             return; // double touch..
18776         }
18777         
18778
18779         if (this.isLocked()) {
18780             //Roo.log('locked');
18781             return;
18782         }
18783
18784         this.DDM.refreshCache(this.groups);
18785 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18786         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18787         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18788             //Roo.log('no outer handes or not over target');
18789                 // do nothing.
18790         } else {
18791 //            Roo.log('check validator');
18792             if (this.clickValidator(e)) {
18793 //                Roo.log('validate success');
18794                 // set the initial element position
18795                 this.setStartPosition();
18796
18797
18798                 this.b4MouseDown(e);
18799                 this.onMouseDown(e);
18800
18801                 this.DDM.handleMouseDown(e, this);
18802
18803                 this.DDM.stopEvent(e);
18804             } else {
18805
18806
18807             }
18808         }
18809     },
18810
18811     clickValidator: function(e) {
18812         var target = e.getTarget();
18813         return ( this.isValidHandleChild(target) &&
18814                     (this.id == this.handleElId ||
18815                         this.DDM.handleWasClicked(target, this.id)) );
18816     },
18817
18818     /**
18819      * Allows you to specify a tag name that should not start a drag operation
18820      * when clicked.  This is designed to facilitate embedding links within a
18821      * drag handle that do something other than start the drag.
18822      * @method addInvalidHandleType
18823      * @param {string} tagName the type of element to exclude
18824      */
18825     addInvalidHandleType: function(tagName) {
18826         var type = tagName.toUpperCase();
18827         this.invalidHandleTypes[type] = type;
18828     },
18829
18830     /**
18831      * Lets you to specify an element id for a child of a drag handle
18832      * that should not initiate a drag
18833      * @method addInvalidHandleId
18834      * @param {string} id the element id of the element you wish to ignore
18835      */
18836     addInvalidHandleId: function(id) {
18837         if (typeof id !== "string") {
18838             id = Roo.id(id);
18839         }
18840         this.invalidHandleIds[id] = id;
18841     },
18842
18843     /**
18844      * Lets you specify a css class of elements that will not initiate a drag
18845      * @method addInvalidHandleClass
18846      * @param {string} cssClass the class of the elements you wish to ignore
18847      */
18848     addInvalidHandleClass: function(cssClass) {
18849         this.invalidHandleClasses.push(cssClass);
18850     },
18851
18852     /**
18853      * Unsets an excluded tag name set by addInvalidHandleType
18854      * @method removeInvalidHandleType
18855      * @param {string} tagName the type of element to unexclude
18856      */
18857     removeInvalidHandleType: function(tagName) {
18858         var type = tagName.toUpperCase();
18859         // this.invalidHandleTypes[type] = null;
18860         delete this.invalidHandleTypes[type];
18861     },
18862
18863     /**
18864      * Unsets an invalid handle id
18865      * @method removeInvalidHandleId
18866      * @param {string} id the id of the element to re-enable
18867      */
18868     removeInvalidHandleId: function(id) {
18869         if (typeof id !== "string") {
18870             id = Roo.id(id);
18871         }
18872         delete this.invalidHandleIds[id];
18873     },
18874
18875     /**
18876      * Unsets an invalid css class
18877      * @method removeInvalidHandleClass
18878      * @param {string} cssClass the class of the element(s) you wish to
18879      * re-enable
18880      */
18881     removeInvalidHandleClass: function(cssClass) {
18882         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18883             if (this.invalidHandleClasses[i] == cssClass) {
18884                 delete this.invalidHandleClasses[i];
18885             }
18886         }
18887     },
18888
18889     /**
18890      * Checks the tag exclusion list to see if this click should be ignored
18891      * @method isValidHandleChild
18892      * @param {HTMLElement} node the HTMLElement to evaluate
18893      * @return {boolean} true if this is a valid tag type, false if not
18894      */
18895     isValidHandleChild: function(node) {
18896
18897         var valid = true;
18898         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18899         var nodeName;
18900         try {
18901             nodeName = node.nodeName.toUpperCase();
18902         } catch(e) {
18903             nodeName = node.nodeName;
18904         }
18905         valid = valid && !this.invalidHandleTypes[nodeName];
18906         valid = valid && !this.invalidHandleIds[node.id];
18907
18908         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18909             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18910         }
18911
18912
18913         return valid;
18914
18915     },
18916
18917     /**
18918      * Create the array of horizontal tick marks if an interval was specified
18919      * in setXConstraint().
18920      * @method setXTicks
18921      * @private
18922      */
18923     setXTicks: function(iStartX, iTickSize) {
18924         this.xTicks = [];
18925         this.xTickSize = iTickSize;
18926
18927         var tickMap = {};
18928
18929         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18930             if (!tickMap[i]) {
18931                 this.xTicks[this.xTicks.length] = i;
18932                 tickMap[i] = true;
18933             }
18934         }
18935
18936         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18937             if (!tickMap[i]) {
18938                 this.xTicks[this.xTicks.length] = i;
18939                 tickMap[i] = true;
18940             }
18941         }
18942
18943         this.xTicks.sort(this.DDM.numericSort) ;
18944     },
18945
18946     /**
18947      * Create the array of vertical tick marks if an interval was specified in
18948      * setYConstraint().
18949      * @method setYTicks
18950      * @private
18951      */
18952     setYTicks: function(iStartY, iTickSize) {
18953         this.yTicks = [];
18954         this.yTickSize = iTickSize;
18955
18956         var tickMap = {};
18957
18958         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18959             if (!tickMap[i]) {
18960                 this.yTicks[this.yTicks.length] = i;
18961                 tickMap[i] = true;
18962             }
18963         }
18964
18965         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18966             if (!tickMap[i]) {
18967                 this.yTicks[this.yTicks.length] = i;
18968                 tickMap[i] = true;
18969             }
18970         }
18971
18972         this.yTicks.sort(this.DDM.numericSort) ;
18973     },
18974
18975     /**
18976      * By default, the element can be dragged any place on the screen.  Use
18977      * this method to limit the horizontal travel of the element.  Pass in
18978      * 0,0 for the parameters if you want to lock the drag to the y axis.
18979      * @method setXConstraint
18980      * @param {int} iLeft the number of pixels the element can move to the left
18981      * @param {int} iRight the number of pixels the element can move to the
18982      * right
18983      * @param {int} iTickSize optional parameter for specifying that the
18984      * element
18985      * should move iTickSize pixels at a time.
18986      */
18987     setXConstraint: function(iLeft, iRight, iTickSize) {
18988         this.leftConstraint = iLeft;
18989         this.rightConstraint = iRight;
18990
18991         this.minX = this.initPageX - iLeft;
18992         this.maxX = this.initPageX + iRight;
18993         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18994
18995         this.constrainX = true;
18996     },
18997
18998     /**
18999      * Clears any constraints applied to this instance.  Also clears ticks
19000      * since they can't exist independent of a constraint at this time.
19001      * @method clearConstraints
19002      */
19003     clearConstraints: function() {
19004         this.constrainX = false;
19005         this.constrainY = false;
19006         this.clearTicks();
19007     },
19008
19009     /**
19010      * Clears any tick interval defined for this instance
19011      * @method clearTicks
19012      */
19013     clearTicks: function() {
19014         this.xTicks = null;
19015         this.yTicks = null;
19016         this.xTickSize = 0;
19017         this.yTickSize = 0;
19018     },
19019
19020     /**
19021      * By default, the element can be dragged any place on the screen.  Set
19022      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19023      * parameters if you want to lock the drag to the x axis.
19024      * @method setYConstraint
19025      * @param {int} iUp the number of pixels the element can move up
19026      * @param {int} iDown the number of pixels the element can move down
19027      * @param {int} iTickSize optional parameter for specifying that the
19028      * element should move iTickSize pixels at a time.
19029      */
19030     setYConstraint: function(iUp, iDown, iTickSize) {
19031         this.topConstraint = iUp;
19032         this.bottomConstraint = iDown;
19033
19034         this.minY = this.initPageY - iUp;
19035         this.maxY = this.initPageY + iDown;
19036         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19037
19038         this.constrainY = true;
19039
19040     },
19041
19042     /**
19043      * resetConstraints must be called if you manually reposition a dd element.
19044      * @method resetConstraints
19045      * @param {boolean} maintainOffset
19046      */
19047     resetConstraints: function() {
19048
19049
19050         // Maintain offsets if necessary
19051         if (this.initPageX || this.initPageX === 0) {
19052             // figure out how much this thing has moved
19053             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19054             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19055
19056             this.setInitPosition(dx, dy);
19057
19058         // This is the first time we have detected the element's position
19059         } else {
19060             this.setInitPosition();
19061         }
19062
19063         if (this.constrainX) {
19064             this.setXConstraint( this.leftConstraint,
19065                                  this.rightConstraint,
19066                                  this.xTickSize        );
19067         }
19068
19069         if (this.constrainY) {
19070             this.setYConstraint( this.topConstraint,
19071                                  this.bottomConstraint,
19072                                  this.yTickSize         );
19073         }
19074     },
19075
19076     /**
19077      * Normally the drag element is moved pixel by pixel, but we can specify
19078      * that it move a number of pixels at a time.  This method resolves the
19079      * location when we have it set up like this.
19080      * @method getTick
19081      * @param {int} val where we want to place the object
19082      * @param {int[]} tickArray sorted array of valid points
19083      * @return {int} the closest tick
19084      * @private
19085      */
19086     getTick: function(val, tickArray) {
19087
19088         if (!tickArray) {
19089             // If tick interval is not defined, it is effectively 1 pixel,
19090             // so we return the value passed to us.
19091             return val;
19092         } else if (tickArray[0] >= val) {
19093             // The value is lower than the first tick, so we return the first
19094             // tick.
19095             return tickArray[0];
19096         } else {
19097             for (var i=0, len=tickArray.length; i<len; ++i) {
19098                 var next = i + 1;
19099                 if (tickArray[next] && tickArray[next] >= val) {
19100                     var diff1 = val - tickArray[i];
19101                     var diff2 = tickArray[next] - val;
19102                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19103                 }
19104             }
19105
19106             // The value is larger than the last tick, so we return the last
19107             // tick.
19108             return tickArray[tickArray.length - 1];
19109         }
19110     },
19111
19112     /**
19113      * toString method
19114      * @method toString
19115      * @return {string} string representation of the dd obj
19116      */
19117     toString: function() {
19118         return ("DragDrop " + this.id);
19119     }
19120
19121 });
19122
19123 })();
19124 /*
19125  * Based on:
19126  * Ext JS Library 1.1.1
19127  * Copyright(c) 2006-2007, Ext JS, LLC.
19128  *
19129  * Originally Released Under LGPL - original licence link has changed is not relivant.
19130  *
19131  * Fork - LGPL
19132  * <script type="text/javascript">
19133  */
19134
19135
19136 /**
19137  * The drag and drop utility provides a framework for building drag and drop
19138  * applications.  In addition to enabling drag and drop for specific elements,
19139  * the drag and drop elements are tracked by the manager class, and the
19140  * interactions between the various elements are tracked during the drag and
19141  * the implementing code is notified about these important moments.
19142  */
19143
19144 // Only load the library once.  Rewriting the manager class would orphan
19145 // existing drag and drop instances.
19146 if (!Roo.dd.DragDropMgr) {
19147
19148 /**
19149  * @class Roo.dd.DragDropMgr
19150  * DragDropMgr is a singleton that tracks the element interaction for
19151  * all DragDrop items in the window.  Generally, you will not call
19152  * this class directly, but it does have helper methods that could
19153  * be useful in your DragDrop implementations.
19154  * @singleton
19155  */
19156 Roo.dd.DragDropMgr = function() {
19157
19158     var Event = Roo.EventManager;
19159
19160     return {
19161
19162         /**
19163          * Two dimensional Array of registered DragDrop objects.  The first
19164          * dimension is the DragDrop item group, the second the DragDrop
19165          * object.
19166          * @property ids
19167          * @type {string: string}
19168          * @private
19169          * @static
19170          */
19171         ids: {},
19172
19173         /**
19174          * Array of element ids defined as drag handles.  Used to determine
19175          * if the element that generated the mousedown event is actually the
19176          * handle and not the html element itself.
19177          * @property handleIds
19178          * @type {string: string}
19179          * @private
19180          * @static
19181          */
19182         handleIds: {},
19183
19184         /**
19185          * the DragDrop object that is currently being dragged
19186          * @property dragCurrent
19187          * @type DragDrop
19188          * @private
19189          * @static
19190          **/
19191         dragCurrent: null,
19192
19193         /**
19194          * the DragDrop object(s) that are being hovered over
19195          * @property dragOvers
19196          * @type Array
19197          * @private
19198          * @static
19199          */
19200         dragOvers: {},
19201
19202         /**
19203          * the X distance between the cursor and the object being dragged
19204          * @property deltaX
19205          * @type int
19206          * @private
19207          * @static
19208          */
19209         deltaX: 0,
19210
19211         /**
19212          * the Y distance between the cursor and the object being dragged
19213          * @property deltaY
19214          * @type int
19215          * @private
19216          * @static
19217          */
19218         deltaY: 0,
19219
19220         /**
19221          * Flag to determine if we should prevent the default behavior of the
19222          * events we define. By default this is true, but this can be set to
19223          * false if you need the default behavior (not recommended)
19224          * @property preventDefault
19225          * @type boolean
19226          * @static
19227          */
19228         preventDefault: true,
19229
19230         /**
19231          * Flag to determine if we should stop the propagation of the events
19232          * we generate. This is true by default but you may want to set it to
19233          * false if the html element contains other features that require the
19234          * mouse click.
19235          * @property stopPropagation
19236          * @type boolean
19237          * @static
19238          */
19239         stopPropagation: true,
19240
19241         /**
19242          * Internal flag that is set to true when drag and drop has been
19243          * intialized
19244          * @property initialized
19245          * @private
19246          * @static
19247          */
19248         initalized: false,
19249
19250         /**
19251          * All drag and drop can be disabled.
19252          * @property locked
19253          * @private
19254          * @static
19255          */
19256         locked: false,
19257
19258         /**
19259          * Called the first time an element is registered.
19260          * @method init
19261          * @private
19262          * @static
19263          */
19264         init: function() {
19265             this.initialized = true;
19266         },
19267
19268         /**
19269          * In point mode, drag and drop interaction is defined by the
19270          * location of the cursor during the drag/drop
19271          * @property POINT
19272          * @type int
19273          * @static
19274          */
19275         POINT: 0,
19276
19277         /**
19278          * In intersect mode, drag and drop interactio nis defined by the
19279          * overlap of two or more drag and drop objects.
19280          * @property INTERSECT
19281          * @type int
19282          * @static
19283          */
19284         INTERSECT: 1,
19285
19286         /**
19287          * The current drag and drop mode.  Default: POINT
19288          * @property mode
19289          * @type int
19290          * @static
19291          */
19292         mode: 0,
19293
19294         /**
19295          * Runs method on all drag and drop objects
19296          * @method _execOnAll
19297          * @private
19298          * @static
19299          */
19300         _execOnAll: function(sMethod, args) {
19301             for (var i in this.ids) {
19302                 for (var j in this.ids[i]) {
19303                     var oDD = this.ids[i][j];
19304                     if (! this.isTypeOfDD(oDD)) {
19305                         continue;
19306                     }
19307                     oDD[sMethod].apply(oDD, args);
19308                 }
19309             }
19310         },
19311
19312         /**
19313          * Drag and drop initialization.  Sets up the global event handlers
19314          * @method _onLoad
19315          * @private
19316          * @static
19317          */
19318         _onLoad: function() {
19319
19320             this.init();
19321
19322             if (!Roo.isTouch) {
19323                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19324                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19325             }
19326             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19327             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19328             
19329             Event.on(window,   "unload",    this._onUnload, this, true);
19330             Event.on(window,   "resize",    this._onResize, this, true);
19331             // Event.on(window,   "mouseout",    this._test);
19332
19333         },
19334
19335         /**
19336          * Reset constraints on all drag and drop objs
19337          * @method _onResize
19338          * @private
19339          * @static
19340          */
19341         _onResize: function(e) {
19342             this._execOnAll("resetConstraints", []);
19343         },
19344
19345         /**
19346          * Lock all drag and drop functionality
19347          * @method lock
19348          * @static
19349          */
19350         lock: function() { this.locked = true; },
19351
19352         /**
19353          * Unlock all drag and drop functionality
19354          * @method unlock
19355          * @static
19356          */
19357         unlock: function() { this.locked = false; },
19358
19359         /**
19360          * Is drag and drop locked?
19361          * @method isLocked
19362          * @return {boolean} True if drag and drop is locked, false otherwise.
19363          * @static
19364          */
19365         isLocked: function() { return this.locked; },
19366
19367         /**
19368          * Location cache that is set for all drag drop objects when a drag is
19369          * initiated, cleared when the drag is finished.
19370          * @property locationCache
19371          * @private
19372          * @static
19373          */
19374         locationCache: {},
19375
19376         /**
19377          * Set useCache to false if you want to force object the lookup of each
19378          * drag and drop linked element constantly during a drag.
19379          * @property useCache
19380          * @type boolean
19381          * @static
19382          */
19383         useCache: true,
19384
19385         /**
19386          * The number of pixels that the mouse needs to move after the
19387          * mousedown before the drag is initiated.  Default=3;
19388          * @property clickPixelThresh
19389          * @type int
19390          * @static
19391          */
19392         clickPixelThresh: 3,
19393
19394         /**
19395          * The number of milliseconds after the mousedown event to initiate the
19396          * drag if we don't get a mouseup event. Default=1000
19397          * @property clickTimeThresh
19398          * @type int
19399          * @static
19400          */
19401         clickTimeThresh: 350,
19402
19403         /**
19404          * Flag that indicates that either the drag pixel threshold or the
19405          * mousdown time threshold has been met
19406          * @property dragThreshMet
19407          * @type boolean
19408          * @private
19409          * @static
19410          */
19411         dragThreshMet: false,
19412
19413         /**
19414          * Timeout used for the click time threshold
19415          * @property clickTimeout
19416          * @type Object
19417          * @private
19418          * @static
19419          */
19420         clickTimeout: null,
19421
19422         /**
19423          * The X position of the mousedown event stored for later use when a
19424          * drag threshold is met.
19425          * @property startX
19426          * @type int
19427          * @private
19428          * @static
19429          */
19430         startX: 0,
19431
19432         /**
19433          * The Y position of the mousedown event stored for later use when a
19434          * drag threshold is met.
19435          * @property startY
19436          * @type int
19437          * @private
19438          * @static
19439          */
19440         startY: 0,
19441
19442         /**
19443          * Each DragDrop instance must be registered with the DragDropMgr.
19444          * This is executed in DragDrop.init()
19445          * @method regDragDrop
19446          * @param {DragDrop} oDD the DragDrop object to register
19447          * @param {String} sGroup the name of the group this element belongs to
19448          * @static
19449          */
19450         regDragDrop: function(oDD, sGroup) {
19451             if (!this.initialized) { this.init(); }
19452
19453             if (!this.ids[sGroup]) {
19454                 this.ids[sGroup] = {};
19455             }
19456             this.ids[sGroup][oDD.id] = oDD;
19457         },
19458
19459         /**
19460          * Removes the supplied dd instance from the supplied group. Executed
19461          * by DragDrop.removeFromGroup, so don't call this function directly.
19462          * @method removeDDFromGroup
19463          * @private
19464          * @static
19465          */
19466         removeDDFromGroup: function(oDD, sGroup) {
19467             if (!this.ids[sGroup]) {
19468                 this.ids[sGroup] = {};
19469             }
19470
19471             var obj = this.ids[sGroup];
19472             if (obj && obj[oDD.id]) {
19473                 delete obj[oDD.id];
19474             }
19475         },
19476
19477         /**
19478          * Unregisters a drag and drop item.  This is executed in
19479          * DragDrop.unreg, use that method instead of calling this directly.
19480          * @method _remove
19481          * @private
19482          * @static
19483          */
19484         _remove: function(oDD) {
19485             for (var g in oDD.groups) {
19486                 if (g && this.ids[g][oDD.id]) {
19487                     delete this.ids[g][oDD.id];
19488                 }
19489             }
19490             delete this.handleIds[oDD.id];
19491         },
19492
19493         /**
19494          * Each DragDrop handle element must be registered.  This is done
19495          * automatically when executing DragDrop.setHandleElId()
19496          * @method regHandle
19497          * @param {String} sDDId the DragDrop id this element is a handle for
19498          * @param {String} sHandleId the id of the element that is the drag
19499          * handle
19500          * @static
19501          */
19502         regHandle: function(sDDId, sHandleId) {
19503             if (!this.handleIds[sDDId]) {
19504                 this.handleIds[sDDId] = {};
19505             }
19506             this.handleIds[sDDId][sHandleId] = sHandleId;
19507         },
19508
19509         /**
19510          * Utility function to determine if a given element has been
19511          * registered as a drag drop item.
19512          * @method isDragDrop
19513          * @param {String} id the element id to check
19514          * @return {boolean} true if this element is a DragDrop item,
19515          * false otherwise
19516          * @static
19517          */
19518         isDragDrop: function(id) {
19519             return ( this.getDDById(id) ) ? true : false;
19520         },
19521
19522         /**
19523          * Returns the drag and drop instances that are in all groups the
19524          * passed in instance belongs to.
19525          * @method getRelated
19526          * @param {DragDrop} p_oDD the obj to get related data for
19527          * @param {boolean} bTargetsOnly if true, only return targetable objs
19528          * @return {DragDrop[]} the related instances
19529          * @static
19530          */
19531         getRelated: function(p_oDD, bTargetsOnly) {
19532             var oDDs = [];
19533             for (var i in p_oDD.groups) {
19534                 for (j in this.ids[i]) {
19535                     var dd = this.ids[i][j];
19536                     if (! this.isTypeOfDD(dd)) {
19537                         continue;
19538                     }
19539                     if (!bTargetsOnly || dd.isTarget) {
19540                         oDDs[oDDs.length] = dd;
19541                     }
19542                 }
19543             }
19544
19545             return oDDs;
19546         },
19547
19548         /**
19549          * Returns true if the specified dd target is a legal target for
19550          * the specifice drag obj
19551          * @method isLegalTarget
19552          * @param {DragDrop} the drag obj
19553          * @param {DragDrop} the target
19554          * @return {boolean} true if the target is a legal target for the
19555          * dd obj
19556          * @static
19557          */
19558         isLegalTarget: function (oDD, oTargetDD) {
19559             var targets = this.getRelated(oDD, true);
19560             for (var i=0, len=targets.length;i<len;++i) {
19561                 if (targets[i].id == oTargetDD.id) {
19562                     return true;
19563                 }
19564             }
19565
19566             return false;
19567         },
19568
19569         /**
19570          * My goal is to be able to transparently determine if an object is
19571          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19572          * returns "object", oDD.constructor.toString() always returns
19573          * "DragDrop" and not the name of the subclass.  So for now it just
19574          * evaluates a well-known variable in DragDrop.
19575          * @method isTypeOfDD
19576          * @param {Object} the object to evaluate
19577          * @return {boolean} true if typeof oDD = DragDrop
19578          * @static
19579          */
19580         isTypeOfDD: function (oDD) {
19581             return (oDD && oDD.__ygDragDrop);
19582         },
19583
19584         /**
19585          * Utility function to determine if a given element has been
19586          * registered as a drag drop handle for the given Drag Drop object.
19587          * @method isHandle
19588          * @param {String} id the element id to check
19589          * @return {boolean} true if this element is a DragDrop handle, false
19590          * otherwise
19591          * @static
19592          */
19593         isHandle: function(sDDId, sHandleId) {
19594             return ( this.handleIds[sDDId] &&
19595                             this.handleIds[sDDId][sHandleId] );
19596         },
19597
19598         /**
19599          * Returns the DragDrop instance for a given id
19600          * @method getDDById
19601          * @param {String} id the id of the DragDrop object
19602          * @return {DragDrop} the drag drop object, null if it is not found
19603          * @static
19604          */
19605         getDDById: function(id) {
19606             for (var i in this.ids) {
19607                 if (this.ids[i][id]) {
19608                     return this.ids[i][id];
19609                 }
19610             }
19611             return null;
19612         },
19613
19614         /**
19615          * Fired after a registered DragDrop object gets the mousedown event.
19616          * Sets up the events required to track the object being dragged
19617          * @method handleMouseDown
19618          * @param {Event} e the event
19619          * @param oDD the DragDrop object being dragged
19620          * @private
19621          * @static
19622          */
19623         handleMouseDown: function(e, oDD) {
19624             if(Roo.QuickTips){
19625                 Roo.QuickTips.disable();
19626             }
19627             this.currentTarget = e.getTarget();
19628
19629             this.dragCurrent = oDD;
19630
19631             var el = oDD.getEl();
19632
19633             // track start position
19634             this.startX = e.getPageX();
19635             this.startY = e.getPageY();
19636
19637             this.deltaX = this.startX - el.offsetLeft;
19638             this.deltaY = this.startY - el.offsetTop;
19639
19640             this.dragThreshMet = false;
19641
19642             this.clickTimeout = setTimeout(
19643                     function() {
19644                         var DDM = Roo.dd.DDM;
19645                         DDM.startDrag(DDM.startX, DDM.startY);
19646                     },
19647                     this.clickTimeThresh );
19648         },
19649
19650         /**
19651          * Fired when either the drag pixel threshol or the mousedown hold
19652          * time threshold has been met.
19653          * @method startDrag
19654          * @param x {int} the X position of the original mousedown
19655          * @param y {int} the Y position of the original mousedown
19656          * @static
19657          */
19658         startDrag: function(x, y) {
19659             clearTimeout(this.clickTimeout);
19660             if (this.dragCurrent) {
19661                 this.dragCurrent.b4StartDrag(x, y);
19662                 this.dragCurrent.startDrag(x, y);
19663             }
19664             this.dragThreshMet = true;
19665         },
19666
19667         /**
19668          * Internal function to handle the mouseup event.  Will be invoked
19669          * from the context of the document.
19670          * @method handleMouseUp
19671          * @param {Event} e the event
19672          * @private
19673          * @static
19674          */
19675         handleMouseUp: function(e) {
19676
19677             if(Roo.QuickTips){
19678                 Roo.QuickTips.enable();
19679             }
19680             if (! this.dragCurrent) {
19681                 return;
19682             }
19683
19684             clearTimeout(this.clickTimeout);
19685
19686             if (this.dragThreshMet) {
19687                 this.fireEvents(e, true);
19688             } else {
19689             }
19690
19691             this.stopDrag(e);
19692
19693             this.stopEvent(e);
19694         },
19695
19696         /**
19697          * Utility to stop event propagation and event default, if these
19698          * features are turned on.
19699          * @method stopEvent
19700          * @param {Event} e the event as returned by this.getEvent()
19701          * @static
19702          */
19703         stopEvent: function(e){
19704             if(this.stopPropagation) {
19705                 e.stopPropagation();
19706             }
19707
19708             if (this.preventDefault) {
19709                 e.preventDefault();
19710             }
19711         },
19712
19713         /**
19714          * Internal function to clean up event handlers after the drag
19715          * operation is complete
19716          * @method stopDrag
19717          * @param {Event} e the event
19718          * @private
19719          * @static
19720          */
19721         stopDrag: function(e) {
19722             // Fire the drag end event for the item that was dragged
19723             if (this.dragCurrent) {
19724                 if (this.dragThreshMet) {
19725                     this.dragCurrent.b4EndDrag(e);
19726                     this.dragCurrent.endDrag(e);
19727                 }
19728
19729                 this.dragCurrent.onMouseUp(e);
19730             }
19731
19732             this.dragCurrent = null;
19733             this.dragOvers = {};
19734         },
19735
19736         /**
19737          * Internal function to handle the mousemove event.  Will be invoked
19738          * from the context of the html element.
19739          *
19740          * @TODO figure out what we can do about mouse events lost when the
19741          * user drags objects beyond the window boundary.  Currently we can
19742          * detect this in internet explorer by verifying that the mouse is
19743          * down during the mousemove event.  Firefox doesn't give us the
19744          * button state on the mousemove event.
19745          * @method handleMouseMove
19746          * @param {Event} e the event
19747          * @private
19748          * @static
19749          */
19750         handleMouseMove: function(e) {
19751             if (! this.dragCurrent) {
19752                 return true;
19753             }
19754
19755             // var button = e.which || e.button;
19756
19757             // check for IE mouseup outside of page boundary
19758             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19759                 this.stopEvent(e);
19760                 return this.handleMouseUp(e);
19761             }
19762
19763             if (!this.dragThreshMet) {
19764                 var diffX = Math.abs(this.startX - e.getPageX());
19765                 var diffY = Math.abs(this.startY - e.getPageY());
19766                 if (diffX > this.clickPixelThresh ||
19767                             diffY > this.clickPixelThresh) {
19768                     this.startDrag(this.startX, this.startY);
19769                 }
19770             }
19771
19772             if (this.dragThreshMet) {
19773                 this.dragCurrent.b4Drag(e);
19774                 this.dragCurrent.onDrag(e);
19775                 if(!this.dragCurrent.moveOnly){
19776                     this.fireEvents(e, false);
19777                 }
19778             }
19779
19780             this.stopEvent(e);
19781
19782             return true;
19783         },
19784
19785         /**
19786          * Iterates over all of the DragDrop elements to find ones we are
19787          * hovering over or dropping on
19788          * @method fireEvents
19789          * @param {Event} e the event
19790          * @param {boolean} isDrop is this a drop op or a mouseover op?
19791          * @private
19792          * @static
19793          */
19794         fireEvents: function(e, isDrop) {
19795             var dc = this.dragCurrent;
19796
19797             // If the user did the mouse up outside of the window, we could
19798             // get here even though we have ended the drag.
19799             if (!dc || dc.isLocked()) {
19800                 return;
19801             }
19802
19803             var pt = e.getPoint();
19804
19805             // cache the previous dragOver array
19806             var oldOvers = [];
19807
19808             var outEvts   = [];
19809             var overEvts  = [];
19810             var dropEvts  = [];
19811             var enterEvts = [];
19812
19813             // Check to see if the object(s) we were hovering over is no longer
19814             // being hovered over so we can fire the onDragOut event
19815             for (var i in this.dragOvers) {
19816
19817                 var ddo = this.dragOvers[i];
19818
19819                 if (! this.isTypeOfDD(ddo)) {
19820                     continue;
19821                 }
19822
19823                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19824                     outEvts.push( ddo );
19825                 }
19826
19827                 oldOvers[i] = true;
19828                 delete this.dragOvers[i];
19829             }
19830
19831             for (var sGroup in dc.groups) {
19832
19833                 if ("string" != typeof sGroup) {
19834                     continue;
19835                 }
19836
19837                 for (i in this.ids[sGroup]) {
19838                     var oDD = this.ids[sGroup][i];
19839                     if (! this.isTypeOfDD(oDD)) {
19840                         continue;
19841                     }
19842
19843                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19844                         if (this.isOverTarget(pt, oDD, this.mode)) {
19845                             // look for drop interactions
19846                             if (isDrop) {
19847                                 dropEvts.push( oDD );
19848                             // look for drag enter and drag over interactions
19849                             } else {
19850
19851                                 // initial drag over: dragEnter fires
19852                                 if (!oldOvers[oDD.id]) {
19853                                     enterEvts.push( oDD );
19854                                 // subsequent drag overs: dragOver fires
19855                                 } else {
19856                                     overEvts.push( oDD );
19857                                 }
19858
19859                                 this.dragOvers[oDD.id] = oDD;
19860                             }
19861                         }
19862                     }
19863                 }
19864             }
19865
19866             if (this.mode) {
19867                 if (outEvts.length) {
19868                     dc.b4DragOut(e, outEvts);
19869                     dc.onDragOut(e, outEvts);
19870                 }
19871
19872                 if (enterEvts.length) {
19873                     dc.onDragEnter(e, enterEvts);
19874                 }
19875
19876                 if (overEvts.length) {
19877                     dc.b4DragOver(e, overEvts);
19878                     dc.onDragOver(e, overEvts);
19879                 }
19880
19881                 if (dropEvts.length) {
19882                     dc.b4DragDrop(e, dropEvts);
19883                     dc.onDragDrop(e, dropEvts);
19884                 }
19885
19886             } else {
19887                 // fire dragout events
19888                 var len = 0;
19889                 for (i=0, len=outEvts.length; i<len; ++i) {
19890                     dc.b4DragOut(e, outEvts[i].id);
19891                     dc.onDragOut(e, outEvts[i].id);
19892                 }
19893
19894                 // fire enter events
19895                 for (i=0,len=enterEvts.length; i<len; ++i) {
19896                     // dc.b4DragEnter(e, oDD.id);
19897                     dc.onDragEnter(e, enterEvts[i].id);
19898                 }
19899
19900                 // fire over events
19901                 for (i=0,len=overEvts.length; i<len; ++i) {
19902                     dc.b4DragOver(e, overEvts[i].id);
19903                     dc.onDragOver(e, overEvts[i].id);
19904                 }
19905
19906                 // fire drop events
19907                 for (i=0, len=dropEvts.length; i<len; ++i) {
19908                     dc.b4DragDrop(e, dropEvts[i].id);
19909                     dc.onDragDrop(e, dropEvts[i].id);
19910                 }
19911
19912             }
19913
19914             // notify about a drop that did not find a target
19915             if (isDrop && !dropEvts.length) {
19916                 dc.onInvalidDrop(e);
19917             }
19918
19919         },
19920
19921         /**
19922          * Helper function for getting the best match from the list of drag
19923          * and drop objects returned by the drag and drop events when we are
19924          * in INTERSECT mode.  It returns either the first object that the
19925          * cursor is over, or the object that has the greatest overlap with
19926          * the dragged element.
19927          * @method getBestMatch
19928          * @param  {DragDrop[]} dds The array of drag and drop objects
19929          * targeted
19930          * @return {DragDrop}       The best single match
19931          * @static
19932          */
19933         getBestMatch: function(dds) {
19934             var winner = null;
19935             // Return null if the input is not what we expect
19936             //if (!dds || !dds.length || dds.length == 0) {
19937                // winner = null;
19938             // If there is only one item, it wins
19939             //} else if (dds.length == 1) {
19940
19941             var len = dds.length;
19942
19943             if (len == 1) {
19944                 winner = dds[0];
19945             } else {
19946                 // Loop through the targeted items
19947                 for (var i=0; i<len; ++i) {
19948                     var dd = dds[i];
19949                     // If the cursor is over the object, it wins.  If the
19950                     // cursor is over multiple matches, the first one we come
19951                     // to wins.
19952                     if (dd.cursorIsOver) {
19953                         winner = dd;
19954                         break;
19955                     // Otherwise the object with the most overlap wins
19956                     } else {
19957                         if (!winner ||
19958                             winner.overlap.getArea() < dd.overlap.getArea()) {
19959                             winner = dd;
19960                         }
19961                     }
19962                 }
19963             }
19964
19965             return winner;
19966         },
19967
19968         /**
19969          * Refreshes the cache of the top-left and bottom-right points of the
19970          * drag and drop objects in the specified group(s).  This is in the
19971          * format that is stored in the drag and drop instance, so typical
19972          * usage is:
19973          * <code>
19974          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19975          * </code>
19976          * Alternatively:
19977          * <code>
19978          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19979          * </code>
19980          * @TODO this really should be an indexed array.  Alternatively this
19981          * method could accept both.
19982          * @method refreshCache
19983          * @param {Object} groups an associative array of groups to refresh
19984          * @static
19985          */
19986         refreshCache: function(groups) {
19987             for (var sGroup in groups) {
19988                 if ("string" != typeof sGroup) {
19989                     continue;
19990                 }
19991                 for (var i in this.ids[sGroup]) {
19992                     var oDD = this.ids[sGroup][i];
19993
19994                     if (this.isTypeOfDD(oDD)) {
19995                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19996                         var loc = this.getLocation(oDD);
19997                         if (loc) {
19998                             this.locationCache[oDD.id] = loc;
19999                         } else {
20000                             delete this.locationCache[oDD.id];
20001                             // this will unregister the drag and drop object if
20002                             // the element is not in a usable state
20003                             // oDD.unreg();
20004                         }
20005                     }
20006                 }
20007             }
20008         },
20009
20010         /**
20011          * This checks to make sure an element exists and is in the DOM.  The
20012          * main purpose is to handle cases where innerHTML is used to remove
20013          * drag and drop objects from the DOM.  IE provides an 'unspecified
20014          * error' when trying to access the offsetParent of such an element
20015          * @method verifyEl
20016          * @param {HTMLElement} el the element to check
20017          * @return {boolean} true if the element looks usable
20018          * @static
20019          */
20020         verifyEl: function(el) {
20021             if (el) {
20022                 var parent;
20023                 if(Roo.isIE){
20024                     try{
20025                         parent = el.offsetParent;
20026                     }catch(e){}
20027                 }else{
20028                     parent = el.offsetParent;
20029                 }
20030                 if (parent) {
20031                     return true;
20032                 }
20033             }
20034
20035             return false;
20036         },
20037
20038         /**
20039          * Returns a Region object containing the drag and drop element's position
20040          * and size, including the padding configured for it
20041          * @method getLocation
20042          * @param {DragDrop} oDD the drag and drop object to get the
20043          *                       location for
20044          * @return {Roo.lib.Region} a Region object representing the total area
20045          *                             the element occupies, including any padding
20046          *                             the instance is configured for.
20047          * @static
20048          */
20049         getLocation: function(oDD) {
20050             if (! this.isTypeOfDD(oDD)) {
20051                 return null;
20052             }
20053
20054             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20055
20056             try {
20057                 pos= Roo.lib.Dom.getXY(el);
20058             } catch (e) { }
20059
20060             if (!pos) {
20061                 return null;
20062             }
20063
20064             x1 = pos[0];
20065             x2 = x1 + el.offsetWidth;
20066             y1 = pos[1];
20067             y2 = y1 + el.offsetHeight;
20068
20069             t = y1 - oDD.padding[0];
20070             r = x2 + oDD.padding[1];
20071             b = y2 + oDD.padding[2];
20072             l = x1 - oDD.padding[3];
20073
20074             return new Roo.lib.Region( t, r, b, l );
20075         },
20076
20077         /**
20078          * Checks the cursor location to see if it over the target
20079          * @method isOverTarget
20080          * @param {Roo.lib.Point} pt The point to evaluate
20081          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20082          * @return {boolean} true if the mouse is over the target
20083          * @private
20084          * @static
20085          */
20086         isOverTarget: function(pt, oTarget, intersect) {
20087             // use cache if available
20088             var loc = this.locationCache[oTarget.id];
20089             if (!loc || !this.useCache) {
20090                 loc = this.getLocation(oTarget);
20091                 this.locationCache[oTarget.id] = loc;
20092
20093             }
20094
20095             if (!loc) {
20096                 return false;
20097             }
20098
20099             oTarget.cursorIsOver = loc.contains( pt );
20100
20101             // DragDrop is using this as a sanity check for the initial mousedown
20102             // in this case we are done.  In POINT mode, if the drag obj has no
20103             // contraints, we are also done. Otherwise we need to evaluate the
20104             // location of the target as related to the actual location of the
20105             // dragged element.
20106             var dc = this.dragCurrent;
20107             if (!dc || !dc.getTargetCoord ||
20108                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20109                 return oTarget.cursorIsOver;
20110             }
20111
20112             oTarget.overlap = null;
20113
20114             // Get the current location of the drag element, this is the
20115             // location of the mouse event less the delta that represents
20116             // where the original mousedown happened on the element.  We
20117             // need to consider constraints and ticks as well.
20118             var pos = dc.getTargetCoord(pt.x, pt.y);
20119
20120             var el = dc.getDragEl();
20121             var curRegion = new Roo.lib.Region( pos.y,
20122                                                    pos.x + el.offsetWidth,
20123                                                    pos.y + el.offsetHeight,
20124                                                    pos.x );
20125
20126             var overlap = curRegion.intersect(loc);
20127
20128             if (overlap) {
20129                 oTarget.overlap = overlap;
20130                 return (intersect) ? true : oTarget.cursorIsOver;
20131             } else {
20132                 return false;
20133             }
20134         },
20135
20136         /**
20137          * unload event handler
20138          * @method _onUnload
20139          * @private
20140          * @static
20141          */
20142         _onUnload: function(e, me) {
20143             Roo.dd.DragDropMgr.unregAll();
20144         },
20145
20146         /**
20147          * Cleans up the drag and drop events and objects.
20148          * @method unregAll
20149          * @private
20150          * @static
20151          */
20152         unregAll: function() {
20153
20154             if (this.dragCurrent) {
20155                 this.stopDrag();
20156                 this.dragCurrent = null;
20157             }
20158
20159             this._execOnAll("unreg", []);
20160
20161             for (i in this.elementCache) {
20162                 delete this.elementCache[i];
20163             }
20164
20165             this.elementCache = {};
20166             this.ids = {};
20167         },
20168
20169         /**
20170          * A cache of DOM elements
20171          * @property elementCache
20172          * @private
20173          * @static
20174          */
20175         elementCache: {},
20176
20177         /**
20178          * Get the wrapper for the DOM element specified
20179          * @method getElWrapper
20180          * @param {String} id the id of the element to get
20181          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20182          * @private
20183          * @deprecated This wrapper isn't that useful
20184          * @static
20185          */
20186         getElWrapper: function(id) {
20187             var oWrapper = this.elementCache[id];
20188             if (!oWrapper || !oWrapper.el) {
20189                 oWrapper = this.elementCache[id] =
20190                     new this.ElementWrapper(Roo.getDom(id));
20191             }
20192             return oWrapper;
20193         },
20194
20195         /**
20196          * Returns the actual DOM element
20197          * @method getElement
20198          * @param {String} id the id of the elment to get
20199          * @return {Object} The element
20200          * @deprecated use Roo.getDom instead
20201          * @static
20202          */
20203         getElement: function(id) {
20204             return Roo.getDom(id);
20205         },
20206
20207         /**
20208          * Returns the style property for the DOM element (i.e.,
20209          * document.getElById(id).style)
20210          * @method getCss
20211          * @param {String} id the id of the elment to get
20212          * @return {Object} The style property of the element
20213          * @deprecated use Roo.getDom instead
20214          * @static
20215          */
20216         getCss: function(id) {
20217             var el = Roo.getDom(id);
20218             return (el) ? el.style : null;
20219         },
20220
20221         /**
20222          * Inner class for cached elements
20223          * @class DragDropMgr.ElementWrapper
20224          * @for DragDropMgr
20225          * @private
20226          * @deprecated
20227          */
20228         ElementWrapper: function(el) {
20229                 /**
20230                  * The element
20231                  * @property el
20232                  */
20233                 this.el = el || null;
20234                 /**
20235                  * The element id
20236                  * @property id
20237                  */
20238                 this.id = this.el && el.id;
20239                 /**
20240                  * A reference to the style property
20241                  * @property css
20242                  */
20243                 this.css = this.el && el.style;
20244             },
20245
20246         /**
20247          * Returns the X position of an html element
20248          * @method getPosX
20249          * @param el the element for which to get the position
20250          * @return {int} the X coordinate
20251          * @for DragDropMgr
20252          * @deprecated use Roo.lib.Dom.getX instead
20253          * @static
20254          */
20255         getPosX: function(el) {
20256             return Roo.lib.Dom.getX(el);
20257         },
20258
20259         /**
20260          * Returns the Y position of an html element
20261          * @method getPosY
20262          * @param el the element for which to get the position
20263          * @return {int} the Y coordinate
20264          * @deprecated use Roo.lib.Dom.getY instead
20265          * @static
20266          */
20267         getPosY: function(el) {
20268             return Roo.lib.Dom.getY(el);
20269         },
20270
20271         /**
20272          * Swap two nodes.  In IE, we use the native method, for others we
20273          * emulate the IE behavior
20274          * @method swapNode
20275          * @param n1 the first node to swap
20276          * @param n2 the other node to swap
20277          * @static
20278          */
20279         swapNode: function(n1, n2) {
20280             if (n1.swapNode) {
20281                 n1.swapNode(n2);
20282             } else {
20283                 var p = n2.parentNode;
20284                 var s = n2.nextSibling;
20285
20286                 if (s == n1) {
20287                     p.insertBefore(n1, n2);
20288                 } else if (n2 == n1.nextSibling) {
20289                     p.insertBefore(n2, n1);
20290                 } else {
20291                     n1.parentNode.replaceChild(n2, n1);
20292                     p.insertBefore(n1, s);
20293                 }
20294             }
20295         },
20296
20297         /**
20298          * Returns the current scroll position
20299          * @method getScroll
20300          * @private
20301          * @static
20302          */
20303         getScroll: function () {
20304             var t, l, dde=document.documentElement, db=document.body;
20305             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20306                 t = dde.scrollTop;
20307                 l = dde.scrollLeft;
20308             } else if (db) {
20309                 t = db.scrollTop;
20310                 l = db.scrollLeft;
20311             } else {
20312
20313             }
20314             return { top: t, left: l };
20315         },
20316
20317         /**
20318          * Returns the specified element style property
20319          * @method getStyle
20320          * @param {HTMLElement} el          the element
20321          * @param {string}      styleProp   the style property
20322          * @return {string} The value of the style property
20323          * @deprecated use Roo.lib.Dom.getStyle
20324          * @static
20325          */
20326         getStyle: function(el, styleProp) {
20327             return Roo.fly(el).getStyle(styleProp);
20328         },
20329
20330         /**
20331          * Gets the scrollTop
20332          * @method getScrollTop
20333          * @return {int} the document's scrollTop
20334          * @static
20335          */
20336         getScrollTop: function () { return this.getScroll().top; },
20337
20338         /**
20339          * Gets the scrollLeft
20340          * @method getScrollLeft
20341          * @return {int} the document's scrollTop
20342          * @static
20343          */
20344         getScrollLeft: function () { return this.getScroll().left; },
20345
20346         /**
20347          * Sets the x/y position of an element to the location of the
20348          * target element.
20349          * @method moveToEl
20350          * @param {HTMLElement} moveEl      The element to move
20351          * @param {HTMLElement} targetEl    The position reference element
20352          * @static
20353          */
20354         moveToEl: function (moveEl, targetEl) {
20355             var aCoord = Roo.lib.Dom.getXY(targetEl);
20356             Roo.lib.Dom.setXY(moveEl, aCoord);
20357         },
20358
20359         /**
20360          * Numeric array sort function
20361          * @method numericSort
20362          * @static
20363          */
20364         numericSort: function(a, b) { return (a - b); },
20365
20366         /**
20367          * Internal counter
20368          * @property _timeoutCount
20369          * @private
20370          * @static
20371          */
20372         _timeoutCount: 0,
20373
20374         /**
20375          * Trying to make the load order less important.  Without this we get
20376          * an error if this file is loaded before the Event Utility.
20377          * @method _addListeners
20378          * @private
20379          * @static
20380          */
20381         _addListeners: function() {
20382             var DDM = Roo.dd.DDM;
20383             if ( Roo.lib.Event && document ) {
20384                 DDM._onLoad();
20385             } else {
20386                 if (DDM._timeoutCount > 2000) {
20387                 } else {
20388                     setTimeout(DDM._addListeners, 10);
20389                     if (document && document.body) {
20390                         DDM._timeoutCount += 1;
20391                     }
20392                 }
20393             }
20394         },
20395
20396         /**
20397          * Recursively searches the immediate parent and all child nodes for
20398          * the handle element in order to determine wheter or not it was
20399          * clicked.
20400          * @method handleWasClicked
20401          * @param node the html element to inspect
20402          * @static
20403          */
20404         handleWasClicked: function(node, id) {
20405             if (this.isHandle(id, node.id)) {
20406                 return true;
20407             } else {
20408                 // check to see if this is a text node child of the one we want
20409                 var p = node.parentNode;
20410
20411                 while (p) {
20412                     if (this.isHandle(id, p.id)) {
20413                         return true;
20414                     } else {
20415                         p = p.parentNode;
20416                     }
20417                 }
20418             }
20419
20420             return false;
20421         }
20422
20423     };
20424
20425 }();
20426
20427 // shorter alias, save a few bytes
20428 Roo.dd.DDM = Roo.dd.DragDropMgr;
20429 Roo.dd.DDM._addListeners();
20430
20431 }/*
20432  * Based on:
20433  * Ext JS Library 1.1.1
20434  * Copyright(c) 2006-2007, Ext JS, LLC.
20435  *
20436  * Originally Released Under LGPL - original licence link has changed is not relivant.
20437  *
20438  * Fork - LGPL
20439  * <script type="text/javascript">
20440  */
20441
20442 /**
20443  * @class Roo.dd.DD
20444  * A DragDrop implementation where the linked element follows the
20445  * mouse cursor during a drag.
20446  * @extends Roo.dd.DragDrop
20447  * @constructor
20448  * @param {String} id the id of the linked element
20449  * @param {String} sGroup the group of related DragDrop items
20450  * @param {object} config an object containing configurable attributes
20451  *                Valid properties for DD:
20452  *                    scroll
20453  */
20454 Roo.dd.DD = function(id, sGroup, config) {
20455     if (id) {
20456         this.init(id, sGroup, config);
20457     }
20458 };
20459
20460 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20461
20462     /**
20463      * When set to true, the utility automatically tries to scroll the browser
20464      * window wehn a drag and drop element is dragged near the viewport boundary.
20465      * Defaults to true.
20466      * @property scroll
20467      * @type boolean
20468      */
20469     scroll: true,
20470
20471     /**
20472      * Sets the pointer offset to the distance between the linked element's top
20473      * left corner and the location the element was clicked
20474      * @method autoOffset
20475      * @param {int} iPageX the X coordinate of the click
20476      * @param {int} iPageY the Y coordinate of the click
20477      */
20478     autoOffset: function(iPageX, iPageY) {
20479         var x = iPageX - this.startPageX;
20480         var y = iPageY - this.startPageY;
20481         this.setDelta(x, y);
20482     },
20483
20484     /**
20485      * Sets the pointer offset.  You can call this directly to force the
20486      * offset to be in a particular location (e.g., pass in 0,0 to set it
20487      * to the center of the object)
20488      * @method setDelta
20489      * @param {int} iDeltaX the distance from the left
20490      * @param {int} iDeltaY the distance from the top
20491      */
20492     setDelta: function(iDeltaX, iDeltaY) {
20493         this.deltaX = iDeltaX;
20494         this.deltaY = iDeltaY;
20495     },
20496
20497     /**
20498      * Sets the drag element to the location of the mousedown or click event,
20499      * maintaining the cursor location relative to the location on the element
20500      * that was clicked.  Override this if you want to place the element in a
20501      * location other than where the cursor is.
20502      * @method setDragElPos
20503      * @param {int} iPageX the X coordinate of the mousedown or drag event
20504      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20505      */
20506     setDragElPos: function(iPageX, iPageY) {
20507         // the first time we do this, we are going to check to make sure
20508         // the element has css positioning
20509
20510         var el = this.getDragEl();
20511         this.alignElWithMouse(el, iPageX, iPageY);
20512     },
20513
20514     /**
20515      * Sets the 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 alignElWithMouse
20520      * @param {HTMLElement} el the element to move
20521      * @param {int} iPageX the X coordinate of the mousedown or drag event
20522      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20523      */
20524     alignElWithMouse: function(el, iPageX, iPageY) {
20525         var oCoord = this.getTargetCoord(iPageX, iPageY);
20526         var fly = el.dom ? el : Roo.fly(el);
20527         if (!this.deltaSetXY) {
20528             var aCoord = [oCoord.x, oCoord.y];
20529             fly.setXY(aCoord);
20530             var newLeft = fly.getLeft(true);
20531             var newTop  = fly.getTop(true);
20532             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20533         } else {
20534             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20535         }
20536
20537         this.cachePosition(oCoord.x, oCoord.y);
20538         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20539         return oCoord;
20540     },
20541
20542     /**
20543      * Saves the most recent position so that we can reset the constraints and
20544      * tick marks on-demand.  We need to know this so that we can calculate the
20545      * number of pixels the element is offset from its original position.
20546      * @method cachePosition
20547      * @param iPageX the current x position (optional, this just makes it so we
20548      * don't have to look it up again)
20549      * @param iPageY the current y position (optional, this just makes it so we
20550      * don't have to look it up again)
20551      */
20552     cachePosition: function(iPageX, iPageY) {
20553         if (iPageX) {
20554             this.lastPageX = iPageX;
20555             this.lastPageY = iPageY;
20556         } else {
20557             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20558             this.lastPageX = aCoord[0];
20559             this.lastPageY = aCoord[1];
20560         }
20561     },
20562
20563     /**
20564      * Auto-scroll the window if the dragged object has been moved beyond the
20565      * visible window boundary.
20566      * @method autoScroll
20567      * @param {int} x the drag element's x position
20568      * @param {int} y the drag element's y position
20569      * @param {int} h the height of the drag element
20570      * @param {int} w the width of the drag element
20571      * @private
20572      */
20573     autoScroll: function(x, y, h, w) {
20574
20575         if (this.scroll) {
20576             // The client height
20577             var clientH = Roo.lib.Dom.getViewWidth();
20578
20579             // The client width
20580             var clientW = Roo.lib.Dom.getViewHeight();
20581
20582             // The amt scrolled down
20583             var st = this.DDM.getScrollTop();
20584
20585             // The amt scrolled right
20586             var sl = this.DDM.getScrollLeft();
20587
20588             // Location of the bottom of the element
20589             var bot = h + y;
20590
20591             // Location of the right of the element
20592             var right = w + x;
20593
20594             // The distance from the cursor to the bottom of the visible area,
20595             // adjusted so that we don't scroll if the cursor is beyond the
20596             // element drag constraints
20597             var toBot = (clientH + st - y - this.deltaY);
20598
20599             // The distance from the cursor to the right of the visible area
20600             var toRight = (clientW + sl - x - this.deltaX);
20601
20602
20603             // How close to the edge the cursor must be before we scroll
20604             // var thresh = (document.all) ? 100 : 40;
20605             var thresh = 40;
20606
20607             // How many pixels to scroll per autoscroll op.  This helps to reduce
20608             // clunky scrolling. IE is more sensitive about this ... it needs this
20609             // value to be higher.
20610             var scrAmt = (document.all) ? 80 : 30;
20611
20612             // Scroll down if we are near the bottom of the visible page and the
20613             // obj extends below the crease
20614             if ( bot > clientH && toBot < thresh ) {
20615                 window.scrollTo(sl, st + scrAmt);
20616             }
20617
20618             // Scroll up if the window is scrolled down and the top of the object
20619             // goes above the top border
20620             if ( y < st && st > 0 && y - st < thresh ) {
20621                 window.scrollTo(sl, st - scrAmt);
20622             }
20623
20624             // Scroll right if the obj is beyond the right border and the cursor is
20625             // near the border.
20626             if ( right > clientW && toRight < thresh ) {
20627                 window.scrollTo(sl + scrAmt, st);
20628             }
20629
20630             // Scroll left if the window has been scrolled to the right and the obj
20631             // extends past the left border
20632             if ( x < sl && sl > 0 && x - sl < thresh ) {
20633                 window.scrollTo(sl - scrAmt, st);
20634             }
20635         }
20636     },
20637
20638     /**
20639      * Finds the location the element should be placed if we want to move
20640      * it to where the mouse location less the click offset would place us.
20641      * @method getTargetCoord
20642      * @param {int} iPageX the X coordinate of the click
20643      * @param {int} iPageY the Y coordinate of the click
20644      * @return an object that contains the coordinates (Object.x and Object.y)
20645      * @private
20646      */
20647     getTargetCoord: function(iPageX, iPageY) {
20648
20649
20650         var x = iPageX - this.deltaX;
20651         var y = iPageY - this.deltaY;
20652
20653         if (this.constrainX) {
20654             if (x < this.minX) { x = this.minX; }
20655             if (x > this.maxX) { x = this.maxX; }
20656         }
20657
20658         if (this.constrainY) {
20659             if (y < this.minY) { y = this.minY; }
20660             if (y > this.maxY) { y = this.maxY; }
20661         }
20662
20663         x = this.getTick(x, this.xTicks);
20664         y = this.getTick(y, this.yTicks);
20665
20666
20667         return {x:x, y:y};
20668     },
20669
20670     /*
20671      * Sets up config options specific to this class. Overrides
20672      * Roo.dd.DragDrop, but all versions of this method through the
20673      * inheritance chain are called
20674      */
20675     applyConfig: function() {
20676         Roo.dd.DD.superclass.applyConfig.call(this);
20677         this.scroll = (this.config.scroll !== false);
20678     },
20679
20680     /*
20681      * Event that fires prior to the onMouseDown event.  Overrides
20682      * Roo.dd.DragDrop.
20683      */
20684     b4MouseDown: function(e) {
20685         // this.resetConstraints();
20686         this.autoOffset(e.getPageX(),
20687                             e.getPageY());
20688     },
20689
20690     /*
20691      * Event that fires prior to the onDrag event.  Overrides
20692      * Roo.dd.DragDrop.
20693      */
20694     b4Drag: function(e) {
20695         this.setDragElPos(e.getPageX(),
20696                             e.getPageY());
20697     },
20698
20699     toString: function() {
20700         return ("DD " + this.id);
20701     }
20702
20703     //////////////////////////////////////////////////////////////////////////
20704     // Debugging ygDragDrop events that can be overridden
20705     //////////////////////////////////////////////////////////////////////////
20706     /*
20707     startDrag: function(x, y) {
20708     },
20709
20710     onDrag: function(e) {
20711     },
20712
20713     onDragEnter: function(e, id) {
20714     },
20715
20716     onDragOver: function(e, id) {
20717     },
20718
20719     onDragOut: function(e, id) {
20720     },
20721
20722     onDragDrop: function(e, id) {
20723     },
20724
20725     endDrag: function(e) {
20726     }
20727
20728     */
20729
20730 });/*
20731  * Based on:
20732  * Ext JS Library 1.1.1
20733  * Copyright(c) 2006-2007, Ext JS, LLC.
20734  *
20735  * Originally Released Under LGPL - original licence link has changed is not relivant.
20736  *
20737  * Fork - LGPL
20738  * <script type="text/javascript">
20739  */
20740
20741 /**
20742  * @class Roo.dd.DDProxy
20743  * A DragDrop implementation that inserts an empty, bordered div into
20744  * the document that follows the cursor during drag operations.  At the time of
20745  * the click, the frame div is resized to the dimensions of the linked html
20746  * element, and moved to the exact location of the linked element.
20747  *
20748  * References to the "frame" element refer to the single proxy element that
20749  * was created to be dragged in place of all DDProxy elements on the
20750  * page.
20751  *
20752  * @extends Roo.dd.DD
20753  * @constructor
20754  * @param {String} id the id of the linked html element
20755  * @param {String} sGroup the group of related DragDrop objects
20756  * @param {object} config an object containing configurable attributes
20757  *                Valid properties for DDProxy in addition to those in DragDrop:
20758  *                   resizeFrame, centerFrame, dragElId
20759  */
20760 Roo.dd.DDProxy = function(id, sGroup, config) {
20761     if (id) {
20762         this.init(id, sGroup, config);
20763         this.initFrame();
20764     }
20765 };
20766
20767 /**
20768  * The default drag frame div id
20769  * @property Roo.dd.DDProxy.dragElId
20770  * @type String
20771  * @static
20772  */
20773 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20774
20775 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20776
20777     /**
20778      * By default we resize the drag frame to be the same size as the element
20779      * we want to drag (this is to get the frame effect).  We can turn it off
20780      * if we want a different behavior.
20781      * @property resizeFrame
20782      * @type boolean
20783      */
20784     resizeFrame: true,
20785
20786     /**
20787      * By default the frame is positioned exactly where the drag element is, so
20788      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20789      * you do not have constraints on the obj is to have the drag frame centered
20790      * around the cursor.  Set centerFrame to true for this effect.
20791      * @property centerFrame
20792      * @type boolean
20793      */
20794     centerFrame: false,
20795
20796     /**
20797      * Creates the proxy element if it does not yet exist
20798      * @method createFrame
20799      */
20800     createFrame: function() {
20801         var self = this;
20802         var body = document.body;
20803
20804         if (!body || !body.firstChild) {
20805             setTimeout( function() { self.createFrame(); }, 50 );
20806             return;
20807         }
20808
20809         var div = this.getDragEl();
20810
20811         if (!div) {
20812             div    = document.createElement("div");
20813             div.id = this.dragElId;
20814             var s  = div.style;
20815
20816             s.position   = "absolute";
20817             s.visibility = "hidden";
20818             s.cursor     = "move";
20819             s.border     = "2px solid #aaa";
20820             s.zIndex     = 999;
20821
20822             // appendChild can blow up IE if invoked prior to the window load event
20823             // while rendering a table.  It is possible there are other scenarios
20824             // that would cause this to happen as well.
20825             body.insertBefore(div, body.firstChild);
20826         }
20827     },
20828
20829     /**
20830      * Initialization for the drag frame element.  Must be called in the
20831      * constructor of all subclasses
20832      * @method initFrame
20833      */
20834     initFrame: function() {
20835         this.createFrame();
20836     },
20837
20838     applyConfig: function() {
20839         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20840
20841         this.resizeFrame = (this.config.resizeFrame !== false);
20842         this.centerFrame = (this.config.centerFrame);
20843         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20844     },
20845
20846     /**
20847      * Resizes the drag frame to the dimensions of the clicked object, positions
20848      * it over the object, and finally displays it
20849      * @method showFrame
20850      * @param {int} iPageX X click position
20851      * @param {int} iPageY Y click position
20852      * @private
20853      */
20854     showFrame: function(iPageX, iPageY) {
20855         var el = this.getEl();
20856         var dragEl = this.getDragEl();
20857         var s = dragEl.style;
20858
20859         this._resizeProxy();
20860
20861         if (this.centerFrame) {
20862             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20863                            Math.round(parseInt(s.height, 10)/2) );
20864         }
20865
20866         this.setDragElPos(iPageX, iPageY);
20867
20868         Roo.fly(dragEl).show();
20869     },
20870
20871     /**
20872      * The proxy is automatically resized to the dimensions of the linked
20873      * element when a drag is initiated, unless resizeFrame is set to false
20874      * @method _resizeProxy
20875      * @private
20876      */
20877     _resizeProxy: function() {
20878         if (this.resizeFrame) {
20879             var el = this.getEl();
20880             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20881         }
20882     },
20883
20884     // overrides Roo.dd.DragDrop
20885     b4MouseDown: function(e) {
20886         var x = e.getPageX();
20887         var y = e.getPageY();
20888         this.autoOffset(x, y);
20889         this.setDragElPos(x, y);
20890     },
20891
20892     // overrides Roo.dd.DragDrop
20893     b4StartDrag: function(x, y) {
20894         // show the drag frame
20895         this.showFrame(x, y);
20896     },
20897
20898     // overrides Roo.dd.DragDrop
20899     b4EndDrag: function(e) {
20900         Roo.fly(this.getDragEl()).hide();
20901     },
20902
20903     // overrides Roo.dd.DragDrop
20904     // By default we try to move the element to the last location of the frame.
20905     // This is so that the default behavior mirrors that of Roo.dd.DD.
20906     endDrag: function(e) {
20907
20908         var lel = this.getEl();
20909         var del = this.getDragEl();
20910
20911         // Show the drag frame briefly so we can get its position
20912         del.style.visibility = "";
20913
20914         this.beforeMove();
20915         // Hide the linked element before the move to get around a Safari
20916         // rendering bug.
20917         lel.style.visibility = "hidden";
20918         Roo.dd.DDM.moveToEl(lel, del);
20919         del.style.visibility = "hidden";
20920         lel.style.visibility = "";
20921
20922         this.afterDrag();
20923     },
20924
20925     beforeMove : function(){
20926
20927     },
20928
20929     afterDrag : function(){
20930
20931     },
20932
20933     toString: function() {
20934         return ("DDProxy " + this.id);
20935     }
20936
20937 });
20938 /*
20939  * Based on:
20940  * Ext JS Library 1.1.1
20941  * Copyright(c) 2006-2007, Ext JS, LLC.
20942  *
20943  * Originally Released Under LGPL - original licence link has changed is not relivant.
20944  *
20945  * Fork - LGPL
20946  * <script type="text/javascript">
20947  */
20948
20949  /**
20950  * @class Roo.dd.DDTarget
20951  * A DragDrop implementation that does not move, but can be a drop
20952  * target.  You would get the same result by simply omitting implementation
20953  * for the event callbacks, but this way we reduce the processing cost of the
20954  * event listener and the callbacks.
20955  * @extends Roo.dd.DragDrop
20956  * @constructor
20957  * @param {String} id the id of the element that is a drop target
20958  * @param {String} sGroup the group of related DragDrop objects
20959  * @param {object} config an object containing configurable attributes
20960  *                 Valid properties for DDTarget in addition to those in
20961  *                 DragDrop:
20962  *                    none
20963  */
20964 Roo.dd.DDTarget = function(id, sGroup, config) {
20965     if (id) {
20966         this.initTarget(id, sGroup, config);
20967     }
20968     if (config.listeners || config.events) { 
20969        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20970             listeners : config.listeners || {}, 
20971             events : config.events || {} 
20972         });    
20973     }
20974 };
20975
20976 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20977 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20978     toString: function() {
20979         return ("DDTarget " + this.id);
20980     }
20981 });
20982 /*
20983  * Based on:
20984  * Ext JS Library 1.1.1
20985  * Copyright(c) 2006-2007, Ext JS, LLC.
20986  *
20987  * Originally Released Under LGPL - original licence link has changed is not relivant.
20988  *
20989  * Fork - LGPL
20990  * <script type="text/javascript">
20991  */
20992  
20993
20994 /**
20995  * @class Roo.dd.ScrollManager
20996  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20997  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20998  * @singleton
20999  */
21000 Roo.dd.ScrollManager = function(){
21001     var ddm = Roo.dd.DragDropMgr;
21002     var els = {};
21003     var dragEl = null;
21004     var proc = {};
21005     
21006     
21007     
21008     var onStop = function(e){
21009         dragEl = null;
21010         clearProc();
21011     };
21012     
21013     var triggerRefresh = function(){
21014         if(ddm.dragCurrent){
21015              ddm.refreshCache(ddm.dragCurrent.groups);
21016         }
21017     };
21018     
21019     var doScroll = function(){
21020         if(ddm.dragCurrent){
21021             var dds = Roo.dd.ScrollManager;
21022             if(!dds.animate){
21023                 if(proc.el.scroll(proc.dir, dds.increment)){
21024                     triggerRefresh();
21025                 }
21026             }else{
21027                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21028             }
21029         }
21030     };
21031     
21032     var clearProc = function(){
21033         if(proc.id){
21034             clearInterval(proc.id);
21035         }
21036         proc.id = 0;
21037         proc.el = null;
21038         proc.dir = "";
21039     };
21040     
21041     var startProc = function(el, dir){
21042          Roo.log('scroll startproc');
21043         clearProc();
21044         proc.el = el;
21045         proc.dir = dir;
21046         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21047     };
21048     
21049     var onFire = function(e, isDrop){
21050        
21051         if(isDrop || !ddm.dragCurrent){ return; }
21052         var dds = Roo.dd.ScrollManager;
21053         if(!dragEl || dragEl != ddm.dragCurrent){
21054             dragEl = ddm.dragCurrent;
21055             // refresh regions on drag start
21056             dds.refreshCache();
21057         }
21058         
21059         var xy = Roo.lib.Event.getXY(e);
21060         var pt = new Roo.lib.Point(xy[0], xy[1]);
21061         for(var id in els){
21062             var el = els[id], r = el._region;
21063             if(r && r.contains(pt) && el.isScrollable()){
21064                 if(r.bottom - pt.y <= dds.thresh){
21065                     if(proc.el != el){
21066                         startProc(el, "down");
21067                     }
21068                     return;
21069                 }else if(r.right - pt.x <= dds.thresh){
21070                     if(proc.el != el){
21071                         startProc(el, "left");
21072                     }
21073                     return;
21074                 }else if(pt.y - r.top <= dds.thresh){
21075                     if(proc.el != el){
21076                         startProc(el, "up");
21077                     }
21078                     return;
21079                 }else if(pt.x - r.left <= dds.thresh){
21080                     if(proc.el != el){
21081                         startProc(el, "right");
21082                     }
21083                     return;
21084                 }
21085             }
21086         }
21087         clearProc();
21088     };
21089     
21090     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21091     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21092     
21093     return {
21094         /**
21095          * Registers new overflow element(s) to auto scroll
21096          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21097          */
21098         register : function(el){
21099             if(el instanceof Array){
21100                 for(var i = 0, len = el.length; i < len; i++) {
21101                         this.register(el[i]);
21102                 }
21103             }else{
21104                 el = Roo.get(el);
21105                 els[el.id] = el;
21106             }
21107             Roo.dd.ScrollManager.els = els;
21108         },
21109         
21110         /**
21111          * Unregisters overflow element(s) so they are no longer scrolled
21112          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21113          */
21114         unregister : function(el){
21115             if(el instanceof Array){
21116                 for(var i = 0, len = el.length; i < len; i++) {
21117                         this.unregister(el[i]);
21118                 }
21119             }else{
21120                 el = Roo.get(el);
21121                 delete els[el.id];
21122             }
21123         },
21124         
21125         /**
21126          * The number of pixels from the edge of a container the pointer needs to be to 
21127          * trigger scrolling (defaults to 25)
21128          * @type Number
21129          */
21130         thresh : 25,
21131         
21132         /**
21133          * The number of pixels to scroll in each scroll increment (defaults to 50)
21134          * @type Number
21135          */
21136         increment : 100,
21137         
21138         /**
21139          * The frequency of scrolls in milliseconds (defaults to 500)
21140          * @type Number
21141          */
21142         frequency : 500,
21143         
21144         /**
21145          * True to animate the scroll (defaults to true)
21146          * @type Boolean
21147          */
21148         animate: true,
21149         
21150         /**
21151          * The animation duration in seconds - 
21152          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21153          * @type Number
21154          */
21155         animDuration: .4,
21156         
21157         /**
21158          * Manually trigger a cache refresh.
21159          */
21160         refreshCache : function(){
21161             for(var id in els){
21162                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21163                     els[id]._region = els[id].getRegion();
21164                 }
21165             }
21166         }
21167     };
21168 }();/*
21169  * Based on:
21170  * Ext JS Library 1.1.1
21171  * Copyright(c) 2006-2007, Ext JS, LLC.
21172  *
21173  * Originally Released Under LGPL - original licence link has changed is not relivant.
21174  *
21175  * Fork - LGPL
21176  * <script type="text/javascript">
21177  */
21178  
21179
21180 /**
21181  * @class Roo.dd.Registry
21182  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21183  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21184  * @singleton
21185  */
21186 Roo.dd.Registry = function(){
21187     var elements = {}; 
21188     var handles = {}; 
21189     var autoIdSeed = 0;
21190
21191     var getId = function(el, autogen){
21192         if(typeof el == "string"){
21193             return el;
21194         }
21195         var id = el.id;
21196         if(!id && autogen !== false){
21197             id = "roodd-" + (++autoIdSeed);
21198             el.id = id;
21199         }
21200         return id;
21201     };
21202     
21203     return {
21204     /**
21205      * Register a drag drop element
21206      * @param {String|HTMLElement} element The id or DOM node to register
21207      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21208      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21209      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21210      * populated in the data object (if applicable):
21211      * <pre>
21212 Value      Description<br />
21213 ---------  ------------------------------------------<br />
21214 handles    Array of DOM nodes that trigger dragging<br />
21215            for the element being registered<br />
21216 isHandle   True if the element passed in triggers<br />
21217            dragging itself, else false
21218 </pre>
21219      */
21220         register : function(el, data){
21221             data = data || {};
21222             if(typeof el == "string"){
21223                 el = document.getElementById(el);
21224             }
21225             data.ddel = el;
21226             elements[getId(el)] = data;
21227             if(data.isHandle !== false){
21228                 handles[data.ddel.id] = data;
21229             }
21230             if(data.handles){
21231                 var hs = data.handles;
21232                 for(var i = 0, len = hs.length; i < len; i++){
21233                         handles[getId(hs[i])] = data;
21234                 }
21235             }
21236         },
21237
21238     /**
21239      * Unregister a drag drop element
21240      * @param {String|HTMLElement}  element The id or DOM node to unregister
21241      */
21242         unregister : function(el){
21243             var id = getId(el, false);
21244             var data = elements[id];
21245             if(data){
21246                 delete elements[id];
21247                 if(data.handles){
21248                     var hs = data.handles;
21249                     for(var i = 0, len = hs.length; i < len; i++){
21250                         delete handles[getId(hs[i], false)];
21251                     }
21252                 }
21253             }
21254         },
21255
21256     /**
21257      * Returns the handle registered for a DOM Node by id
21258      * @param {String|HTMLElement} id The DOM node or id to look up
21259      * @return {Object} handle The custom handle data
21260      */
21261         getHandle : function(id){
21262             if(typeof id != "string"){ // must be element?
21263                 id = id.id;
21264             }
21265             return handles[id];
21266         },
21267
21268     /**
21269      * Returns the handle that is registered for the DOM node that is the target of the event
21270      * @param {Event} e The event
21271      * @return {Object} handle The custom handle data
21272      */
21273         getHandleFromEvent : function(e){
21274             var t = Roo.lib.Event.getTarget(e);
21275             return t ? handles[t.id] : null;
21276         },
21277
21278     /**
21279      * Returns a custom data object that is registered for a DOM node by id
21280      * @param {String|HTMLElement} id The DOM node or id to look up
21281      * @return {Object} data The custom data
21282      */
21283         getTarget : function(id){
21284             if(typeof id != "string"){ // must be element?
21285                 id = id.id;
21286             }
21287             return elements[id];
21288         },
21289
21290     /**
21291      * Returns a custom data object that is registered for the DOM node that is the target of the event
21292      * @param {Event} e The event
21293      * @return {Object} data The custom data
21294      */
21295         getTargetFromEvent : function(e){
21296             var t = Roo.lib.Event.getTarget(e);
21297             return t ? elements[t.id] || handles[t.id] : null;
21298         }
21299     };
21300 }();/*
21301  * Based on:
21302  * Ext JS Library 1.1.1
21303  * Copyright(c) 2006-2007, Ext JS, LLC.
21304  *
21305  * Originally Released Under LGPL - original licence link has changed is not relivant.
21306  *
21307  * Fork - LGPL
21308  * <script type="text/javascript">
21309  */
21310  
21311
21312 /**
21313  * @class Roo.dd.StatusProxy
21314  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21315  * default drag proxy used by all Roo.dd components.
21316  * @constructor
21317  * @param {Object} config
21318  */
21319 Roo.dd.StatusProxy = function(config){
21320     Roo.apply(this, config);
21321     this.id = this.id || Roo.id();
21322     this.el = new Roo.Layer({
21323         dh: {
21324             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21325                 {tag: "div", cls: "x-dd-drop-icon"},
21326                 {tag: "div", cls: "x-dd-drag-ghost"}
21327             ]
21328         }, 
21329         shadow: !config || config.shadow !== false
21330     });
21331     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21332     this.dropStatus = this.dropNotAllowed;
21333 };
21334
21335 Roo.dd.StatusProxy.prototype = {
21336     /**
21337      * @cfg {String} dropAllowed
21338      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21339      */
21340     dropAllowed : "x-dd-drop-ok",
21341     /**
21342      * @cfg {String} dropNotAllowed
21343      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21344      */
21345     dropNotAllowed : "x-dd-drop-nodrop",
21346
21347     /**
21348      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21349      * over the current target element.
21350      * @param {String} cssClass The css class for the new drop status indicator image
21351      */
21352     setStatus : function(cssClass){
21353         cssClass = cssClass || this.dropNotAllowed;
21354         if(this.dropStatus != cssClass){
21355             this.el.replaceClass(this.dropStatus, cssClass);
21356             this.dropStatus = cssClass;
21357         }
21358     },
21359
21360     /**
21361      * Resets the status indicator to the default dropNotAllowed value
21362      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21363      */
21364     reset : function(clearGhost){
21365         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21366         this.dropStatus = this.dropNotAllowed;
21367         if(clearGhost){
21368             this.ghost.update("");
21369         }
21370     },
21371
21372     /**
21373      * Updates the contents of the ghost element
21374      * @param {String} html The html that will replace the current innerHTML of the ghost element
21375      */
21376     update : function(html){
21377         if(typeof html == "string"){
21378             this.ghost.update(html);
21379         }else{
21380             this.ghost.update("");
21381             html.style.margin = "0";
21382             this.ghost.dom.appendChild(html);
21383         }
21384         // ensure float = none set?? cant remember why though.
21385         var el = this.ghost.dom.firstChild;
21386                 if(el){
21387                         Roo.fly(el).setStyle('float', 'none');
21388                 }
21389     },
21390     
21391     /**
21392      * Returns the underlying proxy {@link Roo.Layer}
21393      * @return {Roo.Layer} el
21394     */
21395     getEl : function(){
21396         return this.el;
21397     },
21398
21399     /**
21400      * Returns the ghost element
21401      * @return {Roo.Element} el
21402      */
21403     getGhost : function(){
21404         return this.ghost;
21405     },
21406
21407     /**
21408      * Hides the proxy
21409      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21410      */
21411     hide : function(clear){
21412         this.el.hide();
21413         if(clear){
21414             this.reset(true);
21415         }
21416     },
21417
21418     /**
21419      * Stops the repair animation if it's currently running
21420      */
21421     stop : function(){
21422         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21423             this.anim.stop();
21424         }
21425     },
21426
21427     /**
21428      * Displays this proxy
21429      */
21430     show : function(){
21431         this.el.show();
21432     },
21433
21434     /**
21435      * Force the Layer to sync its shadow and shim positions to the element
21436      */
21437     sync : function(){
21438         this.el.sync();
21439     },
21440
21441     /**
21442      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21443      * invalid drop operation by the item being dragged.
21444      * @param {Array} xy The XY position of the element ([x, y])
21445      * @param {Function} callback The function to call after the repair is complete
21446      * @param {Object} scope The scope in which to execute the callback
21447      */
21448     repair : function(xy, callback, scope){
21449         this.callback = callback;
21450         this.scope = scope;
21451         if(xy && this.animRepair !== false){
21452             this.el.addClass("x-dd-drag-repair");
21453             this.el.hideUnders(true);
21454             this.anim = this.el.shift({
21455                 duration: this.repairDuration || .5,
21456                 easing: 'easeOut',
21457                 xy: xy,
21458                 stopFx: true,
21459                 callback: this.afterRepair,
21460                 scope: this
21461             });
21462         }else{
21463             this.afterRepair();
21464         }
21465     },
21466
21467     // private
21468     afterRepair : function(){
21469         this.hide(true);
21470         if(typeof this.callback == "function"){
21471             this.callback.call(this.scope || this);
21472         }
21473         this.callback = null;
21474         this.scope = null;
21475     }
21476 };/*
21477  * Based on:
21478  * Ext JS Library 1.1.1
21479  * Copyright(c) 2006-2007, Ext JS, LLC.
21480  *
21481  * Originally Released Under LGPL - original licence link has changed is not relivant.
21482  *
21483  * Fork - LGPL
21484  * <script type="text/javascript">
21485  */
21486
21487 /**
21488  * @class Roo.dd.DragSource
21489  * @extends Roo.dd.DDProxy
21490  * A simple class that provides the basic implementation needed to make any element draggable.
21491  * @constructor
21492  * @param {String/HTMLElement/Element} el The container element
21493  * @param {Object} config
21494  */
21495 Roo.dd.DragSource = function(el, config){
21496     this.el = Roo.get(el);
21497     this.dragData = {};
21498     
21499     Roo.apply(this, config);
21500     
21501     if(!this.proxy){
21502         this.proxy = new Roo.dd.StatusProxy();
21503     }
21504
21505     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21506           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21507     
21508     this.dragging = false;
21509 };
21510
21511 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21512     /**
21513      * @cfg {String} dropAllowed
21514      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21515      */
21516     dropAllowed : "x-dd-drop-ok",
21517     /**
21518      * @cfg {String} dropNotAllowed
21519      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21520      */
21521     dropNotAllowed : "x-dd-drop-nodrop",
21522
21523     /**
21524      * Returns the data object associated with this drag source
21525      * @return {Object} data An object containing arbitrary data
21526      */
21527     getDragData : function(e){
21528         return this.dragData;
21529     },
21530
21531     // private
21532     onDragEnter : function(e, id){
21533         var target = Roo.dd.DragDropMgr.getDDById(id);
21534         this.cachedTarget = target;
21535         if(this.beforeDragEnter(target, e, id) !== false){
21536             if(target.isNotifyTarget){
21537                 var status = target.notifyEnter(this, e, this.dragData);
21538                 this.proxy.setStatus(status);
21539             }else{
21540                 this.proxy.setStatus(this.dropAllowed);
21541             }
21542             
21543             if(this.afterDragEnter){
21544                 /**
21545                  * An empty function by default, but provided so that you can perform a custom action
21546                  * when the dragged item enters the drop target by providing an implementation.
21547                  * @param {Roo.dd.DragDrop} target The drop target
21548                  * @param {Event} e The event object
21549                  * @param {String} id The id of the dragged element
21550                  * @method afterDragEnter
21551                  */
21552                 this.afterDragEnter(target, e, id);
21553             }
21554         }
21555     },
21556
21557     /**
21558      * An empty function by default, but provided so that you can perform a custom action
21559      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21560      * @param {Roo.dd.DragDrop} target The drop target
21561      * @param {Event} e The event object
21562      * @param {String} id The id of the dragged element
21563      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21564      */
21565     beforeDragEnter : function(target, e, id){
21566         return true;
21567     },
21568
21569     // private
21570     alignElWithMouse: function() {
21571         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21572         this.proxy.sync();
21573     },
21574
21575     // private
21576     onDragOver : function(e, id){
21577         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21578         if(this.beforeDragOver(target, e, id) !== false){
21579             if(target.isNotifyTarget){
21580                 var status = target.notifyOver(this, e, this.dragData);
21581                 this.proxy.setStatus(status);
21582             }
21583
21584             if(this.afterDragOver){
21585                 /**
21586                  * An empty function by default, but provided so that you can perform a custom action
21587                  * while the dragged item is over the drop target by providing an implementation.
21588                  * @param {Roo.dd.DragDrop} target The drop target
21589                  * @param {Event} e The event object
21590                  * @param {String} id The id of the dragged element
21591                  * @method afterDragOver
21592                  */
21593                 this.afterDragOver(target, e, id);
21594             }
21595         }
21596     },
21597
21598     /**
21599      * An empty function by default, but provided so that you can perform a custom action
21600      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21601      * @param {Roo.dd.DragDrop} target The drop target
21602      * @param {Event} e The event object
21603      * @param {String} id The id of the dragged element
21604      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21605      */
21606     beforeDragOver : function(target, e, id){
21607         return true;
21608     },
21609
21610     // private
21611     onDragOut : function(e, id){
21612         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21613         if(this.beforeDragOut(target, e, id) !== false){
21614             if(target.isNotifyTarget){
21615                 target.notifyOut(this, e, this.dragData);
21616             }
21617             this.proxy.reset();
21618             if(this.afterDragOut){
21619                 /**
21620                  * An empty function by default, but provided so that you can perform a custom action
21621                  * after the dragged item is dragged out of the target without dropping.
21622                  * @param {Roo.dd.DragDrop} target The drop target
21623                  * @param {Event} e The event object
21624                  * @param {String} id The id of the dragged element
21625                  * @method afterDragOut
21626                  */
21627                 this.afterDragOut(target, e, id);
21628             }
21629         }
21630         this.cachedTarget = null;
21631     },
21632
21633     /**
21634      * An empty function by default, but provided so that you can perform a custom action before the dragged
21635      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21636      * @param {Roo.dd.DragDrop} target The drop target
21637      * @param {Event} e The event object
21638      * @param {String} id The id of the dragged element
21639      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21640      */
21641     beforeDragOut : function(target, e, id){
21642         return true;
21643     },
21644     
21645     // private
21646     onDragDrop : function(e, id){
21647         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21648         if(this.beforeDragDrop(target, e, id) !== false){
21649             if(target.isNotifyTarget){
21650                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21651                     this.onValidDrop(target, e, id);
21652                 }else{
21653                     this.onInvalidDrop(target, e, id);
21654                 }
21655             }else{
21656                 this.onValidDrop(target, e, id);
21657             }
21658             
21659             if(this.afterDragDrop){
21660                 /**
21661                  * An empty function by default, but provided so that you can perform a custom action
21662                  * after a valid drag drop has occurred by providing an implementation.
21663                  * @param {Roo.dd.DragDrop} target The drop target
21664                  * @param {Event} e The event object
21665                  * @param {String} id The id of the dropped element
21666                  * @method afterDragDrop
21667                  */
21668                 this.afterDragDrop(target, e, id);
21669             }
21670         }
21671         delete this.cachedTarget;
21672     },
21673
21674     /**
21675      * An empty function by default, but provided so that you can perform a custom action before the dragged
21676      * item is dropped onto the target and optionally cancel the onDragDrop.
21677      * @param {Roo.dd.DragDrop} target The drop target
21678      * @param {Event} e The event object
21679      * @param {String} id The id of the dragged element
21680      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21681      */
21682     beforeDragDrop : function(target, e, id){
21683         return true;
21684     },
21685
21686     // private
21687     onValidDrop : function(target, e, id){
21688         this.hideProxy();
21689         if(this.afterValidDrop){
21690             /**
21691              * An empty function by default, but provided so that you can perform a custom action
21692              * after a valid drop has occurred by providing an implementation.
21693              * @param {Object} target The target DD 
21694              * @param {Event} e The event object
21695              * @param {String} id The id of the dropped element
21696              * @method afterInvalidDrop
21697              */
21698             this.afterValidDrop(target, e, id);
21699         }
21700     },
21701
21702     // private
21703     getRepairXY : function(e, data){
21704         return this.el.getXY();  
21705     },
21706
21707     // private
21708     onInvalidDrop : function(target, e, id){
21709         this.beforeInvalidDrop(target, e, id);
21710         if(this.cachedTarget){
21711             if(this.cachedTarget.isNotifyTarget){
21712                 this.cachedTarget.notifyOut(this, e, this.dragData);
21713             }
21714             this.cacheTarget = null;
21715         }
21716         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21717
21718         if(this.afterInvalidDrop){
21719             /**
21720              * An empty function by default, but provided so that you can perform a custom action
21721              * after an invalid drop has occurred by providing an implementation.
21722              * @param {Event} e The event object
21723              * @param {String} id The id of the dropped element
21724              * @method afterInvalidDrop
21725              */
21726             this.afterInvalidDrop(e, id);
21727         }
21728     },
21729
21730     // private
21731     afterRepair : function(){
21732         if(Roo.enableFx){
21733             this.el.highlight(this.hlColor || "c3daf9");
21734         }
21735         this.dragging = false;
21736     },
21737
21738     /**
21739      * An empty function by default, but provided so that you can perform a custom action after an invalid
21740      * drop has occurred.
21741      * @param {Roo.dd.DragDrop} target The drop target
21742      * @param {Event} e The event object
21743      * @param {String} id The id of the dragged element
21744      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21745      */
21746     beforeInvalidDrop : function(target, e, id){
21747         return true;
21748     },
21749
21750     // private
21751     handleMouseDown : function(e){
21752         if(this.dragging) {
21753             return;
21754         }
21755         var data = this.getDragData(e);
21756         if(data && this.onBeforeDrag(data, e) !== false){
21757             this.dragData = data;
21758             this.proxy.stop();
21759             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21760         } 
21761     },
21762
21763     /**
21764      * An empty function by default, but provided so that you can perform a custom action before the initial
21765      * drag event begins and optionally cancel it.
21766      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21767      * @param {Event} e The event object
21768      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21769      */
21770     onBeforeDrag : function(data, e){
21771         return true;
21772     },
21773
21774     /**
21775      * An empty function by default, but provided so that you can perform a custom action once the initial
21776      * drag event has begun.  The drag cannot be canceled from this function.
21777      * @param {Number} x The x position of the click on the dragged object
21778      * @param {Number} y The y position of the click on the dragged object
21779      */
21780     onStartDrag : Roo.emptyFn,
21781
21782     // private - YUI override
21783     startDrag : function(x, y){
21784         this.proxy.reset();
21785         this.dragging = true;
21786         this.proxy.update("");
21787         this.onInitDrag(x, y);
21788         this.proxy.show();
21789     },
21790
21791     // private
21792     onInitDrag : function(x, y){
21793         var clone = this.el.dom.cloneNode(true);
21794         clone.id = Roo.id(); // prevent duplicate ids
21795         this.proxy.update(clone);
21796         this.onStartDrag(x, y);
21797         return true;
21798     },
21799
21800     /**
21801      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21802      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21803      */
21804     getProxy : function(){
21805         return this.proxy;  
21806     },
21807
21808     /**
21809      * Hides the drag source's {@link Roo.dd.StatusProxy}
21810      */
21811     hideProxy : function(){
21812         this.proxy.hide();  
21813         this.proxy.reset(true);
21814         this.dragging = false;
21815     },
21816
21817     // private
21818     triggerCacheRefresh : function(){
21819         Roo.dd.DDM.refreshCache(this.groups);
21820     },
21821
21822     // private - override to prevent hiding
21823     b4EndDrag: function(e) {
21824     },
21825
21826     // private - override to prevent moving
21827     endDrag : function(e){
21828         this.onEndDrag(this.dragData, e);
21829     },
21830
21831     // private
21832     onEndDrag : function(data, e){
21833     },
21834     
21835     // private - pin to cursor
21836     autoOffset : function(x, y) {
21837         this.setDelta(-12, -20);
21838     }    
21839 });/*
21840  * Based on:
21841  * Ext JS Library 1.1.1
21842  * Copyright(c) 2006-2007, Ext JS, LLC.
21843  *
21844  * Originally Released Under LGPL - original licence link has changed is not relivant.
21845  *
21846  * Fork - LGPL
21847  * <script type="text/javascript">
21848  */
21849
21850
21851 /**
21852  * @class Roo.dd.DropTarget
21853  * @extends Roo.dd.DDTarget
21854  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21855  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21856  * @constructor
21857  * @param {String/HTMLElement/Element} el The container element
21858  * @param {Object} config
21859  */
21860 Roo.dd.DropTarget = function(el, config){
21861     this.el = Roo.get(el);
21862     
21863     var listeners = false; ;
21864     if (config && config.listeners) {
21865         listeners= config.listeners;
21866         delete config.listeners;
21867     }
21868     Roo.apply(this, config);
21869     
21870     if(this.containerScroll){
21871         Roo.dd.ScrollManager.register(this.el);
21872     }
21873     this.addEvents( {
21874          /**
21875          * @scope Roo.dd.DropTarget
21876          */
21877          
21878          /**
21879          * @event enter
21880          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21881          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21882          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21883          * 
21884          * IMPORTANT : it should set this.overClass and this.dropAllowed
21885          * 
21886          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21887          * @param {Event} e The event
21888          * @param {Object} data An object containing arbitrary data supplied by the drag source
21889          */
21890         "enter" : true,
21891         
21892          /**
21893          * @event over
21894          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21895          * This method will be called on every mouse movement while the drag source is over the drop target.
21896          * This default implementation simply returns the dropAllowed config value.
21897          * 
21898          * IMPORTANT : it should set this.dropAllowed
21899          * 
21900          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21901          * @param {Event} e The event
21902          * @param {Object} data An object containing arbitrary data supplied by the drag source
21903          
21904          */
21905         "over" : true,
21906         /**
21907          * @event out
21908          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21909          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21910          * overClass (if any) from the drop element.
21911          * 
21912          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21913          * @param {Event} e The event
21914          * @param {Object} data An object containing arbitrary data supplied by the drag source
21915          */
21916          "out" : true,
21917          
21918         /**
21919          * @event drop
21920          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21921          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21922          * implementation that does something to process the drop event and returns true so that the drag source's
21923          * repair action does not run.
21924          * 
21925          * IMPORTANT : it should set this.success
21926          * 
21927          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21928          * @param {Event} e The event
21929          * @param {Object} data An object containing arbitrary data supplied by the drag source
21930         */
21931          "drop" : true
21932     });
21933             
21934      
21935     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21936         this.el.dom, 
21937         this.ddGroup || this.group,
21938         {
21939             isTarget: true,
21940             listeners : listeners || {} 
21941            
21942         
21943         }
21944     );
21945
21946 };
21947
21948 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21949     /**
21950      * @cfg {String} overClass
21951      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21952      */
21953      /**
21954      * @cfg {String} ddGroup
21955      * The drag drop group to handle drop events for
21956      */
21957      
21958     /**
21959      * @cfg {String} dropAllowed
21960      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21961      */
21962     dropAllowed : "x-dd-drop-ok",
21963     /**
21964      * @cfg {String} dropNotAllowed
21965      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21966      */
21967     dropNotAllowed : "x-dd-drop-nodrop",
21968     /**
21969      * @cfg {boolean} success
21970      * set this after drop listener.. 
21971      */
21972     success : false,
21973     /**
21974      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21975      * if the drop point is valid for over/enter..
21976      */
21977     valid : false,
21978     // private
21979     isTarget : true,
21980
21981     // private
21982     isNotifyTarget : true,
21983     
21984     /**
21985      * @hide
21986      */
21987     notifyEnter : function(dd, e, data)
21988     {
21989         this.valid = true;
21990         this.fireEvent('enter', dd, e, data);
21991         if(this.overClass){
21992             this.el.addClass(this.overClass);
21993         }
21994         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21995             this.valid ? this.dropAllowed : this.dropNotAllowed
21996         );
21997     },
21998
21999     /**
22000      * @hide
22001      */
22002     notifyOver : function(dd, e, data)
22003     {
22004         this.valid = true;
22005         this.fireEvent('over', dd, e, data);
22006         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22007             this.valid ? this.dropAllowed : this.dropNotAllowed
22008         );
22009     },
22010
22011     /**
22012      * @hide
22013      */
22014     notifyOut : function(dd, e, data)
22015     {
22016         this.fireEvent('out', dd, e, data);
22017         if(this.overClass){
22018             this.el.removeClass(this.overClass);
22019         }
22020     },
22021
22022     /**
22023      * @hide
22024      */
22025     notifyDrop : function(dd, e, data)
22026     {
22027         this.success = false;
22028         this.fireEvent('drop', dd, e, data);
22029         return this.success;
22030     }
22031 });/*
22032  * Based on:
22033  * Ext JS Library 1.1.1
22034  * Copyright(c) 2006-2007, Ext JS, LLC.
22035  *
22036  * Originally Released Under LGPL - original licence link has changed is not relivant.
22037  *
22038  * Fork - LGPL
22039  * <script type="text/javascript">
22040  */
22041
22042
22043 /**
22044  * @class Roo.dd.DragZone
22045  * @extends Roo.dd.DragSource
22046  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22047  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22048  * @constructor
22049  * @param {String/HTMLElement/Element} el The container element
22050  * @param {Object} config
22051  */
22052 Roo.dd.DragZone = function(el, config){
22053     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22054     if(this.containerScroll){
22055         Roo.dd.ScrollManager.register(this.el);
22056     }
22057 };
22058
22059 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22060     /**
22061      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22062      * for auto scrolling during drag operations.
22063      */
22064     /**
22065      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22066      * method after a failed drop (defaults to "c3daf9" - light blue)
22067      */
22068
22069     /**
22070      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22071      * for a valid target to drag based on the mouse down. Override this method
22072      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22073      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22074      * @param {EventObject} e The mouse down event
22075      * @return {Object} The dragData
22076      */
22077     getDragData : function(e){
22078         return Roo.dd.Registry.getHandleFromEvent(e);
22079     },
22080     
22081     /**
22082      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22083      * this.dragData.ddel
22084      * @param {Number} x The x position of the click on the dragged object
22085      * @param {Number} y The y position of the click on the dragged object
22086      * @return {Boolean} true to continue the drag, false to cancel
22087      */
22088     onInitDrag : function(x, y){
22089         this.proxy.update(this.dragData.ddel.cloneNode(true));
22090         this.onStartDrag(x, y);
22091         return true;
22092     },
22093     
22094     /**
22095      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22096      */
22097     afterRepair : function(){
22098         if(Roo.enableFx){
22099             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22100         }
22101         this.dragging = false;
22102     },
22103
22104     /**
22105      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22106      * the XY of this.dragData.ddel
22107      * @param {EventObject} e The mouse up event
22108      * @return {Array} The xy location (e.g. [100, 200])
22109      */
22110     getRepairXY : function(e){
22111         return Roo.Element.fly(this.dragData.ddel).getXY();  
22112     }
22113 });/*
22114  * Based on:
22115  * Ext JS Library 1.1.1
22116  * Copyright(c) 2006-2007, Ext JS, LLC.
22117  *
22118  * Originally Released Under LGPL - original licence link has changed is not relivant.
22119  *
22120  * Fork - LGPL
22121  * <script type="text/javascript">
22122  */
22123 /**
22124  * @class Roo.dd.DropZone
22125  * @extends Roo.dd.DropTarget
22126  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22127  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22128  * @constructor
22129  * @param {String/HTMLElement/Element} el The container element
22130  * @param {Object} config
22131  */
22132 Roo.dd.DropZone = function(el, config){
22133     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22134 };
22135
22136 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22137     /**
22138      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22139      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22140      * provide your own custom lookup.
22141      * @param {Event} e The event
22142      * @return {Object} data The custom data
22143      */
22144     getTargetFromEvent : function(e){
22145         return Roo.dd.Registry.getTargetFromEvent(e);
22146     },
22147
22148     /**
22149      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22150      * that it has registered.  This method has no default implementation and should be overridden to provide
22151      * node-specific processing if necessary.
22152      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22153      * {@link #getTargetFromEvent} for this node)
22154      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22155      * @param {Event} e The event
22156      * @param {Object} data An object containing arbitrary data supplied by the drag source
22157      */
22158     onNodeEnter : function(n, dd, e, data){
22159         
22160     },
22161
22162     /**
22163      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22164      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22165      * overridden to provide the proper feedback.
22166      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22167      * {@link #getTargetFromEvent} for this node)
22168      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22169      * @param {Event} e The event
22170      * @param {Object} data An object containing arbitrary data supplied by the drag source
22171      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22172      * underlying {@link Roo.dd.StatusProxy} can be updated
22173      */
22174     onNodeOver : function(n, dd, e, data){
22175         return this.dropAllowed;
22176     },
22177
22178     /**
22179      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22180      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22181      * node-specific processing if necessary.
22182      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22183      * {@link #getTargetFromEvent} for this node)
22184      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22185      * @param {Event} e The event
22186      * @param {Object} data An object containing arbitrary data supplied by the drag source
22187      */
22188     onNodeOut : function(n, dd, e, data){
22189         
22190     },
22191
22192     /**
22193      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22194      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22195      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22196      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22197      * {@link #getTargetFromEvent} for this node)
22198      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22199      * @param {Event} e The event
22200      * @param {Object} data An object containing arbitrary data supplied by the drag source
22201      * @return {Boolean} True if the drop was valid, else false
22202      */
22203     onNodeDrop : function(n, dd, e, data){
22204         return false;
22205     },
22206
22207     /**
22208      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22209      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22210      * it should be overridden to provide the proper feedback if necessary.
22211      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22212      * @param {Event} e The event
22213      * @param {Object} data An object containing arbitrary data supplied by the drag source
22214      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22215      * underlying {@link Roo.dd.StatusProxy} can be updated
22216      */
22217     onContainerOver : function(dd, e, data){
22218         return this.dropNotAllowed;
22219     },
22220
22221     /**
22222      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22223      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22224      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22225      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22226      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22227      * @param {Event} e The event
22228      * @param {Object} data An object containing arbitrary data supplied by the drag source
22229      * @return {Boolean} True if the drop was valid, else false
22230      */
22231     onContainerDrop : function(dd, e, data){
22232         return false;
22233     },
22234
22235     /**
22236      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22237      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22238      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22239      * you should override this method and provide a custom implementation.
22240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22241      * @param {Event} e The event
22242      * @param {Object} data An object containing arbitrary data supplied by the drag source
22243      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22244      * underlying {@link Roo.dd.StatusProxy} can be updated
22245      */
22246     notifyEnter : function(dd, e, data){
22247         return this.dropNotAllowed;
22248     },
22249
22250     /**
22251      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22252      * This method will be called on every mouse movement while the drag source is over the drop zone.
22253      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22254      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22255      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22256      * registered node, it will call {@link #onContainerOver}.
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     notifyOver : function(dd, e, data){
22264         var n = this.getTargetFromEvent(e);
22265         if(!n){ // not over valid drop target
22266             if(this.lastOverNode){
22267                 this.onNodeOut(this.lastOverNode, dd, e, data);
22268                 this.lastOverNode = null;
22269             }
22270             return this.onContainerOver(dd, e, data);
22271         }
22272         if(this.lastOverNode != n){
22273             if(this.lastOverNode){
22274                 this.onNodeOut(this.lastOverNode, dd, e, data);
22275             }
22276             this.onNodeEnter(n, dd, e, data);
22277             this.lastOverNode = n;
22278         }
22279         return this.onNodeOver(n, dd, e, data);
22280     },
22281
22282     /**
22283      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22284      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22285      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22286      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22287      * @param {Event} e The event
22288      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22289      */
22290     notifyOut : function(dd, e, data){
22291         if(this.lastOverNode){
22292             this.onNodeOut(this.lastOverNode, dd, e, data);
22293             this.lastOverNode = null;
22294         }
22295     },
22296
22297     /**
22298      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22299      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22300      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22301      * otherwise it will call {@link #onContainerDrop}.
22302      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22303      * @param {Event} e The event
22304      * @param {Object} data An object containing arbitrary data supplied by the drag source
22305      * @return {Boolean} True if the drop was valid, else false
22306      */
22307     notifyDrop : function(dd, e, data){
22308         if(this.lastOverNode){
22309             this.onNodeOut(this.lastOverNode, dd, e, data);
22310             this.lastOverNode = null;
22311         }
22312         var n = this.getTargetFromEvent(e);
22313         return n ?
22314             this.onNodeDrop(n, dd, e, data) :
22315             this.onContainerDrop(dd, e, data);
22316     },
22317
22318     // private
22319     triggerCacheRefresh : function(){
22320         Roo.dd.DDM.refreshCache(this.groups);
22321     }  
22322 });/*
22323  * Based on:
22324  * Ext JS Library 1.1.1
22325  * Copyright(c) 2006-2007, Ext JS, LLC.
22326  *
22327  * Originally Released Under LGPL - original licence link has changed is not relivant.
22328  *
22329  * Fork - LGPL
22330  * <script type="text/javascript">
22331  */
22332
22333
22334 /**
22335  * @class Roo.data.SortTypes
22336  * @singleton
22337  * Defines the default sorting (casting?) comparison functions used when sorting data.
22338  */
22339 Roo.data.SortTypes = {
22340     /**
22341      * Default sort that does nothing
22342      * @param {Mixed} s The value being converted
22343      * @return {Mixed} The comparison value
22344      */
22345     none : function(s){
22346         return s;
22347     },
22348     
22349     /**
22350      * The regular expression used to strip tags
22351      * @type {RegExp}
22352      * @property
22353      */
22354     stripTagsRE : /<\/?[^>]+>/gi,
22355     
22356     /**
22357      * Strips all HTML tags to sort on text only
22358      * @param {Mixed} s The value being converted
22359      * @return {String} The comparison value
22360      */
22361     asText : function(s){
22362         return String(s).replace(this.stripTagsRE, "");
22363     },
22364     
22365     /**
22366      * Strips all HTML tags to sort on text only - Case insensitive
22367      * @param {Mixed} s The value being converted
22368      * @return {String} The comparison value
22369      */
22370     asUCText : function(s){
22371         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22372     },
22373     
22374     /**
22375      * Case insensitive string
22376      * @param {Mixed} s The value being converted
22377      * @return {String} The comparison value
22378      */
22379     asUCString : function(s) {
22380         return String(s).toUpperCase();
22381     },
22382     
22383     /**
22384      * Date sorting
22385      * @param {Mixed} s The value being converted
22386      * @return {Number} The comparison value
22387      */
22388     asDate : function(s) {
22389         if(!s){
22390             return 0;
22391         }
22392         if(s instanceof Date){
22393             return s.getTime();
22394         }
22395         return Date.parse(String(s));
22396     },
22397     
22398     /**
22399      * Float sorting
22400      * @param {Mixed} s The value being converted
22401      * @return {Float} The comparison value
22402      */
22403     asFloat : function(s) {
22404         var val = parseFloat(String(s).replace(/,/g, ""));
22405         if(isNaN(val)) {
22406             val = 0;
22407         }
22408         return val;
22409     },
22410     
22411     /**
22412      * Integer sorting
22413      * @param {Mixed} s The value being converted
22414      * @return {Number} The comparison value
22415      */
22416     asInt : function(s) {
22417         var val = parseInt(String(s).replace(/,/g, ""));
22418         if(isNaN(val)) {
22419             val = 0;
22420         }
22421         return val;
22422     }
22423 };/*
22424  * Based on:
22425  * Ext JS Library 1.1.1
22426  * Copyright(c) 2006-2007, Ext JS, LLC.
22427  *
22428  * Originally Released Under LGPL - original licence link has changed is not relivant.
22429  *
22430  * Fork - LGPL
22431  * <script type="text/javascript">
22432  */
22433
22434 /**
22435 * @class Roo.data.Record
22436  * Instances of this class encapsulate both record <em>definition</em> information, and record
22437  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22438  * to access Records cached in an {@link Roo.data.Store} object.<br>
22439  * <p>
22440  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22441  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22442  * objects.<br>
22443  * <p>
22444  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22445  * @constructor
22446  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22447  * {@link #create}. The parameters are the same.
22448  * @param {Array} data An associative Array of data values keyed by the field name.
22449  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22450  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22451  * not specified an integer id is generated.
22452  */
22453 Roo.data.Record = function(data, id){
22454     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22455     this.data = data;
22456 };
22457
22458 /**
22459  * Generate a constructor for a specific record layout.
22460  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22461  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22462  * Each field definition object may contain the following properties: <ul>
22463  * <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,
22464  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22465  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22466  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22467  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22468  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22469  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22470  * this may be omitted.</p></li>
22471  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22472  * <ul><li>auto (Default, implies no conversion)</li>
22473  * <li>string</li>
22474  * <li>int</li>
22475  * <li>float</li>
22476  * <li>boolean</li>
22477  * <li>date</li></ul></p></li>
22478  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22479  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22480  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22481  * by the Reader into an object that will be stored in the Record. It is passed the
22482  * following parameters:<ul>
22483  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22484  * </ul></p></li>
22485  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22486  * </ul>
22487  * <br>usage:<br><pre><code>
22488 var TopicRecord = Roo.data.Record.create(
22489     {name: 'title', mapping: 'topic_title'},
22490     {name: 'author', mapping: 'username'},
22491     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22492     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22493     {name: 'lastPoster', mapping: 'user2'},
22494     {name: 'excerpt', mapping: 'post_text'}
22495 );
22496
22497 var myNewRecord = new TopicRecord({
22498     title: 'Do my job please',
22499     author: 'noobie',
22500     totalPosts: 1,
22501     lastPost: new Date(),
22502     lastPoster: 'Animal',
22503     excerpt: 'No way dude!'
22504 });
22505 myStore.add(myNewRecord);
22506 </code></pre>
22507  * @method create
22508  * @static
22509  */
22510 Roo.data.Record.create = function(o){
22511     var f = function(){
22512         f.superclass.constructor.apply(this, arguments);
22513     };
22514     Roo.extend(f, Roo.data.Record);
22515     var p = f.prototype;
22516     p.fields = new Roo.util.MixedCollection(false, function(field){
22517         return field.name;
22518     });
22519     for(var i = 0, len = o.length; i < len; i++){
22520         p.fields.add(new Roo.data.Field(o[i]));
22521     }
22522     f.getField = function(name){
22523         return p.fields.get(name);  
22524     };
22525     return f;
22526 };
22527
22528 Roo.data.Record.AUTO_ID = 1000;
22529 Roo.data.Record.EDIT = 'edit';
22530 Roo.data.Record.REJECT = 'reject';
22531 Roo.data.Record.COMMIT = 'commit';
22532
22533 Roo.data.Record.prototype = {
22534     /**
22535      * Readonly flag - true if this record has been modified.
22536      * @type Boolean
22537      */
22538     dirty : false,
22539     editing : false,
22540     error: null,
22541     modified: null,
22542
22543     // private
22544     join : function(store){
22545         this.store = store;
22546     },
22547
22548     /**
22549      * Set the named field to the specified value.
22550      * @param {String} name The name of the field to set.
22551      * @param {Object} value The value to set the field to.
22552      */
22553     set : function(name, value){
22554         if(this.data[name] == value){
22555             return;
22556         }
22557         this.dirty = true;
22558         if(!this.modified){
22559             this.modified = {};
22560         }
22561         if(typeof this.modified[name] == 'undefined'){
22562             this.modified[name] = this.data[name];
22563         }
22564         this.data[name] = value;
22565         if(!this.editing && this.store){
22566             this.store.afterEdit(this);
22567         }       
22568     },
22569
22570     /**
22571      * Get the value of the named field.
22572      * @param {String} name The name of the field to get the value of.
22573      * @return {Object} The value of the field.
22574      */
22575     get : function(name){
22576         return this.data[name]; 
22577     },
22578
22579     // private
22580     beginEdit : function(){
22581         this.editing = true;
22582         this.modified = {}; 
22583     },
22584
22585     // private
22586     cancelEdit : function(){
22587         this.editing = false;
22588         delete this.modified;
22589     },
22590
22591     // private
22592     endEdit : function(){
22593         this.editing = false;
22594         if(this.dirty && this.store){
22595             this.store.afterEdit(this);
22596         }
22597     },
22598
22599     /**
22600      * Usually called by the {@link Roo.data.Store} which owns the Record.
22601      * Rejects all changes made to the Record since either creation, or the last commit operation.
22602      * Modified fields are reverted to their original values.
22603      * <p>
22604      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22605      * of reject operations.
22606      */
22607     reject : function(){
22608         var m = this.modified;
22609         for(var n in m){
22610             if(typeof m[n] != "function"){
22611                 this.data[n] = m[n];
22612             }
22613         }
22614         this.dirty = false;
22615         delete this.modified;
22616         this.editing = false;
22617         if(this.store){
22618             this.store.afterReject(this);
22619         }
22620     },
22621
22622     /**
22623      * Usually called by the {@link Roo.data.Store} which owns the Record.
22624      * Commits all changes made to the Record since either creation, or the last commit operation.
22625      * <p>
22626      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22627      * of commit operations.
22628      */
22629     commit : function(){
22630         this.dirty = false;
22631         delete this.modified;
22632         this.editing = false;
22633         if(this.store){
22634             this.store.afterCommit(this);
22635         }
22636     },
22637
22638     // private
22639     hasError : function(){
22640         return this.error != null;
22641     },
22642
22643     // private
22644     clearError : function(){
22645         this.error = null;
22646     },
22647
22648     /**
22649      * Creates a copy of this record.
22650      * @param {String} id (optional) A new record id if you don't want to use this record's id
22651      * @return {Record}
22652      */
22653     copy : function(newId) {
22654         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22655     }
22656 };/*
22657  * Based on:
22658  * Ext JS Library 1.1.1
22659  * Copyright(c) 2006-2007, Ext JS, LLC.
22660  *
22661  * Originally Released Under LGPL - original licence link has changed is not relivant.
22662  *
22663  * Fork - LGPL
22664  * <script type="text/javascript">
22665  */
22666
22667
22668
22669 /**
22670  * @class Roo.data.Store
22671  * @extends Roo.util.Observable
22672  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22673  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22674  * <p>
22675  * 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
22676  * has no knowledge of the format of the data returned by the Proxy.<br>
22677  * <p>
22678  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22679  * instances from the data object. These records are cached and made available through accessor functions.
22680  * @constructor
22681  * Creates a new Store.
22682  * @param {Object} config A config object containing the objects needed for the Store to access data,
22683  * and read the data into Records.
22684  */
22685 Roo.data.Store = function(config){
22686     this.data = new Roo.util.MixedCollection(false);
22687     this.data.getKey = function(o){
22688         return o.id;
22689     };
22690     this.baseParams = {};
22691     // private
22692     this.paramNames = {
22693         "start" : "start",
22694         "limit" : "limit",
22695         "sort" : "sort",
22696         "dir" : "dir",
22697         "multisort" : "_multisort"
22698     };
22699
22700     if(config && config.data){
22701         this.inlineData = config.data;
22702         delete config.data;
22703     }
22704
22705     Roo.apply(this, config);
22706     
22707     if(this.reader){ // reader passed
22708         this.reader = Roo.factory(this.reader, Roo.data);
22709         this.reader.xmodule = this.xmodule || false;
22710         if(!this.recordType){
22711             this.recordType = this.reader.recordType;
22712         }
22713         if(this.reader.onMetaChange){
22714             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22715         }
22716     }
22717
22718     if(this.recordType){
22719         this.fields = this.recordType.prototype.fields;
22720     }
22721     this.modified = [];
22722
22723     this.addEvents({
22724         /**
22725          * @event datachanged
22726          * Fires when the data cache has changed, and a widget which is using this Store
22727          * as a Record cache should refresh its view.
22728          * @param {Store} this
22729          */
22730         datachanged : true,
22731         /**
22732          * @event metachange
22733          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22734          * @param {Store} this
22735          * @param {Object} meta The JSON metadata
22736          */
22737         metachange : true,
22738         /**
22739          * @event add
22740          * Fires when Records have been added to the Store
22741          * @param {Store} this
22742          * @param {Roo.data.Record[]} records The array of Records added
22743          * @param {Number} index The index at which the record(s) were added
22744          */
22745         add : true,
22746         /**
22747          * @event remove
22748          * Fires when a Record has been removed from the Store
22749          * @param {Store} this
22750          * @param {Roo.data.Record} record The Record that was removed
22751          * @param {Number} index The index at which the record was removed
22752          */
22753         remove : true,
22754         /**
22755          * @event update
22756          * Fires when a Record has been updated
22757          * @param {Store} this
22758          * @param {Roo.data.Record} record The Record that was updated
22759          * @param {String} operation The update operation being performed.  Value may be one of:
22760          * <pre><code>
22761  Roo.data.Record.EDIT
22762  Roo.data.Record.REJECT
22763  Roo.data.Record.COMMIT
22764          * </code></pre>
22765          */
22766         update : true,
22767         /**
22768          * @event clear
22769          * Fires when the data cache has been cleared.
22770          * @param {Store} this
22771          */
22772         clear : true,
22773         /**
22774          * @event beforeload
22775          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22776          * the load action will be canceled.
22777          * @param {Store} this
22778          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22779          */
22780         beforeload : true,
22781         /**
22782          * @event beforeloadadd
22783          * Fires after a new set of Records has been loaded.
22784          * @param {Store} this
22785          * @param {Roo.data.Record[]} records The Records that were loaded
22786          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22787          */
22788         beforeloadadd : true,
22789         /**
22790          * @event load
22791          * Fires after a new set of Records has been loaded, before they are added to the store.
22792          * @param {Store} this
22793          * @param {Roo.data.Record[]} records The Records that were loaded
22794          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22795          * @params {Object} return from reader
22796          */
22797         load : true,
22798         /**
22799          * @event loadexception
22800          * Fires if an exception occurs in the Proxy during loading.
22801          * Called with the signature of the Proxy's "loadexception" event.
22802          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22803          * 
22804          * @param {Proxy} 
22805          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22806          * @param {Object} load options 
22807          * @param {Object} jsonData from your request (normally this contains the Exception)
22808          */
22809         loadexception : true
22810     });
22811     
22812     if(this.proxy){
22813         this.proxy = Roo.factory(this.proxy, Roo.data);
22814         this.proxy.xmodule = this.xmodule || false;
22815         this.relayEvents(this.proxy,  ["loadexception"]);
22816     }
22817     this.sortToggle = {};
22818     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22819
22820     Roo.data.Store.superclass.constructor.call(this);
22821
22822     if(this.inlineData){
22823         this.loadData(this.inlineData);
22824         delete this.inlineData;
22825     }
22826 };
22827
22828 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22829      /**
22830     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22831     * without a remote query - used by combo/forms at present.
22832     */
22833     
22834     /**
22835     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22836     */
22837     /**
22838     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22839     */
22840     /**
22841     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22842     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22843     */
22844     /**
22845     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22846     * on any HTTP request
22847     */
22848     /**
22849     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22850     */
22851     /**
22852     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22853     */
22854     multiSort: false,
22855     /**
22856     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22857     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22858     */
22859     remoteSort : false,
22860
22861     /**
22862     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22863      * loaded or when a record is removed. (defaults to false).
22864     */
22865     pruneModifiedRecords : false,
22866
22867     // private
22868     lastOptions : null,
22869
22870     /**
22871      * Add Records to the Store and fires the add event.
22872      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22873      */
22874     add : function(records){
22875         records = [].concat(records);
22876         for(var i = 0, len = records.length; i < len; i++){
22877             records[i].join(this);
22878         }
22879         var index = this.data.length;
22880         this.data.addAll(records);
22881         this.fireEvent("add", this, records, index);
22882     },
22883
22884     /**
22885      * Remove a Record from the Store and fires the remove event.
22886      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22887      */
22888     remove : function(record){
22889         var index = this.data.indexOf(record);
22890         this.data.removeAt(index);
22891         if(this.pruneModifiedRecords){
22892             this.modified.remove(record);
22893         }
22894         this.fireEvent("remove", this, record, index);
22895     },
22896
22897     /**
22898      * Remove all Records from the Store and fires the clear event.
22899      */
22900     removeAll : function(){
22901         this.data.clear();
22902         if(this.pruneModifiedRecords){
22903             this.modified = [];
22904         }
22905         this.fireEvent("clear", this);
22906     },
22907
22908     /**
22909      * Inserts Records to the Store at the given index and fires the add event.
22910      * @param {Number} index The start index at which to insert the passed Records.
22911      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22912      */
22913     insert : function(index, records){
22914         records = [].concat(records);
22915         for(var i = 0, len = records.length; i < len; i++){
22916             this.data.insert(index, records[i]);
22917             records[i].join(this);
22918         }
22919         this.fireEvent("add", this, records, index);
22920     },
22921
22922     /**
22923      * Get the index within the cache of the passed Record.
22924      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22925      * @return {Number} The index of the passed Record. Returns -1 if not found.
22926      */
22927     indexOf : function(record){
22928         return this.data.indexOf(record);
22929     },
22930
22931     /**
22932      * Get the index within the cache of the Record with the passed id.
22933      * @param {String} id The id of the Record to find.
22934      * @return {Number} The index of the Record. Returns -1 if not found.
22935      */
22936     indexOfId : function(id){
22937         return this.data.indexOfKey(id);
22938     },
22939
22940     /**
22941      * Get the Record with the specified id.
22942      * @param {String} id The id of the Record to find.
22943      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22944      */
22945     getById : function(id){
22946         return this.data.key(id);
22947     },
22948
22949     /**
22950      * Get the Record at the specified index.
22951      * @param {Number} index The index of the Record to find.
22952      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22953      */
22954     getAt : function(index){
22955         return this.data.itemAt(index);
22956     },
22957
22958     /**
22959      * Returns a range of Records between specified indices.
22960      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22961      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22962      * @return {Roo.data.Record[]} An array of Records
22963      */
22964     getRange : function(start, end){
22965         return this.data.getRange(start, end);
22966     },
22967
22968     // private
22969     storeOptions : function(o){
22970         o = Roo.apply({}, o);
22971         delete o.callback;
22972         delete o.scope;
22973         this.lastOptions = o;
22974     },
22975
22976     /**
22977      * Loads the Record cache from the configured Proxy using the configured Reader.
22978      * <p>
22979      * If using remote paging, then the first load call must specify the <em>start</em>
22980      * and <em>limit</em> properties in the options.params property to establish the initial
22981      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22982      * <p>
22983      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22984      * and this call will return before the new data has been loaded. Perform any post-processing
22985      * in a callback function, or in a "load" event handler.</strong>
22986      * <p>
22987      * @param {Object} options An object containing properties which control loading options:<ul>
22988      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22989      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22990      * passed the following arguments:<ul>
22991      * <li>r : Roo.data.Record[]</li>
22992      * <li>options: Options object from the load call</li>
22993      * <li>success: Boolean success indicator</li></ul></li>
22994      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22995      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22996      * </ul>
22997      */
22998     load : function(options){
22999         options = options || {};
23000         if(this.fireEvent("beforeload", this, options) !== false){
23001             this.storeOptions(options);
23002             var p = Roo.apply(options.params || {}, this.baseParams);
23003             // if meta was not loaded from remote source.. try requesting it.
23004             if (!this.reader.metaFromRemote) {
23005                 p._requestMeta = 1;
23006             }
23007             if(this.sortInfo && this.remoteSort){
23008                 var pn = this.paramNames;
23009                 p[pn["sort"]] = this.sortInfo.field;
23010                 p[pn["dir"]] = this.sortInfo.direction;
23011             }
23012             if (this.multiSort) {
23013                 var pn = this.paramNames;
23014                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23015             }
23016             
23017             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23018         }
23019     },
23020
23021     /**
23022      * Reloads the Record cache from the configured Proxy using the configured Reader and
23023      * the options from the last load operation performed.
23024      * @param {Object} options (optional) An object containing properties which may override the options
23025      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23026      * the most recently used options are reused).
23027      */
23028     reload : function(options){
23029         this.load(Roo.applyIf(options||{}, this.lastOptions));
23030     },
23031
23032     // private
23033     // Called as a callback by the Reader during a load operation.
23034     loadRecords : function(o, options, success){
23035         if(!o || success === false){
23036             if(success !== false){
23037                 this.fireEvent("load", this, [], options, o);
23038             }
23039             if(options.callback){
23040                 options.callback.call(options.scope || this, [], options, false);
23041             }
23042             return;
23043         }
23044         // if data returned failure - throw an exception.
23045         if (o.success === false) {
23046             // show a message if no listener is registered.
23047             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23048                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23049             }
23050             // loadmask wil be hooked into this..
23051             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23052             return;
23053         }
23054         var r = o.records, t = o.totalRecords || r.length;
23055         
23056         this.fireEvent("beforeloadadd", this, r, options, o);
23057         
23058         if(!options || options.add !== true){
23059             if(this.pruneModifiedRecords){
23060                 this.modified = [];
23061             }
23062             for(var i = 0, len = r.length; i < len; i++){
23063                 r[i].join(this);
23064             }
23065             if(this.snapshot){
23066                 this.data = this.snapshot;
23067                 delete this.snapshot;
23068             }
23069             this.data.clear();
23070             this.data.addAll(r);
23071             this.totalLength = t;
23072             this.applySort();
23073             this.fireEvent("datachanged", this);
23074         }else{
23075             this.totalLength = Math.max(t, this.data.length+r.length);
23076             this.add(r);
23077         }
23078         this.fireEvent("load", this, r, options, o);
23079         if(options.callback){
23080             options.callback.call(options.scope || this, r, options, true);
23081         }
23082     },
23083
23084
23085     /**
23086      * Loads data from a passed data block. A Reader which understands the format of the data
23087      * must have been configured in the constructor.
23088      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23089      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23090      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23091      */
23092     loadData : function(o, append){
23093         var r = this.reader.readRecords(o);
23094         this.loadRecords(r, {add: append}, true);
23095     },
23096
23097     /**
23098      * Gets the number of cached records.
23099      * <p>
23100      * <em>If using paging, this may not be the total size of the dataset. If the data object
23101      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23102      * the data set size</em>
23103      */
23104     getCount : function(){
23105         return this.data.length || 0;
23106     },
23107
23108     /**
23109      * Gets the total number of records in the dataset as returned by the server.
23110      * <p>
23111      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23112      * the dataset size</em>
23113      */
23114     getTotalCount : function(){
23115         return this.totalLength || 0;
23116     },
23117
23118     /**
23119      * Returns the sort state of the Store as an object with two properties:
23120      * <pre><code>
23121  field {String} The name of the field by which the Records are sorted
23122  direction {String} The sort order, "ASC" or "DESC"
23123      * </code></pre>
23124      */
23125     getSortState : function(){
23126         return this.sortInfo;
23127     },
23128
23129     // private
23130     applySort : function(){
23131         if(this.sortInfo && !this.remoteSort){
23132             var s = this.sortInfo, f = s.field;
23133             var st = this.fields.get(f).sortType;
23134             var fn = function(r1, r2){
23135                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23136                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23137             };
23138             this.data.sort(s.direction, fn);
23139             if(this.snapshot && this.snapshot != this.data){
23140                 this.snapshot.sort(s.direction, fn);
23141             }
23142         }
23143     },
23144
23145     /**
23146      * Sets the default sort column and order to be used by the next load operation.
23147      * @param {String} fieldName The name of the field to sort by.
23148      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23149      */
23150     setDefaultSort : function(field, dir){
23151         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23152     },
23153
23154     /**
23155      * Sort the Records.
23156      * If remote sorting is used, the sort is performed on the server, and the cache is
23157      * reloaded. If local sorting is used, the cache is sorted internally.
23158      * @param {String} fieldName The name of the field to sort by.
23159      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23160      */
23161     sort : function(fieldName, dir){
23162         var f = this.fields.get(fieldName);
23163         if(!dir){
23164             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23165             
23166             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23167                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23168             }else{
23169                 dir = f.sortDir;
23170             }
23171         }
23172         this.sortToggle[f.name] = dir;
23173         this.sortInfo = {field: f.name, direction: dir};
23174         if(!this.remoteSort){
23175             this.applySort();
23176             this.fireEvent("datachanged", this);
23177         }else{
23178             this.load(this.lastOptions);
23179         }
23180     },
23181
23182     /**
23183      * Calls the specified function for each of the Records in the cache.
23184      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23185      * Returning <em>false</em> aborts and exits the iteration.
23186      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23187      */
23188     each : function(fn, scope){
23189         this.data.each(fn, scope);
23190     },
23191
23192     /**
23193      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23194      * (e.g., during paging).
23195      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23196      */
23197     getModifiedRecords : function(){
23198         return this.modified;
23199     },
23200
23201     // private
23202     createFilterFn : function(property, value, anyMatch){
23203         if(!value.exec){ // not a regex
23204             value = String(value);
23205             if(value.length == 0){
23206                 return false;
23207             }
23208             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23209         }
23210         return function(r){
23211             return value.test(r.data[property]);
23212         };
23213     },
23214
23215     /**
23216      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23217      * @param {String} property A field on your records
23218      * @param {Number} start The record index to start at (defaults to 0)
23219      * @param {Number} end The last record index to include (defaults to length - 1)
23220      * @return {Number} The sum
23221      */
23222     sum : function(property, start, end){
23223         var rs = this.data.items, v = 0;
23224         start = start || 0;
23225         end = (end || end === 0) ? end : rs.length-1;
23226
23227         for(var i = start; i <= end; i++){
23228             v += (rs[i].data[property] || 0);
23229         }
23230         return v;
23231     },
23232
23233     /**
23234      * Filter the records by a specified property.
23235      * @param {String} field A field on your records
23236      * @param {String/RegExp} value Either a string that the field
23237      * should start with or a RegExp to test against the field
23238      * @param {Boolean} anyMatch True to match any part not just the beginning
23239      */
23240     filter : function(property, value, anyMatch){
23241         var fn = this.createFilterFn(property, value, anyMatch);
23242         return fn ? this.filterBy(fn) : this.clearFilter();
23243     },
23244
23245     /**
23246      * Filter by a function. The specified function will be called with each
23247      * record in this data source. If the function returns true the record is included,
23248      * otherwise it is filtered.
23249      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23250      * @param {Object} scope (optional) The scope of the function (defaults to this)
23251      */
23252     filterBy : function(fn, scope){
23253         this.snapshot = this.snapshot || this.data;
23254         this.data = this.queryBy(fn, scope||this);
23255         this.fireEvent("datachanged", this);
23256     },
23257
23258     /**
23259      * Query the records by a specified property.
23260      * @param {String} field A field on your records
23261      * @param {String/RegExp} value Either a string that the field
23262      * should start with or a RegExp to test against the field
23263      * @param {Boolean} anyMatch True to match any part not just the beginning
23264      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23265      */
23266     query : function(property, value, anyMatch){
23267         var fn = this.createFilterFn(property, value, anyMatch);
23268         return fn ? this.queryBy(fn) : this.data.clone();
23269     },
23270
23271     /**
23272      * Query by a function. The specified function will be called with each
23273      * record in this data source. If the function returns true the record is included
23274      * in the results.
23275      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23276      * @param {Object} scope (optional) The scope of the function (defaults to this)
23277       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23278      **/
23279     queryBy : function(fn, scope){
23280         var data = this.snapshot || this.data;
23281         return data.filterBy(fn, scope||this);
23282     },
23283
23284     /**
23285      * Collects unique values for a particular dataIndex from this store.
23286      * @param {String} dataIndex The property to collect
23287      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23288      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23289      * @return {Array} An array of the unique values
23290      **/
23291     collect : function(dataIndex, allowNull, bypassFilter){
23292         var d = (bypassFilter === true && this.snapshot) ?
23293                 this.snapshot.items : this.data.items;
23294         var v, sv, r = [], l = {};
23295         for(var i = 0, len = d.length; i < len; i++){
23296             v = d[i].data[dataIndex];
23297             sv = String(v);
23298             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23299                 l[sv] = true;
23300                 r[r.length] = v;
23301             }
23302         }
23303         return r;
23304     },
23305
23306     /**
23307      * Revert to a view of the Record cache with no filtering applied.
23308      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23309      */
23310     clearFilter : function(suppressEvent){
23311         if(this.snapshot && this.snapshot != this.data){
23312             this.data = this.snapshot;
23313             delete this.snapshot;
23314             if(suppressEvent !== true){
23315                 this.fireEvent("datachanged", this);
23316             }
23317         }
23318     },
23319
23320     // private
23321     afterEdit : function(record){
23322         if(this.modified.indexOf(record) == -1){
23323             this.modified.push(record);
23324         }
23325         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23326     },
23327     
23328     // private
23329     afterReject : function(record){
23330         this.modified.remove(record);
23331         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23332     },
23333
23334     // private
23335     afterCommit : function(record){
23336         this.modified.remove(record);
23337         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23338     },
23339
23340     /**
23341      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23342      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23343      */
23344     commitChanges : function(){
23345         var m = this.modified.slice(0);
23346         this.modified = [];
23347         for(var i = 0, len = m.length; i < len; i++){
23348             m[i].commit();
23349         }
23350     },
23351
23352     /**
23353      * Cancel outstanding changes on all changed records.
23354      */
23355     rejectChanges : function(){
23356         var m = this.modified.slice(0);
23357         this.modified = [];
23358         for(var i = 0, len = m.length; i < len; i++){
23359             m[i].reject();
23360         }
23361     },
23362
23363     onMetaChange : function(meta, rtype, o){
23364         this.recordType = rtype;
23365         this.fields = rtype.prototype.fields;
23366         delete this.snapshot;
23367         this.sortInfo = meta.sortInfo || this.sortInfo;
23368         this.modified = [];
23369         this.fireEvent('metachange', this, this.reader.meta);
23370     },
23371     
23372     moveIndex : function(data, type)
23373     {
23374         var index = this.indexOf(data);
23375         
23376         var newIndex = index + type;
23377         
23378         this.remove(data);
23379         
23380         this.insert(newIndex, data);
23381         
23382     }
23383 });/*
23384  * Based on:
23385  * Ext JS Library 1.1.1
23386  * Copyright(c) 2006-2007, Ext JS, LLC.
23387  *
23388  * Originally Released Under LGPL - original licence link has changed is not relivant.
23389  *
23390  * Fork - LGPL
23391  * <script type="text/javascript">
23392  */
23393
23394 /**
23395  * @class Roo.data.SimpleStore
23396  * @extends Roo.data.Store
23397  * Small helper class to make creating Stores from Array data easier.
23398  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23399  * @cfg {Array} fields An array of field definition objects, or field name strings.
23400  * @cfg {Array} data The multi-dimensional array of data
23401  * @constructor
23402  * @param {Object} config
23403  */
23404 Roo.data.SimpleStore = function(config){
23405     Roo.data.SimpleStore.superclass.constructor.call(this, {
23406         isLocal : true,
23407         reader: new Roo.data.ArrayReader({
23408                 id: config.id
23409             },
23410             Roo.data.Record.create(config.fields)
23411         ),
23412         proxy : new Roo.data.MemoryProxy(config.data)
23413     });
23414     this.load();
23415 };
23416 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23417  * Based on:
23418  * Ext JS Library 1.1.1
23419  * Copyright(c) 2006-2007, Ext JS, LLC.
23420  *
23421  * Originally Released Under LGPL - original licence link has changed is not relivant.
23422  *
23423  * Fork - LGPL
23424  * <script type="text/javascript">
23425  */
23426
23427 /**
23428 /**
23429  * @extends Roo.data.Store
23430  * @class Roo.data.JsonStore
23431  * Small helper class to make creating Stores for JSON data easier. <br/>
23432 <pre><code>
23433 var store = new Roo.data.JsonStore({
23434     url: 'get-images.php',
23435     root: 'images',
23436     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23437 });
23438 </code></pre>
23439  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23440  * JsonReader and HttpProxy (unless inline data is provided).</b>
23441  * @cfg {Array} fields An array of field definition objects, or field name strings.
23442  * @constructor
23443  * @param {Object} config
23444  */
23445 Roo.data.JsonStore = function(c){
23446     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23447         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23448         reader: new Roo.data.JsonReader(c, c.fields)
23449     }));
23450 };
23451 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23452  * Based on:
23453  * Ext JS Library 1.1.1
23454  * Copyright(c) 2006-2007, Ext JS, LLC.
23455  *
23456  * Originally Released Under LGPL - original licence link has changed is not relivant.
23457  *
23458  * Fork - LGPL
23459  * <script type="text/javascript">
23460  */
23461
23462  
23463 Roo.data.Field = function(config){
23464     if(typeof config == "string"){
23465         config = {name: config};
23466     }
23467     Roo.apply(this, config);
23468     
23469     if(!this.type){
23470         this.type = "auto";
23471     }
23472     
23473     var st = Roo.data.SortTypes;
23474     // named sortTypes are supported, here we look them up
23475     if(typeof this.sortType == "string"){
23476         this.sortType = st[this.sortType];
23477     }
23478     
23479     // set default sortType for strings and dates
23480     if(!this.sortType){
23481         switch(this.type){
23482             case "string":
23483                 this.sortType = st.asUCString;
23484                 break;
23485             case "date":
23486                 this.sortType = st.asDate;
23487                 break;
23488             default:
23489                 this.sortType = st.none;
23490         }
23491     }
23492
23493     // define once
23494     var stripRe = /[\$,%]/g;
23495
23496     // prebuilt conversion function for this field, instead of
23497     // switching every time we're reading a value
23498     if(!this.convert){
23499         var cv, dateFormat = this.dateFormat;
23500         switch(this.type){
23501             case "":
23502             case "auto":
23503             case undefined:
23504                 cv = function(v){ return v; };
23505                 break;
23506             case "string":
23507                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23508                 break;
23509             case "int":
23510                 cv = function(v){
23511                     return v !== undefined && v !== null && v !== '' ?
23512                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23513                     };
23514                 break;
23515             case "float":
23516                 cv = function(v){
23517                     return v !== undefined && v !== null && v !== '' ?
23518                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23519                     };
23520                 break;
23521             case "bool":
23522             case "boolean":
23523                 cv = function(v){ return v === true || v === "true" || v == 1; };
23524                 break;
23525             case "date":
23526                 cv = function(v){
23527                     if(!v){
23528                         return '';
23529                     }
23530                     if(v instanceof Date){
23531                         return v;
23532                     }
23533                     if(dateFormat){
23534                         if(dateFormat == "timestamp"){
23535                             return new Date(v*1000);
23536                         }
23537                         return Date.parseDate(v, dateFormat);
23538                     }
23539                     var parsed = Date.parse(v);
23540                     return parsed ? new Date(parsed) : null;
23541                 };
23542              break;
23543             
23544         }
23545         this.convert = cv;
23546     }
23547 };
23548
23549 Roo.data.Field.prototype = {
23550     dateFormat: null,
23551     defaultValue: "",
23552     mapping: null,
23553     sortType : null,
23554     sortDir : "ASC"
23555 };/*
23556  * Based on:
23557  * Ext JS Library 1.1.1
23558  * Copyright(c) 2006-2007, Ext JS, LLC.
23559  *
23560  * Originally Released Under LGPL - original licence link has changed is not relivant.
23561  *
23562  * Fork - LGPL
23563  * <script type="text/javascript">
23564  */
23565  
23566 // Base class for reading structured data from a data source.  This class is intended to be
23567 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23568
23569 /**
23570  * @class Roo.data.DataReader
23571  * Base class for reading structured data from a data source.  This class is intended to be
23572  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23573  */
23574
23575 Roo.data.DataReader = function(meta, recordType){
23576     
23577     this.meta = meta;
23578     
23579     this.recordType = recordType instanceof Array ? 
23580         Roo.data.Record.create(recordType) : recordType;
23581 };
23582
23583 Roo.data.DataReader.prototype = {
23584      /**
23585      * Create an empty record
23586      * @param {Object} data (optional) - overlay some values
23587      * @return {Roo.data.Record} record created.
23588      */
23589     newRow :  function(d) {
23590         var da =  {};
23591         this.recordType.prototype.fields.each(function(c) {
23592             switch( c.type) {
23593                 case 'int' : da[c.name] = 0; break;
23594                 case 'date' : da[c.name] = new Date(); break;
23595                 case 'float' : da[c.name] = 0.0; break;
23596                 case 'boolean' : da[c.name] = false; break;
23597                 default : da[c.name] = ""; break;
23598             }
23599             
23600         });
23601         return new this.recordType(Roo.apply(da, d));
23602     }
23603     
23604 };/*
23605  * Based on:
23606  * Ext JS Library 1.1.1
23607  * Copyright(c) 2006-2007, Ext JS, LLC.
23608  *
23609  * Originally Released Under LGPL - original licence link has changed is not relivant.
23610  *
23611  * Fork - LGPL
23612  * <script type="text/javascript">
23613  */
23614
23615 /**
23616  * @class Roo.data.DataProxy
23617  * @extends Roo.data.Observable
23618  * This class is an abstract base class for implementations which provide retrieval of
23619  * unformatted data objects.<br>
23620  * <p>
23621  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23622  * (of the appropriate type which knows how to parse the data object) to provide a block of
23623  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23624  * <p>
23625  * Custom implementations must implement the load method as described in
23626  * {@link Roo.data.HttpProxy#load}.
23627  */
23628 Roo.data.DataProxy = function(){
23629     this.addEvents({
23630         /**
23631          * @event beforeload
23632          * Fires before a network request is made to retrieve a data object.
23633          * @param {Object} This DataProxy object.
23634          * @param {Object} params The params parameter to the load function.
23635          */
23636         beforeload : true,
23637         /**
23638          * @event load
23639          * Fires before the load method's callback is called.
23640          * @param {Object} This DataProxy object.
23641          * @param {Object} o The data object.
23642          * @param {Object} arg The callback argument object passed to the load function.
23643          */
23644         load : true,
23645         /**
23646          * @event loadexception
23647          * Fires if an Exception occurs during data retrieval.
23648          * @param {Object} This DataProxy object.
23649          * @param {Object} o The data object.
23650          * @param {Object} arg The callback argument object passed to the load function.
23651          * @param {Object} e The Exception.
23652          */
23653         loadexception : true
23654     });
23655     Roo.data.DataProxy.superclass.constructor.call(this);
23656 };
23657
23658 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23659
23660     /**
23661      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23662      */
23663 /*
23664  * Based on:
23665  * Ext JS Library 1.1.1
23666  * Copyright(c) 2006-2007, Ext JS, LLC.
23667  *
23668  * Originally Released Under LGPL - original licence link has changed is not relivant.
23669  *
23670  * Fork - LGPL
23671  * <script type="text/javascript">
23672  */
23673 /**
23674  * @class Roo.data.MemoryProxy
23675  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23676  * to the Reader when its load method is called.
23677  * @constructor
23678  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23679  */
23680 Roo.data.MemoryProxy = function(data){
23681     if (data.data) {
23682         data = data.data;
23683     }
23684     Roo.data.MemoryProxy.superclass.constructor.call(this);
23685     this.data = data;
23686 };
23687
23688 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23689     
23690     /**
23691      * Load data from the requested source (in this case an in-memory
23692      * data object passed to the constructor), read the data object into
23693      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23694      * process that block using the passed callback.
23695      * @param {Object} params This parameter is not used by the MemoryProxy class.
23696      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23697      * object into a block of Roo.data.Records.
23698      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23699      * The function must be passed <ul>
23700      * <li>The Record block object</li>
23701      * <li>The "arg" argument from the load function</li>
23702      * <li>A boolean success indicator</li>
23703      * </ul>
23704      * @param {Object} scope The scope in which to call the callback
23705      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23706      */
23707     load : function(params, reader, callback, scope, arg){
23708         params = params || {};
23709         var result;
23710         try {
23711             result = reader.readRecords(this.data);
23712         }catch(e){
23713             this.fireEvent("loadexception", this, arg, null, e);
23714             callback.call(scope, null, arg, false);
23715             return;
23716         }
23717         callback.call(scope, result, arg, true);
23718     },
23719     
23720     // private
23721     update : function(params, records){
23722         
23723     }
23724 });/*
23725  * Based on:
23726  * Ext JS Library 1.1.1
23727  * Copyright(c) 2006-2007, Ext JS, LLC.
23728  *
23729  * Originally Released Under LGPL - original licence link has changed is not relivant.
23730  *
23731  * Fork - LGPL
23732  * <script type="text/javascript">
23733  */
23734 /**
23735  * @class Roo.data.HttpProxy
23736  * @extends Roo.data.DataProxy
23737  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23738  * configured to reference a certain URL.<br><br>
23739  * <p>
23740  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23741  * from which the running page was served.<br><br>
23742  * <p>
23743  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23744  * <p>
23745  * Be aware that to enable the browser to parse an XML document, the server must set
23746  * the Content-Type header in the HTTP response to "text/xml".
23747  * @constructor
23748  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23749  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23750  * will be used to make the request.
23751  */
23752 Roo.data.HttpProxy = function(conn){
23753     Roo.data.HttpProxy.superclass.constructor.call(this);
23754     // is conn a conn config or a real conn?
23755     this.conn = conn;
23756     this.useAjax = !conn || !conn.events;
23757   
23758 };
23759
23760 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23761     // thse are take from connection...
23762     
23763     /**
23764      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23765      */
23766     /**
23767      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23768      * extra parameters to each request made by this object. (defaults to undefined)
23769      */
23770     /**
23771      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23772      *  to each request made by this object. (defaults to undefined)
23773      */
23774     /**
23775      * @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)
23776      */
23777     /**
23778      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23779      */
23780      /**
23781      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23782      * @type Boolean
23783      */
23784   
23785
23786     /**
23787      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23788      * @type Boolean
23789      */
23790     /**
23791      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23792      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23793      * a finer-grained basis than the DataProxy events.
23794      */
23795     getConnection : function(){
23796         return this.useAjax ? Roo.Ajax : this.conn;
23797     },
23798
23799     /**
23800      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23801      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23802      * process that block using the passed callback.
23803      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23804      * for the request to the remote server.
23805      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23806      * object into a block of Roo.data.Records.
23807      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23808      * The function must be passed <ul>
23809      * <li>The Record block object</li>
23810      * <li>The "arg" argument from the load function</li>
23811      * <li>A boolean success indicator</li>
23812      * </ul>
23813      * @param {Object} scope The scope in which to call the callback
23814      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23815      */
23816     load : function(params, reader, callback, scope, arg){
23817         if(this.fireEvent("beforeload", this, params) !== false){
23818             var  o = {
23819                 params : params || {},
23820                 request: {
23821                     callback : callback,
23822                     scope : scope,
23823                     arg : arg
23824                 },
23825                 reader: reader,
23826                 callback : this.loadResponse,
23827                 scope: this
23828             };
23829             if(this.useAjax){
23830                 Roo.applyIf(o, this.conn);
23831                 if(this.activeRequest){
23832                     Roo.Ajax.abort(this.activeRequest);
23833                 }
23834                 this.activeRequest = Roo.Ajax.request(o);
23835             }else{
23836                 this.conn.request(o);
23837             }
23838         }else{
23839             callback.call(scope||this, null, arg, false);
23840         }
23841     },
23842
23843     // private
23844     loadResponse : function(o, success, response){
23845         delete this.activeRequest;
23846         if(!success){
23847             this.fireEvent("loadexception", this, o, response);
23848             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23849             return;
23850         }
23851         var result;
23852         try {
23853             result = o.reader.read(response);
23854         }catch(e){
23855             this.fireEvent("loadexception", this, o, response, e);
23856             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23857             return;
23858         }
23859         
23860         this.fireEvent("load", this, o, o.request.arg);
23861         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23862     },
23863
23864     // private
23865     update : function(dataSet){
23866
23867     },
23868
23869     // private
23870     updateResponse : function(dataSet){
23871
23872     }
23873 });/*
23874  * Based on:
23875  * Ext JS Library 1.1.1
23876  * Copyright(c) 2006-2007, Ext JS, LLC.
23877  *
23878  * Originally Released Under LGPL - original licence link has changed is not relivant.
23879  *
23880  * Fork - LGPL
23881  * <script type="text/javascript">
23882  */
23883
23884 /**
23885  * @class Roo.data.ScriptTagProxy
23886  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23887  * other than the originating domain of the running page.<br><br>
23888  * <p>
23889  * <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
23890  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23891  * <p>
23892  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23893  * source code that is used as the source inside a &lt;script> tag.<br><br>
23894  * <p>
23895  * In order for the browser to process the returned data, the server must wrap the data object
23896  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23897  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23898  * depending on whether the callback name was passed:
23899  * <p>
23900  * <pre><code>
23901 boolean scriptTag = false;
23902 String cb = request.getParameter("callback");
23903 if (cb != null) {
23904     scriptTag = true;
23905     response.setContentType("text/javascript");
23906 } else {
23907     response.setContentType("application/x-json");
23908 }
23909 Writer out = response.getWriter();
23910 if (scriptTag) {
23911     out.write(cb + "(");
23912 }
23913 out.print(dataBlock.toJsonString());
23914 if (scriptTag) {
23915     out.write(");");
23916 }
23917 </pre></code>
23918  *
23919  * @constructor
23920  * @param {Object} config A configuration object.
23921  */
23922 Roo.data.ScriptTagProxy = function(config){
23923     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23924     Roo.apply(this, config);
23925     this.head = document.getElementsByTagName("head")[0];
23926 };
23927
23928 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23929
23930 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23931     /**
23932      * @cfg {String} url The URL from which to request the data object.
23933      */
23934     /**
23935      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23936      */
23937     timeout : 30000,
23938     /**
23939      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23940      * the server the name of the callback function set up by the load call to process the returned data object.
23941      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23942      * javascript output which calls this named function passing the data object as its only parameter.
23943      */
23944     callbackParam : "callback",
23945     /**
23946      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23947      * name to the request.
23948      */
23949     nocache : true,
23950
23951     /**
23952      * Load data from the configured URL, read the data object into
23953      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23954      * process that block using the passed callback.
23955      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23956      * for the request to the remote server.
23957      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23958      * object into a block of Roo.data.Records.
23959      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23960      * The function must be passed <ul>
23961      * <li>The Record block object</li>
23962      * <li>The "arg" argument from the load function</li>
23963      * <li>A boolean success indicator</li>
23964      * </ul>
23965      * @param {Object} scope The scope in which to call the callback
23966      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23967      */
23968     load : function(params, reader, callback, scope, arg){
23969         if(this.fireEvent("beforeload", this, params) !== false){
23970
23971             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23972
23973             var url = this.url;
23974             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23975             if(this.nocache){
23976                 url += "&_dc=" + (new Date().getTime());
23977             }
23978             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23979             var trans = {
23980                 id : transId,
23981                 cb : "stcCallback"+transId,
23982                 scriptId : "stcScript"+transId,
23983                 params : params,
23984                 arg : arg,
23985                 url : url,
23986                 callback : callback,
23987                 scope : scope,
23988                 reader : reader
23989             };
23990             var conn = this;
23991
23992             window[trans.cb] = function(o){
23993                 conn.handleResponse(o, trans);
23994             };
23995
23996             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23997
23998             if(this.autoAbort !== false){
23999                 this.abort();
24000             }
24001
24002             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24003
24004             var script = document.createElement("script");
24005             script.setAttribute("src", url);
24006             script.setAttribute("type", "text/javascript");
24007             script.setAttribute("id", trans.scriptId);
24008             this.head.appendChild(script);
24009
24010             this.trans = trans;
24011         }else{
24012             callback.call(scope||this, null, arg, false);
24013         }
24014     },
24015
24016     // private
24017     isLoading : function(){
24018         return this.trans ? true : false;
24019     },
24020
24021     /**
24022      * Abort the current server request.
24023      */
24024     abort : function(){
24025         if(this.isLoading()){
24026             this.destroyTrans(this.trans);
24027         }
24028     },
24029
24030     // private
24031     destroyTrans : function(trans, isLoaded){
24032         this.head.removeChild(document.getElementById(trans.scriptId));
24033         clearTimeout(trans.timeoutId);
24034         if(isLoaded){
24035             window[trans.cb] = undefined;
24036             try{
24037                 delete window[trans.cb];
24038             }catch(e){}
24039         }else{
24040             // if hasn't been loaded, wait for load to remove it to prevent script error
24041             window[trans.cb] = function(){
24042                 window[trans.cb] = undefined;
24043                 try{
24044                     delete window[trans.cb];
24045                 }catch(e){}
24046             };
24047         }
24048     },
24049
24050     // private
24051     handleResponse : function(o, trans){
24052         this.trans = false;
24053         this.destroyTrans(trans, true);
24054         var result;
24055         try {
24056             result = trans.reader.readRecords(o);
24057         }catch(e){
24058             this.fireEvent("loadexception", this, o, trans.arg, e);
24059             trans.callback.call(trans.scope||window, null, trans.arg, false);
24060             return;
24061         }
24062         this.fireEvent("load", this, o, trans.arg);
24063         trans.callback.call(trans.scope||window, result, trans.arg, true);
24064     },
24065
24066     // private
24067     handleFailure : function(trans){
24068         this.trans = false;
24069         this.destroyTrans(trans, false);
24070         this.fireEvent("loadexception", this, null, trans.arg);
24071         trans.callback.call(trans.scope||window, null, trans.arg, false);
24072     }
24073 });/*
24074  * Based on:
24075  * Ext JS Library 1.1.1
24076  * Copyright(c) 2006-2007, Ext JS, LLC.
24077  *
24078  * Originally Released Under LGPL - original licence link has changed is not relivant.
24079  *
24080  * Fork - LGPL
24081  * <script type="text/javascript">
24082  */
24083
24084 /**
24085  * @class Roo.data.JsonReader
24086  * @extends Roo.data.DataReader
24087  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24088  * based on mappings in a provided Roo.data.Record constructor.
24089  * 
24090  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24091  * in the reply previously. 
24092  * 
24093  * <p>
24094  * Example code:
24095  * <pre><code>
24096 var RecordDef = Roo.data.Record.create([
24097     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24098     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24099 ]);
24100 var myReader = new Roo.data.JsonReader({
24101     totalProperty: "results",    // The property which contains the total dataset size (optional)
24102     root: "rows",                // The property which contains an Array of row objects
24103     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24104 }, RecordDef);
24105 </code></pre>
24106  * <p>
24107  * This would consume a JSON file like this:
24108  * <pre><code>
24109 { 'results': 2, 'rows': [
24110     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24111     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24112 }
24113 </code></pre>
24114  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24115  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24116  * paged from the remote server.
24117  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24118  * @cfg {String} root name of the property which contains the Array of row objects.
24119  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24120  * @cfg {Array} fields Array of field definition objects
24121  * @constructor
24122  * Create a new JsonReader
24123  * @param {Object} meta Metadata configuration options
24124  * @param {Object} recordType Either an Array of field definition objects,
24125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24126  */
24127 Roo.data.JsonReader = function(meta, recordType){
24128     
24129     meta = meta || {};
24130     // set some defaults:
24131     Roo.applyIf(meta, {
24132         totalProperty: 'total',
24133         successProperty : 'success',
24134         root : 'data',
24135         id : 'id'
24136     });
24137     
24138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24139 };
24140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24141     
24142     /**
24143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24144      * Used by Store query builder to append _requestMeta to params.
24145      * 
24146      */
24147     metaFromRemote : false,
24148     /**
24149      * This method is only used by a DataProxy which has retrieved data from a remote server.
24150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24151      * @return {Object} data A data block which is used by an Roo.data.Store object as
24152      * a cache of Roo.data.Records.
24153      */
24154     read : function(response){
24155         var json = response.responseText;
24156        
24157         var o = /* eval:var:o */ eval("("+json+")");
24158         if(!o) {
24159             throw {message: "JsonReader.read: Json object not found"};
24160         }
24161         
24162         if(o.metaData){
24163             
24164             delete this.ef;
24165             this.metaFromRemote = true;
24166             this.meta = o.metaData;
24167             this.recordType = Roo.data.Record.create(o.metaData.fields);
24168             this.onMetaChange(this.meta, this.recordType, o);
24169         }
24170         return this.readRecords(o);
24171     },
24172
24173     // private function a store will implement
24174     onMetaChange : function(meta, recordType, o){
24175
24176     },
24177
24178     /**
24179          * @ignore
24180          */
24181     simpleAccess: function(obj, subsc) {
24182         return obj[subsc];
24183     },
24184
24185         /**
24186          * @ignore
24187          */
24188     getJsonAccessor: function(){
24189         var re = /[\[\.]/;
24190         return function(expr) {
24191             try {
24192                 return(re.test(expr))
24193                     ? new Function("obj", "return obj." + expr)
24194                     : function(obj){
24195                         return obj[expr];
24196                     };
24197             } catch(e){}
24198             return Roo.emptyFn;
24199         };
24200     }(),
24201
24202     /**
24203      * Create a data block containing Roo.data.Records from an XML document.
24204      * @param {Object} o An object which contains an Array of row objects in the property specified
24205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24206      * which contains the total size of the dataset.
24207      * @return {Object} data A data block which is used by an Roo.data.Store object as
24208      * a cache of Roo.data.Records.
24209      */
24210     readRecords : function(o){
24211         /**
24212          * After any data loads, the raw JSON data is available for further custom processing.
24213          * @type Object
24214          */
24215         this.o = o;
24216         var s = this.meta, Record = this.recordType,
24217             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24218
24219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24220         if (!this.ef) {
24221             if(s.totalProperty) {
24222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24223                 }
24224                 if(s.successProperty) {
24225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24226                 }
24227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24228                 if (s.id) {
24229                         var g = this.getJsonAccessor(s.id);
24230                         this.getId = function(rec) {
24231                                 var r = g(rec);  
24232                                 return (r === undefined || r === "") ? null : r;
24233                         };
24234                 } else {
24235                         this.getId = function(){return null;};
24236                 }
24237             this.ef = [];
24238             for(var jj = 0; jj < fl; jj++){
24239                 f = fi[jj];
24240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24241                 this.ef[jj] = this.getJsonAccessor(map);
24242             }
24243         }
24244
24245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24246         if(s.totalProperty){
24247             var vt = parseInt(this.getTotal(o), 10);
24248             if(!isNaN(vt)){
24249                 totalRecords = vt;
24250             }
24251         }
24252         if(s.successProperty){
24253             var vs = this.getSuccess(o);
24254             if(vs === false || vs === 'false'){
24255                 success = false;
24256             }
24257         }
24258         var records = [];
24259         for(var i = 0; i < c; i++){
24260                 var n = root[i];
24261             var values = {};
24262             var id = this.getId(n);
24263             for(var j = 0; j < fl; j++){
24264                 f = fi[j];
24265             var v = this.ef[j](n);
24266             if (!f.convert) {
24267                 Roo.log('missing convert for ' + f.name);
24268                 Roo.log(f);
24269                 continue;
24270             }
24271             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24272             }
24273             var record = new Record(values, id);
24274             record.json = n;
24275             records[i] = record;
24276         }
24277         return {
24278             raw : o,
24279             success : success,
24280             records : records,
24281             totalRecords : totalRecords
24282         };
24283     }
24284 });/*
24285  * Based on:
24286  * Ext JS Library 1.1.1
24287  * Copyright(c) 2006-2007, Ext JS, LLC.
24288  *
24289  * Originally Released Under LGPL - original licence link has changed is not relivant.
24290  *
24291  * Fork - LGPL
24292  * <script type="text/javascript">
24293  */
24294
24295 /**
24296  * @class Roo.data.XmlReader
24297  * @extends Roo.data.DataReader
24298  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24299  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24300  * <p>
24301  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24302  * header in the HTTP response must be set to "text/xml".</em>
24303  * <p>
24304  * Example code:
24305  * <pre><code>
24306 var RecordDef = Roo.data.Record.create([
24307    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24308    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24309 ]);
24310 var myReader = new Roo.data.XmlReader({
24311    totalRecords: "results", // The element which contains the total dataset size (optional)
24312    record: "row",           // The repeated element which contains row information
24313    id: "id"                 // The element within the row that provides an ID for the record (optional)
24314 }, RecordDef);
24315 </code></pre>
24316  * <p>
24317  * This would consume an XML file like this:
24318  * <pre><code>
24319 &lt;?xml?>
24320 &lt;dataset>
24321  &lt;results>2&lt;/results>
24322  &lt;row>
24323    &lt;id>1&lt;/id>
24324    &lt;name>Bill&lt;/name>
24325    &lt;occupation>Gardener&lt;/occupation>
24326  &lt;/row>
24327  &lt;row>
24328    &lt;id>2&lt;/id>
24329    &lt;name>Ben&lt;/name>
24330    &lt;occupation>Horticulturalist&lt;/occupation>
24331  &lt;/row>
24332 &lt;/dataset>
24333 </code></pre>
24334  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24335  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24336  * paged from the remote server.
24337  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24338  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24339  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24340  * a record identifier value.
24341  * @constructor
24342  * Create a new XmlReader
24343  * @param {Object} meta Metadata configuration options
24344  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24345  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24346  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24347  */
24348 Roo.data.XmlReader = function(meta, recordType){
24349     meta = meta || {};
24350     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24351 };
24352 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24353     /**
24354      * This method is only used by a DataProxy which has retrieved data from a remote server.
24355          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24356          * to contain a method called 'responseXML' that returns an XML document object.
24357      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24358      * a cache of Roo.data.Records.
24359      */
24360     read : function(response){
24361         var doc = response.responseXML;
24362         if(!doc) {
24363             throw {message: "XmlReader.read: XML Document not available"};
24364         }
24365         return this.readRecords(doc);
24366     },
24367
24368     /**
24369      * Create a data block containing Roo.data.Records from an XML document.
24370          * @param {Object} doc A parsed XML document.
24371      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24372      * a cache of Roo.data.Records.
24373      */
24374     readRecords : function(doc){
24375         /**
24376          * After any data loads/reads, the raw XML Document is available for further custom processing.
24377          * @type XMLDocument
24378          */
24379         this.xmlData = doc;
24380         var root = doc.documentElement || doc;
24381         var q = Roo.DomQuery;
24382         var recordType = this.recordType, fields = recordType.prototype.fields;
24383         var sid = this.meta.id;
24384         var totalRecords = 0, success = true;
24385         if(this.meta.totalRecords){
24386             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24387         }
24388         
24389         if(this.meta.success){
24390             var sv = q.selectValue(this.meta.success, root, true);
24391             success = sv !== false && sv !== 'false';
24392         }
24393         var records = [];
24394         var ns = q.select(this.meta.record, root);
24395         for(var i = 0, len = ns.length; i < len; i++) {
24396                 var n = ns[i];
24397                 var values = {};
24398                 var id = sid ? q.selectValue(sid, n) : undefined;
24399                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24400                     var f = fields.items[j];
24401                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24402                     v = f.convert(v);
24403                     values[f.name] = v;
24404                 }
24405                 var record = new recordType(values, id);
24406                 record.node = n;
24407                 records[records.length] = record;
24408             }
24409
24410             return {
24411                 success : success,
24412                 records : records,
24413                 totalRecords : totalRecords || records.length
24414             };
24415     }
24416 });/*
24417  * Based on:
24418  * Ext JS Library 1.1.1
24419  * Copyright(c) 2006-2007, Ext JS, LLC.
24420  *
24421  * Originally Released Under LGPL - original licence link has changed is not relivant.
24422  *
24423  * Fork - LGPL
24424  * <script type="text/javascript">
24425  */
24426
24427 /**
24428  * @class Roo.data.ArrayReader
24429  * @extends Roo.data.DataReader
24430  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24431  * Each element of that Array represents a row of data fields. The
24432  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24433  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24434  * <p>
24435  * Example code:.
24436  * <pre><code>
24437 var RecordDef = Roo.data.Record.create([
24438     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24439     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24440 ]);
24441 var myReader = new Roo.data.ArrayReader({
24442     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24443 }, RecordDef);
24444 </code></pre>
24445  * <p>
24446  * This would consume an Array like this:
24447  * <pre><code>
24448 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24449   </code></pre>
24450  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24451  * @constructor
24452  * Create a new JsonReader
24453  * @param {Object} meta Metadata configuration options.
24454  * @param {Object} recordType Either an Array of field definition objects
24455  * as specified to {@link Roo.data.Record#create},
24456  * or an {@link Roo.data.Record} object
24457  * created using {@link Roo.data.Record#create}.
24458  */
24459 Roo.data.ArrayReader = function(meta, recordType){
24460     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24461 };
24462
24463 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24464     /**
24465      * Create a data block containing Roo.data.Records from an XML document.
24466      * @param {Object} o An Array of row objects which represents the dataset.
24467      * @return {Object} data A data block which is used by an Roo.data.Store object as
24468      * a cache of Roo.data.Records.
24469      */
24470     readRecords : function(o){
24471         var sid = this.meta ? this.meta.id : null;
24472         var recordType = this.recordType, fields = recordType.prototype.fields;
24473         var records = [];
24474         var root = o;
24475             for(var i = 0; i < root.length; i++){
24476                     var n = root[i];
24477                 var values = {};
24478                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24479                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24480                 var f = fields.items[j];
24481                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24482                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24483                 v = f.convert(v);
24484                 values[f.name] = v;
24485             }
24486                 var record = new recordType(values, id);
24487                 record.json = n;
24488                 records[records.length] = record;
24489             }
24490             return {
24491                 records : records,
24492                 totalRecords : records.length
24493             };
24494     }
24495 });/*
24496  * Based on:
24497  * Ext JS Library 1.1.1
24498  * Copyright(c) 2006-2007, Ext JS, LLC.
24499  *
24500  * Originally Released Under LGPL - original licence link has changed is not relivant.
24501  *
24502  * Fork - LGPL
24503  * <script type="text/javascript">
24504  */
24505
24506
24507 /**
24508  * @class Roo.data.Tree
24509  * @extends Roo.util.Observable
24510  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24511  * in the tree have most standard DOM functionality.
24512  * @constructor
24513  * @param {Node} root (optional) The root node
24514  */
24515 Roo.data.Tree = function(root){
24516    this.nodeHash = {};
24517    /**
24518     * The root node for this tree
24519     * @type Node
24520     */
24521    this.root = null;
24522    if(root){
24523        this.setRootNode(root);
24524    }
24525    this.addEvents({
24526        /**
24527         * @event append
24528         * Fires when a new child node is appended to a node in this tree.
24529         * @param {Tree} tree The owner tree
24530         * @param {Node} parent The parent node
24531         * @param {Node} node The newly appended node
24532         * @param {Number} index The index of the newly appended node
24533         */
24534        "append" : true,
24535        /**
24536         * @event remove
24537         * Fires when a child node is removed from a node in this tree.
24538         * @param {Tree} tree The owner tree
24539         * @param {Node} parent The parent node
24540         * @param {Node} node The child node removed
24541         */
24542        "remove" : true,
24543        /**
24544         * @event move
24545         * Fires when a node is moved to a new location in the tree
24546         * @param {Tree} tree The owner tree
24547         * @param {Node} node The node moved
24548         * @param {Node} oldParent The old parent of this node
24549         * @param {Node} newParent The new parent of this node
24550         * @param {Number} index The index it was moved to
24551         */
24552        "move" : true,
24553        /**
24554         * @event insert
24555         * Fires when a new child node is inserted in a node in this tree.
24556         * @param {Tree} tree The owner tree
24557         * @param {Node} parent The parent node
24558         * @param {Node} node The child node inserted
24559         * @param {Node} refNode The child node the node was inserted before
24560         */
24561        "insert" : true,
24562        /**
24563         * @event beforeappend
24564         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24565         * @param {Tree} tree The owner tree
24566         * @param {Node} parent The parent node
24567         * @param {Node} node The child node to be appended
24568         */
24569        "beforeappend" : true,
24570        /**
24571         * @event beforeremove
24572         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24573         * @param {Tree} tree The owner tree
24574         * @param {Node} parent The parent node
24575         * @param {Node} node The child node to be removed
24576         */
24577        "beforeremove" : true,
24578        /**
24579         * @event beforemove
24580         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24581         * @param {Tree} tree The owner tree
24582         * @param {Node} node The node being moved
24583         * @param {Node} oldParent The parent of the node
24584         * @param {Node} newParent The new parent the node is moving to
24585         * @param {Number} index The index it is being moved to
24586         */
24587        "beforemove" : true,
24588        /**
24589         * @event beforeinsert
24590         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24591         * @param {Tree} tree The owner tree
24592         * @param {Node} parent The parent node
24593         * @param {Node} node The child node to be inserted
24594         * @param {Node} refNode The child node the node is being inserted before
24595         */
24596        "beforeinsert" : true
24597    });
24598
24599     Roo.data.Tree.superclass.constructor.call(this);
24600 };
24601
24602 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24603     pathSeparator: "/",
24604
24605     proxyNodeEvent : function(){
24606         return this.fireEvent.apply(this, arguments);
24607     },
24608
24609     /**
24610      * Returns the root node for this tree.
24611      * @return {Node}
24612      */
24613     getRootNode : function(){
24614         return this.root;
24615     },
24616
24617     /**
24618      * Sets the root node for this tree.
24619      * @param {Node} node
24620      * @return {Node}
24621      */
24622     setRootNode : function(node){
24623         this.root = node;
24624         node.ownerTree = this;
24625         node.isRoot = true;
24626         this.registerNode(node);
24627         return node;
24628     },
24629
24630     /**
24631      * Gets a node in this tree by its id.
24632      * @param {String} id
24633      * @return {Node}
24634      */
24635     getNodeById : function(id){
24636         return this.nodeHash[id];
24637     },
24638
24639     registerNode : function(node){
24640         this.nodeHash[node.id] = node;
24641     },
24642
24643     unregisterNode : function(node){
24644         delete this.nodeHash[node.id];
24645     },
24646
24647     toString : function(){
24648         return "[Tree"+(this.id?" "+this.id:"")+"]";
24649     }
24650 });
24651
24652 /**
24653  * @class Roo.data.Node
24654  * @extends Roo.util.Observable
24655  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24656  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24657  * @constructor
24658  * @param {Object} attributes The attributes/config for the node
24659  */
24660 Roo.data.Node = function(attributes){
24661     /**
24662      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24663      * @type {Object}
24664      */
24665     this.attributes = attributes || {};
24666     this.leaf = this.attributes.leaf;
24667     /**
24668      * The node id. @type String
24669      */
24670     this.id = this.attributes.id;
24671     if(!this.id){
24672         this.id = Roo.id(null, "ynode-");
24673         this.attributes.id = this.id;
24674     }
24675      
24676     
24677     /**
24678      * All child nodes of this node. @type Array
24679      */
24680     this.childNodes = [];
24681     if(!this.childNodes.indexOf){ // indexOf is a must
24682         this.childNodes.indexOf = function(o){
24683             for(var i = 0, len = this.length; i < len; i++){
24684                 if(this[i] == o) {
24685                     return i;
24686                 }
24687             }
24688             return -1;
24689         };
24690     }
24691     /**
24692      * The parent node for this node. @type Node
24693      */
24694     this.parentNode = null;
24695     /**
24696      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24697      */
24698     this.firstChild = null;
24699     /**
24700      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24701      */
24702     this.lastChild = null;
24703     /**
24704      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24705      */
24706     this.previousSibling = null;
24707     /**
24708      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24709      */
24710     this.nextSibling = null;
24711
24712     this.addEvents({
24713        /**
24714         * @event append
24715         * Fires when a new child node is appended
24716         * @param {Tree} tree The owner tree
24717         * @param {Node} this This node
24718         * @param {Node} node The newly appended node
24719         * @param {Number} index The index of the newly appended node
24720         */
24721        "append" : true,
24722        /**
24723         * @event remove
24724         * Fires when a child node is removed
24725         * @param {Tree} tree The owner tree
24726         * @param {Node} this This node
24727         * @param {Node} node The removed node
24728         */
24729        "remove" : true,
24730        /**
24731         * @event move
24732         * Fires when this node is moved to a new location in the tree
24733         * @param {Tree} tree The owner tree
24734         * @param {Node} this This node
24735         * @param {Node} oldParent The old parent of this node
24736         * @param {Node} newParent The new parent of this node
24737         * @param {Number} index The index it was moved to
24738         */
24739        "move" : true,
24740        /**
24741         * @event insert
24742         * Fires when a new child node is inserted.
24743         * @param {Tree} tree The owner tree
24744         * @param {Node} this This node
24745         * @param {Node} node The child node inserted
24746         * @param {Node} refNode The child node the node was inserted before
24747         */
24748        "insert" : true,
24749        /**
24750         * @event beforeappend
24751         * Fires before a new child is appended, return false to cancel the append.
24752         * @param {Tree} tree The owner tree
24753         * @param {Node} this This node
24754         * @param {Node} node The child node to be appended
24755         */
24756        "beforeappend" : true,
24757        /**
24758         * @event beforeremove
24759         * Fires before a child is removed, return false to cancel the remove.
24760         * @param {Tree} tree The owner tree
24761         * @param {Node} this This node
24762         * @param {Node} node The child node to be removed
24763         */
24764        "beforeremove" : true,
24765        /**
24766         * @event beforemove
24767         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24768         * @param {Tree} tree The owner tree
24769         * @param {Node} this This node
24770         * @param {Node} oldParent The parent of this node
24771         * @param {Node} newParent The new parent this node is moving to
24772         * @param {Number} index The index it is being moved to
24773         */
24774        "beforemove" : true,
24775        /**
24776         * @event beforeinsert
24777         * Fires before a new child is inserted, return false to cancel the insert.
24778         * @param {Tree} tree The owner tree
24779         * @param {Node} this This node
24780         * @param {Node} node The child node to be inserted
24781         * @param {Node} refNode The child node the node is being inserted before
24782         */
24783        "beforeinsert" : true
24784    });
24785     this.listeners = this.attributes.listeners;
24786     Roo.data.Node.superclass.constructor.call(this);
24787 };
24788
24789 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24790     fireEvent : function(evtName){
24791         // first do standard event for this node
24792         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24793             return false;
24794         }
24795         // then bubble it up to the tree if the event wasn't cancelled
24796         var ot = this.getOwnerTree();
24797         if(ot){
24798             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24799                 return false;
24800             }
24801         }
24802         return true;
24803     },
24804
24805     /**
24806      * Returns true if this node is a leaf
24807      * @return {Boolean}
24808      */
24809     isLeaf : function(){
24810         return this.leaf === true;
24811     },
24812
24813     // private
24814     setFirstChild : function(node){
24815         this.firstChild = node;
24816     },
24817
24818     //private
24819     setLastChild : function(node){
24820         this.lastChild = node;
24821     },
24822
24823
24824     /**
24825      * Returns true if this node is the last child of its parent
24826      * @return {Boolean}
24827      */
24828     isLast : function(){
24829        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24830     },
24831
24832     /**
24833      * Returns true if this node is the first child of its parent
24834      * @return {Boolean}
24835      */
24836     isFirst : function(){
24837        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24838     },
24839
24840     hasChildNodes : function(){
24841         return !this.isLeaf() && this.childNodes.length > 0;
24842     },
24843
24844     /**
24845      * Insert node(s) as the last child node of this node.
24846      * @param {Node/Array} node The node or Array of nodes to append
24847      * @return {Node} The appended node if single append, or null if an array was passed
24848      */
24849     appendChild : function(node){
24850         var multi = false;
24851         if(node instanceof Array){
24852             multi = node;
24853         }else if(arguments.length > 1){
24854             multi = arguments;
24855         }
24856         // if passed an array or multiple args do them one by one
24857         if(multi){
24858             for(var i = 0, len = multi.length; i < len; i++) {
24859                 this.appendChild(multi[i]);
24860             }
24861         }else{
24862             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24863                 return false;
24864             }
24865             var index = this.childNodes.length;
24866             var oldParent = node.parentNode;
24867             // it's a move, make sure we move it cleanly
24868             if(oldParent){
24869                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24870                     return false;
24871                 }
24872                 oldParent.removeChild(node);
24873             }
24874             index = this.childNodes.length;
24875             if(index == 0){
24876                 this.setFirstChild(node);
24877             }
24878             this.childNodes.push(node);
24879             node.parentNode = this;
24880             var ps = this.childNodes[index-1];
24881             if(ps){
24882                 node.previousSibling = ps;
24883                 ps.nextSibling = node;
24884             }else{
24885                 node.previousSibling = null;
24886             }
24887             node.nextSibling = null;
24888             this.setLastChild(node);
24889             node.setOwnerTree(this.getOwnerTree());
24890             this.fireEvent("append", this.ownerTree, this, node, index);
24891             if(oldParent){
24892                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24893             }
24894             return node;
24895         }
24896     },
24897
24898     /**
24899      * Removes a child node from this node.
24900      * @param {Node} node The node to remove
24901      * @return {Node} The removed node
24902      */
24903     removeChild : function(node){
24904         var index = this.childNodes.indexOf(node);
24905         if(index == -1){
24906             return false;
24907         }
24908         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24909             return false;
24910         }
24911
24912         // remove it from childNodes collection
24913         this.childNodes.splice(index, 1);
24914
24915         // update siblings
24916         if(node.previousSibling){
24917             node.previousSibling.nextSibling = node.nextSibling;
24918         }
24919         if(node.nextSibling){
24920             node.nextSibling.previousSibling = node.previousSibling;
24921         }
24922
24923         // update child refs
24924         if(this.firstChild == node){
24925             this.setFirstChild(node.nextSibling);
24926         }
24927         if(this.lastChild == node){
24928             this.setLastChild(node.previousSibling);
24929         }
24930
24931         node.setOwnerTree(null);
24932         // clear any references from the node
24933         node.parentNode = null;
24934         node.previousSibling = null;
24935         node.nextSibling = null;
24936         this.fireEvent("remove", this.ownerTree, this, node);
24937         return node;
24938     },
24939
24940     /**
24941      * Inserts the first node before the second node in this nodes childNodes collection.
24942      * @param {Node} node The node to insert
24943      * @param {Node} refNode The node to insert before (if null the node is appended)
24944      * @return {Node} The inserted node
24945      */
24946     insertBefore : function(node, refNode){
24947         if(!refNode){ // like standard Dom, refNode can be null for append
24948             return this.appendChild(node);
24949         }
24950         // nothing to do
24951         if(node == refNode){
24952             return false;
24953         }
24954
24955         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24956             return false;
24957         }
24958         var index = this.childNodes.indexOf(refNode);
24959         var oldParent = node.parentNode;
24960         var refIndex = index;
24961
24962         // when moving internally, indexes will change after remove
24963         if(oldParent == this && this.childNodes.indexOf(node) < index){
24964             refIndex--;
24965         }
24966
24967         // it's a move, make sure we move it cleanly
24968         if(oldParent){
24969             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24970                 return false;
24971             }
24972             oldParent.removeChild(node);
24973         }
24974         if(refIndex == 0){
24975             this.setFirstChild(node);
24976         }
24977         this.childNodes.splice(refIndex, 0, node);
24978         node.parentNode = this;
24979         var ps = this.childNodes[refIndex-1];
24980         if(ps){
24981             node.previousSibling = ps;
24982             ps.nextSibling = node;
24983         }else{
24984             node.previousSibling = null;
24985         }
24986         node.nextSibling = refNode;
24987         refNode.previousSibling = node;
24988         node.setOwnerTree(this.getOwnerTree());
24989         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24990         if(oldParent){
24991             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24992         }
24993         return node;
24994     },
24995
24996     /**
24997      * Returns the child node at the specified index.
24998      * @param {Number} index
24999      * @return {Node}
25000      */
25001     item : function(index){
25002         return this.childNodes[index];
25003     },
25004
25005     /**
25006      * Replaces one child node in this node with another.
25007      * @param {Node} newChild The replacement node
25008      * @param {Node} oldChild The node to replace
25009      * @return {Node} The replaced node
25010      */
25011     replaceChild : function(newChild, oldChild){
25012         this.insertBefore(newChild, oldChild);
25013         this.removeChild(oldChild);
25014         return oldChild;
25015     },
25016
25017     /**
25018      * Returns the index of a child node
25019      * @param {Node} node
25020      * @return {Number} The index of the node or -1 if it was not found
25021      */
25022     indexOf : function(child){
25023         return this.childNodes.indexOf(child);
25024     },
25025
25026     /**
25027      * Returns the tree this node is in.
25028      * @return {Tree}
25029      */
25030     getOwnerTree : function(){
25031         // if it doesn't have one, look for one
25032         if(!this.ownerTree){
25033             var p = this;
25034             while(p){
25035                 if(p.ownerTree){
25036                     this.ownerTree = p.ownerTree;
25037                     break;
25038                 }
25039                 p = p.parentNode;
25040             }
25041         }
25042         return this.ownerTree;
25043     },
25044
25045     /**
25046      * Returns depth of this node (the root node has a depth of 0)
25047      * @return {Number}
25048      */
25049     getDepth : function(){
25050         var depth = 0;
25051         var p = this;
25052         while(p.parentNode){
25053             ++depth;
25054             p = p.parentNode;
25055         }
25056         return depth;
25057     },
25058
25059     // private
25060     setOwnerTree : function(tree){
25061         // if it's move, we need to update everyone
25062         if(tree != this.ownerTree){
25063             if(this.ownerTree){
25064                 this.ownerTree.unregisterNode(this);
25065             }
25066             this.ownerTree = tree;
25067             var cs = this.childNodes;
25068             for(var i = 0, len = cs.length; i < len; i++) {
25069                 cs[i].setOwnerTree(tree);
25070             }
25071             if(tree){
25072                 tree.registerNode(this);
25073             }
25074         }
25075     },
25076
25077     /**
25078      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25079      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25080      * @return {String} The path
25081      */
25082     getPath : function(attr){
25083         attr = attr || "id";
25084         var p = this.parentNode;
25085         var b = [this.attributes[attr]];
25086         while(p){
25087             b.unshift(p.attributes[attr]);
25088             p = p.parentNode;
25089         }
25090         var sep = this.getOwnerTree().pathSeparator;
25091         return sep + b.join(sep);
25092     },
25093
25094     /**
25095      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25096      * function call will be the scope provided or the current node. The arguments to the function
25097      * will be the args provided or the current node. If the function returns false at any point,
25098      * the bubble is stopped.
25099      * @param {Function} fn The function to call
25100      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25101      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25102      */
25103     bubble : function(fn, scope, args){
25104         var p = this;
25105         while(p){
25106             if(fn.call(scope || p, args || p) === false){
25107                 break;
25108             }
25109             p = p.parentNode;
25110         }
25111     },
25112
25113     /**
25114      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25115      * function call will be the scope provided or the current node. The arguments to the function
25116      * will be the args provided or the current node. If the function returns false at any point,
25117      * the cascade is stopped on that branch.
25118      * @param {Function} fn The function to call
25119      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25120      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25121      */
25122     cascade : function(fn, scope, args){
25123         if(fn.call(scope || this, args || this) !== false){
25124             var cs = this.childNodes;
25125             for(var i = 0, len = cs.length; i < len; i++) {
25126                 cs[i].cascade(fn, scope, args);
25127             }
25128         }
25129     },
25130
25131     /**
25132      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25133      * function call will be the scope provided or the current node. The arguments to the function
25134      * will be the args provided or the current node. If the function returns false at any point,
25135      * the iteration stops.
25136      * @param {Function} fn The function to call
25137      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25138      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25139      */
25140     eachChild : function(fn, scope, args){
25141         var cs = this.childNodes;
25142         for(var i = 0, len = cs.length; i < len; i++) {
25143                 if(fn.call(scope || this, args || cs[i]) === false){
25144                     break;
25145                 }
25146         }
25147     },
25148
25149     /**
25150      * Finds the first child that has the attribute with the specified value.
25151      * @param {String} attribute The attribute name
25152      * @param {Mixed} value The value to search for
25153      * @return {Node} The found child or null if none was found
25154      */
25155     findChild : function(attribute, value){
25156         var cs = this.childNodes;
25157         for(var i = 0, len = cs.length; i < len; i++) {
25158                 if(cs[i].attributes[attribute] == value){
25159                     return cs[i];
25160                 }
25161         }
25162         return null;
25163     },
25164
25165     /**
25166      * Finds the first child by a custom function. The child matches if the function passed
25167      * returns true.
25168      * @param {Function} fn
25169      * @param {Object} scope (optional)
25170      * @return {Node} The found child or null if none was found
25171      */
25172     findChildBy : function(fn, scope){
25173         var cs = this.childNodes;
25174         for(var i = 0, len = cs.length; i < len; i++) {
25175                 if(fn.call(scope||cs[i], cs[i]) === true){
25176                     return cs[i];
25177                 }
25178         }
25179         return null;
25180     },
25181
25182     /**
25183      * Sorts this nodes children using the supplied sort function
25184      * @param {Function} fn
25185      * @param {Object} scope (optional)
25186      */
25187     sort : function(fn, scope){
25188         var cs = this.childNodes;
25189         var len = cs.length;
25190         if(len > 0){
25191             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25192             cs.sort(sortFn);
25193             for(var i = 0; i < len; i++){
25194                 var n = cs[i];
25195                 n.previousSibling = cs[i-1];
25196                 n.nextSibling = cs[i+1];
25197                 if(i == 0){
25198                     this.setFirstChild(n);
25199                 }
25200                 if(i == len-1){
25201                     this.setLastChild(n);
25202                 }
25203             }
25204         }
25205     },
25206
25207     /**
25208      * Returns true if this node is an ancestor (at any point) of the passed node.
25209      * @param {Node} node
25210      * @return {Boolean}
25211      */
25212     contains : function(node){
25213         return node.isAncestor(this);
25214     },
25215
25216     /**
25217      * Returns true if the passed node is an ancestor (at any point) of this node.
25218      * @param {Node} node
25219      * @return {Boolean}
25220      */
25221     isAncestor : function(node){
25222         var p = this.parentNode;
25223         while(p){
25224             if(p == node){
25225                 return true;
25226             }
25227             p = p.parentNode;
25228         }
25229         return false;
25230     },
25231
25232     toString : function(){
25233         return "[Node"+(this.id?" "+this.id:"")+"]";
25234     }
25235 });/*
25236  * Based on:
25237  * Ext JS Library 1.1.1
25238  * Copyright(c) 2006-2007, Ext JS, LLC.
25239  *
25240  * Originally Released Under LGPL - original licence link has changed is not relivant.
25241  *
25242  * Fork - LGPL
25243  * <script type="text/javascript">
25244  */
25245  (function(){ 
25246 /**
25247  * @class Roo.Layer
25248  * @extends Roo.Element
25249  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25250  * automatic maintaining of shadow/shim positions.
25251  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25252  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25253  * you can pass a string with a CSS class name. False turns off the shadow.
25254  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25255  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25256  * @cfg {String} cls CSS class to add to the element
25257  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25258  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25259  * @constructor
25260  * @param {Object} config An object with config options.
25261  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25262  */
25263
25264 Roo.Layer = function(config, existingEl){
25265     config = config || {};
25266     var dh = Roo.DomHelper;
25267     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25268     if(existingEl){
25269         this.dom = Roo.getDom(existingEl);
25270     }
25271     if(!this.dom){
25272         var o = config.dh || {tag: "div", cls: "x-layer"};
25273         this.dom = dh.append(pel, o);
25274     }
25275     if(config.cls){
25276         this.addClass(config.cls);
25277     }
25278     this.constrain = config.constrain !== false;
25279     this.visibilityMode = Roo.Element.VISIBILITY;
25280     if(config.id){
25281         this.id = this.dom.id = config.id;
25282     }else{
25283         this.id = Roo.id(this.dom);
25284     }
25285     this.zindex = config.zindex || this.getZIndex();
25286     this.position("absolute", this.zindex);
25287     if(config.shadow){
25288         this.shadowOffset = config.shadowOffset || 4;
25289         this.shadow = new Roo.Shadow({
25290             offset : this.shadowOffset,
25291             mode : config.shadow
25292         });
25293     }else{
25294         this.shadowOffset = 0;
25295     }
25296     this.useShim = config.shim !== false && Roo.useShims;
25297     this.useDisplay = config.useDisplay;
25298     this.hide();
25299 };
25300
25301 var supr = Roo.Element.prototype;
25302
25303 // shims are shared among layer to keep from having 100 iframes
25304 var shims = [];
25305
25306 Roo.extend(Roo.Layer, Roo.Element, {
25307
25308     getZIndex : function(){
25309         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25310     },
25311
25312     getShim : function(){
25313         if(!this.useShim){
25314             return null;
25315         }
25316         if(this.shim){
25317             return this.shim;
25318         }
25319         var shim = shims.shift();
25320         if(!shim){
25321             shim = this.createShim();
25322             shim.enableDisplayMode('block');
25323             shim.dom.style.display = 'none';
25324             shim.dom.style.visibility = 'visible';
25325         }
25326         var pn = this.dom.parentNode;
25327         if(shim.dom.parentNode != pn){
25328             pn.insertBefore(shim.dom, this.dom);
25329         }
25330         shim.setStyle('z-index', this.getZIndex()-2);
25331         this.shim = shim;
25332         return shim;
25333     },
25334
25335     hideShim : function(){
25336         if(this.shim){
25337             this.shim.setDisplayed(false);
25338             shims.push(this.shim);
25339             delete this.shim;
25340         }
25341     },
25342
25343     disableShadow : function(){
25344         if(this.shadow){
25345             this.shadowDisabled = true;
25346             this.shadow.hide();
25347             this.lastShadowOffset = this.shadowOffset;
25348             this.shadowOffset = 0;
25349         }
25350     },
25351
25352     enableShadow : function(show){
25353         if(this.shadow){
25354             this.shadowDisabled = false;
25355             this.shadowOffset = this.lastShadowOffset;
25356             delete this.lastShadowOffset;
25357             if(show){
25358                 this.sync(true);
25359             }
25360         }
25361     },
25362
25363     // private
25364     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25365     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25366     sync : function(doShow){
25367         var sw = this.shadow;
25368         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25369             var sh = this.getShim();
25370
25371             var w = this.getWidth(),
25372                 h = this.getHeight();
25373
25374             var l = this.getLeft(true),
25375                 t = this.getTop(true);
25376
25377             if(sw && !this.shadowDisabled){
25378                 if(doShow && !sw.isVisible()){
25379                     sw.show(this);
25380                 }else{
25381                     sw.realign(l, t, w, h);
25382                 }
25383                 if(sh){
25384                     if(doShow){
25385                        sh.show();
25386                     }
25387                     // fit the shim behind the shadow, so it is shimmed too
25388                     var a = sw.adjusts, s = sh.dom.style;
25389                     s.left = (Math.min(l, l+a.l))+"px";
25390                     s.top = (Math.min(t, t+a.t))+"px";
25391                     s.width = (w+a.w)+"px";
25392                     s.height = (h+a.h)+"px";
25393                 }
25394             }else if(sh){
25395                 if(doShow){
25396                    sh.show();
25397                 }
25398                 sh.setSize(w, h);
25399                 sh.setLeftTop(l, t);
25400             }
25401             
25402         }
25403     },
25404
25405     // private
25406     destroy : function(){
25407         this.hideShim();
25408         if(this.shadow){
25409             this.shadow.hide();
25410         }
25411         this.removeAllListeners();
25412         var pn = this.dom.parentNode;
25413         if(pn){
25414             pn.removeChild(this.dom);
25415         }
25416         Roo.Element.uncache(this.id);
25417     },
25418
25419     remove : function(){
25420         this.destroy();
25421     },
25422
25423     // private
25424     beginUpdate : function(){
25425         this.updating = true;
25426     },
25427
25428     // private
25429     endUpdate : function(){
25430         this.updating = false;
25431         this.sync(true);
25432     },
25433
25434     // private
25435     hideUnders : function(negOffset){
25436         if(this.shadow){
25437             this.shadow.hide();
25438         }
25439         this.hideShim();
25440     },
25441
25442     // private
25443     constrainXY : function(){
25444         if(this.constrain){
25445             var vw = Roo.lib.Dom.getViewWidth(),
25446                 vh = Roo.lib.Dom.getViewHeight();
25447             var s = Roo.get(document).getScroll();
25448
25449             var xy = this.getXY();
25450             var x = xy[0], y = xy[1];   
25451             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25452             // only move it if it needs it
25453             var moved = false;
25454             // first validate right/bottom
25455             if((x + w) > vw+s.left){
25456                 x = vw - w - this.shadowOffset;
25457                 moved = true;
25458             }
25459             if((y + h) > vh+s.top){
25460                 y = vh - h - this.shadowOffset;
25461                 moved = true;
25462             }
25463             // then make sure top/left isn't negative
25464             if(x < s.left){
25465                 x = s.left;
25466                 moved = true;
25467             }
25468             if(y < s.top){
25469                 y = s.top;
25470                 moved = true;
25471             }
25472             if(moved){
25473                 if(this.avoidY){
25474                     var ay = this.avoidY;
25475                     if(y <= ay && (y+h) >= ay){
25476                         y = ay-h-5;   
25477                     }
25478                 }
25479                 xy = [x, y];
25480                 this.storeXY(xy);
25481                 supr.setXY.call(this, xy);
25482                 this.sync();
25483             }
25484         }
25485     },
25486
25487     isVisible : function(){
25488         return this.visible;    
25489     },
25490
25491     // private
25492     showAction : function(){
25493         this.visible = true; // track visibility to prevent getStyle calls
25494         if(this.useDisplay === true){
25495             this.setDisplayed("");
25496         }else if(this.lastXY){
25497             supr.setXY.call(this, this.lastXY);
25498         }else if(this.lastLT){
25499             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25500         }
25501     },
25502
25503     // private
25504     hideAction : function(){
25505         this.visible = false;
25506         if(this.useDisplay === true){
25507             this.setDisplayed(false);
25508         }else{
25509             this.setLeftTop(-10000,-10000);
25510         }
25511     },
25512
25513     // overridden Element method
25514     setVisible : function(v, a, d, c, e){
25515         if(v){
25516             this.showAction();
25517         }
25518         if(a && v){
25519             var cb = function(){
25520                 this.sync(true);
25521                 if(c){
25522                     c();
25523                 }
25524             }.createDelegate(this);
25525             supr.setVisible.call(this, true, true, d, cb, e);
25526         }else{
25527             if(!v){
25528                 this.hideUnders(true);
25529             }
25530             var cb = c;
25531             if(a){
25532                 cb = function(){
25533                     this.hideAction();
25534                     if(c){
25535                         c();
25536                     }
25537                 }.createDelegate(this);
25538             }
25539             supr.setVisible.call(this, v, a, d, cb, e);
25540             if(v){
25541                 this.sync(true);
25542             }else if(!a){
25543                 this.hideAction();
25544             }
25545         }
25546     },
25547
25548     storeXY : function(xy){
25549         delete this.lastLT;
25550         this.lastXY = xy;
25551     },
25552
25553     storeLeftTop : function(left, top){
25554         delete this.lastXY;
25555         this.lastLT = [left, top];
25556     },
25557
25558     // private
25559     beforeFx : function(){
25560         this.beforeAction();
25561         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25562     },
25563
25564     // private
25565     afterFx : function(){
25566         Roo.Layer.superclass.afterFx.apply(this, arguments);
25567         this.sync(this.isVisible());
25568     },
25569
25570     // private
25571     beforeAction : function(){
25572         if(!this.updating && this.shadow){
25573             this.shadow.hide();
25574         }
25575     },
25576
25577     // overridden Element method
25578     setLeft : function(left){
25579         this.storeLeftTop(left, this.getTop(true));
25580         supr.setLeft.apply(this, arguments);
25581         this.sync();
25582     },
25583
25584     setTop : function(top){
25585         this.storeLeftTop(this.getLeft(true), top);
25586         supr.setTop.apply(this, arguments);
25587         this.sync();
25588     },
25589
25590     setLeftTop : function(left, top){
25591         this.storeLeftTop(left, top);
25592         supr.setLeftTop.apply(this, arguments);
25593         this.sync();
25594     },
25595
25596     setXY : function(xy, a, d, c, e){
25597         this.fixDisplay();
25598         this.beforeAction();
25599         this.storeXY(xy);
25600         var cb = this.createCB(c);
25601         supr.setXY.call(this, xy, a, d, cb, e);
25602         if(!a){
25603             cb();
25604         }
25605     },
25606
25607     // private
25608     createCB : function(c){
25609         var el = this;
25610         return function(){
25611             el.constrainXY();
25612             el.sync(true);
25613             if(c){
25614                 c();
25615             }
25616         };
25617     },
25618
25619     // overridden Element method
25620     setX : function(x, a, d, c, e){
25621         this.setXY([x, this.getY()], a, d, c, e);
25622     },
25623
25624     // overridden Element method
25625     setY : function(y, a, d, c, e){
25626         this.setXY([this.getX(), y], a, d, c, e);
25627     },
25628
25629     // overridden Element method
25630     setSize : function(w, h, a, d, c, e){
25631         this.beforeAction();
25632         var cb = this.createCB(c);
25633         supr.setSize.call(this, w, h, a, d, cb, e);
25634         if(!a){
25635             cb();
25636         }
25637     },
25638
25639     // overridden Element method
25640     setWidth : function(w, a, d, c, e){
25641         this.beforeAction();
25642         var cb = this.createCB(c);
25643         supr.setWidth.call(this, w, a, d, cb, e);
25644         if(!a){
25645             cb();
25646         }
25647     },
25648
25649     // overridden Element method
25650     setHeight : function(h, a, d, c, e){
25651         this.beforeAction();
25652         var cb = this.createCB(c);
25653         supr.setHeight.call(this, h, a, d, cb, e);
25654         if(!a){
25655             cb();
25656         }
25657     },
25658
25659     // overridden Element method
25660     setBounds : function(x, y, w, h, a, d, c, e){
25661         this.beforeAction();
25662         var cb = this.createCB(c);
25663         if(!a){
25664             this.storeXY([x, y]);
25665             supr.setXY.call(this, [x, y]);
25666             supr.setSize.call(this, w, h, a, d, cb, e);
25667             cb();
25668         }else{
25669             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25670         }
25671         return this;
25672     },
25673     
25674     /**
25675      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25676      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25677      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25678      * @param {Number} zindex The new z-index to set
25679      * @return {this} The Layer
25680      */
25681     setZIndex : function(zindex){
25682         this.zindex = zindex;
25683         this.setStyle("z-index", zindex + 2);
25684         if(this.shadow){
25685             this.shadow.setZIndex(zindex + 1);
25686         }
25687         if(this.shim){
25688             this.shim.setStyle("z-index", zindex);
25689         }
25690     }
25691 });
25692 })();/*
25693  * Based on:
25694  * Ext JS Library 1.1.1
25695  * Copyright(c) 2006-2007, Ext JS, LLC.
25696  *
25697  * Originally Released Under LGPL - original licence link has changed is not relivant.
25698  *
25699  * Fork - LGPL
25700  * <script type="text/javascript">
25701  */
25702
25703
25704 /**
25705  * @class Roo.Shadow
25706  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25707  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25708  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25709  * @constructor
25710  * Create a new Shadow
25711  * @param {Object} config The config object
25712  */
25713 Roo.Shadow = function(config){
25714     Roo.apply(this, config);
25715     if(typeof this.mode != "string"){
25716         this.mode = this.defaultMode;
25717     }
25718     var o = this.offset, a = {h: 0};
25719     var rad = Math.floor(this.offset/2);
25720     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25721         case "drop":
25722             a.w = 0;
25723             a.l = a.t = o;
25724             a.t -= 1;
25725             if(Roo.isIE){
25726                 a.l -= this.offset + rad;
25727                 a.t -= this.offset + rad;
25728                 a.w -= rad;
25729                 a.h -= rad;
25730                 a.t += 1;
25731             }
25732         break;
25733         case "sides":
25734             a.w = (o*2);
25735             a.l = -o;
25736             a.t = o-1;
25737             if(Roo.isIE){
25738                 a.l -= (this.offset - rad);
25739                 a.t -= this.offset + rad;
25740                 a.l += 1;
25741                 a.w -= (this.offset - rad)*2;
25742                 a.w -= rad + 1;
25743                 a.h -= 1;
25744             }
25745         break;
25746         case "frame":
25747             a.w = a.h = (o*2);
25748             a.l = a.t = -o;
25749             a.t += 1;
25750             a.h -= 2;
25751             if(Roo.isIE){
25752                 a.l -= (this.offset - rad);
25753                 a.t -= (this.offset - rad);
25754                 a.l += 1;
25755                 a.w -= (this.offset + rad + 1);
25756                 a.h -= (this.offset + rad);
25757                 a.h += 1;
25758             }
25759         break;
25760     };
25761
25762     this.adjusts = a;
25763 };
25764
25765 Roo.Shadow.prototype = {
25766     /**
25767      * @cfg {String} mode
25768      * The shadow display mode.  Supports the following options:<br />
25769      * sides: Shadow displays on both sides and bottom only<br />
25770      * frame: Shadow displays equally on all four sides<br />
25771      * drop: Traditional bottom-right drop shadow (default)
25772      */
25773     /**
25774      * @cfg {String} offset
25775      * The number of pixels to offset the shadow from the element (defaults to 4)
25776      */
25777     offset: 4,
25778
25779     // private
25780     defaultMode: "drop",
25781
25782     /**
25783      * Displays the shadow under the target element
25784      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25785      */
25786     show : function(target){
25787         target = Roo.get(target);
25788         if(!this.el){
25789             this.el = Roo.Shadow.Pool.pull();
25790             if(this.el.dom.nextSibling != target.dom){
25791                 this.el.insertBefore(target);
25792             }
25793         }
25794         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25795         if(Roo.isIE){
25796             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25797         }
25798         this.realign(
25799             target.getLeft(true),
25800             target.getTop(true),
25801             target.getWidth(),
25802             target.getHeight()
25803         );
25804         this.el.dom.style.display = "block";
25805     },
25806
25807     /**
25808      * Returns true if the shadow is visible, else false
25809      */
25810     isVisible : function(){
25811         return this.el ? true : false;  
25812     },
25813
25814     /**
25815      * Direct alignment when values are already available. Show must be called at least once before
25816      * calling this method to ensure it is initialized.
25817      * @param {Number} left The target element left position
25818      * @param {Number} top The target element top position
25819      * @param {Number} width The target element width
25820      * @param {Number} height The target element height
25821      */
25822     realign : function(l, t, w, h){
25823         if(!this.el){
25824             return;
25825         }
25826         var a = this.adjusts, d = this.el.dom, s = d.style;
25827         var iea = 0;
25828         s.left = (l+a.l)+"px";
25829         s.top = (t+a.t)+"px";
25830         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25831  
25832         if(s.width != sws || s.height != shs){
25833             s.width = sws;
25834             s.height = shs;
25835             if(!Roo.isIE){
25836                 var cn = d.childNodes;
25837                 var sww = Math.max(0, (sw-12))+"px";
25838                 cn[0].childNodes[1].style.width = sww;
25839                 cn[1].childNodes[1].style.width = sww;
25840                 cn[2].childNodes[1].style.width = sww;
25841                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25842             }
25843         }
25844     },
25845
25846     /**
25847      * Hides this shadow
25848      */
25849     hide : function(){
25850         if(this.el){
25851             this.el.dom.style.display = "none";
25852             Roo.Shadow.Pool.push(this.el);
25853             delete this.el;
25854         }
25855     },
25856
25857     /**
25858      * Adjust the z-index of this shadow
25859      * @param {Number} zindex The new z-index
25860      */
25861     setZIndex : function(z){
25862         this.zIndex = z;
25863         if(this.el){
25864             this.el.setStyle("z-index", z);
25865         }
25866     }
25867 };
25868
25869 // Private utility class that manages the internal Shadow cache
25870 Roo.Shadow.Pool = function(){
25871     var p = [];
25872     var markup = Roo.isIE ?
25873                  '<div class="x-ie-shadow"></div>' :
25874                  '<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>';
25875     return {
25876         pull : function(){
25877             var sh = p.shift();
25878             if(!sh){
25879                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25880                 sh.autoBoxAdjust = false;
25881             }
25882             return sh;
25883         },
25884
25885         push : function(sh){
25886             p.push(sh);
25887         }
25888     };
25889 }();/*
25890  * Based on:
25891  * Ext JS Library 1.1.1
25892  * Copyright(c) 2006-2007, Ext JS, LLC.
25893  *
25894  * Originally Released Under LGPL - original licence link has changed is not relivant.
25895  *
25896  * Fork - LGPL
25897  * <script type="text/javascript">
25898  */
25899
25900
25901 /**
25902  * @class Roo.SplitBar
25903  * @extends Roo.util.Observable
25904  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25905  * <br><br>
25906  * Usage:
25907  * <pre><code>
25908 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25909                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25910 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25911 split.minSize = 100;
25912 split.maxSize = 600;
25913 split.animate = true;
25914 split.on('moved', splitterMoved);
25915 </code></pre>
25916  * @constructor
25917  * Create a new SplitBar
25918  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25919  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25920  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25921  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25922                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25923                         position of the SplitBar).
25924  */
25925 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25926     
25927     /** @private */
25928     this.el = Roo.get(dragElement, true);
25929     this.el.dom.unselectable = "on";
25930     /** @private */
25931     this.resizingEl = Roo.get(resizingElement, true);
25932
25933     /**
25934      * @private
25935      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25936      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25937      * @type Number
25938      */
25939     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25940     
25941     /**
25942      * The minimum size of the resizing element. (Defaults to 0)
25943      * @type Number
25944      */
25945     this.minSize = 0;
25946     
25947     /**
25948      * The maximum size of the resizing element. (Defaults to 2000)
25949      * @type Number
25950      */
25951     this.maxSize = 2000;
25952     
25953     /**
25954      * Whether to animate the transition to the new size
25955      * @type Boolean
25956      */
25957     this.animate = false;
25958     
25959     /**
25960      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25961      * @type Boolean
25962      */
25963     this.useShim = false;
25964     
25965     /** @private */
25966     this.shim = null;
25967     
25968     if(!existingProxy){
25969         /** @private */
25970         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25971     }else{
25972         this.proxy = Roo.get(existingProxy).dom;
25973     }
25974     /** @private */
25975     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25976     
25977     /** @private */
25978     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25979     
25980     /** @private */
25981     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25982     
25983     /** @private */
25984     this.dragSpecs = {};
25985     
25986     /**
25987      * @private The adapter to use to positon and resize elements
25988      */
25989     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25990     this.adapter.init(this);
25991     
25992     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25993         /** @private */
25994         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25995         this.el.addClass("x-splitbar-h");
25996     }else{
25997         /** @private */
25998         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25999         this.el.addClass("x-splitbar-v");
26000     }
26001     
26002     this.addEvents({
26003         /**
26004          * @event resize
26005          * Fires when the splitter is moved (alias for {@link #event-moved})
26006          * @param {Roo.SplitBar} this
26007          * @param {Number} newSize the new width or height
26008          */
26009         "resize" : true,
26010         /**
26011          * @event moved
26012          * Fires when the splitter is moved
26013          * @param {Roo.SplitBar} this
26014          * @param {Number} newSize the new width or height
26015          */
26016         "moved" : true,
26017         /**
26018          * @event beforeresize
26019          * Fires before the splitter is dragged
26020          * @param {Roo.SplitBar} this
26021          */
26022         "beforeresize" : true,
26023
26024         "beforeapply" : true
26025     });
26026
26027     Roo.util.Observable.call(this);
26028 };
26029
26030 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26031     onStartProxyDrag : function(x, y){
26032         this.fireEvent("beforeresize", this);
26033         if(!this.overlay){
26034             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26035             o.unselectable();
26036             o.enableDisplayMode("block");
26037             // all splitbars share the same overlay
26038             Roo.SplitBar.prototype.overlay = o;
26039         }
26040         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26041         this.overlay.show();
26042         Roo.get(this.proxy).setDisplayed("block");
26043         var size = this.adapter.getElementSize(this);
26044         this.activeMinSize = this.getMinimumSize();;
26045         this.activeMaxSize = this.getMaximumSize();;
26046         var c1 = size - this.activeMinSize;
26047         var c2 = Math.max(this.activeMaxSize - size, 0);
26048         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26049             this.dd.resetConstraints();
26050             this.dd.setXConstraint(
26051                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26052                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26053             );
26054             this.dd.setYConstraint(0, 0);
26055         }else{
26056             this.dd.resetConstraints();
26057             this.dd.setXConstraint(0, 0);
26058             this.dd.setYConstraint(
26059                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26060                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26061             );
26062          }
26063         this.dragSpecs.startSize = size;
26064         this.dragSpecs.startPoint = [x, y];
26065         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26066     },
26067     
26068     /** 
26069      * @private Called after the drag operation by the DDProxy
26070      */
26071     onEndProxyDrag : function(e){
26072         Roo.get(this.proxy).setDisplayed(false);
26073         var endPoint = Roo.lib.Event.getXY(e);
26074         if(this.overlay){
26075             this.overlay.hide();
26076         }
26077         var newSize;
26078         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26079             newSize = this.dragSpecs.startSize + 
26080                 (this.placement == Roo.SplitBar.LEFT ?
26081                     endPoint[0] - this.dragSpecs.startPoint[0] :
26082                     this.dragSpecs.startPoint[0] - endPoint[0]
26083                 );
26084         }else{
26085             newSize = this.dragSpecs.startSize + 
26086                 (this.placement == Roo.SplitBar.TOP ?
26087                     endPoint[1] - this.dragSpecs.startPoint[1] :
26088                     this.dragSpecs.startPoint[1] - endPoint[1]
26089                 );
26090         }
26091         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26092         if(newSize != this.dragSpecs.startSize){
26093             if(this.fireEvent('beforeapply', this, newSize) !== false){
26094                 this.adapter.setElementSize(this, newSize);
26095                 this.fireEvent("moved", this, newSize);
26096                 this.fireEvent("resize", this, newSize);
26097             }
26098         }
26099     },
26100     
26101     /**
26102      * Get the adapter this SplitBar uses
26103      * @return The adapter object
26104      */
26105     getAdapter : function(){
26106         return this.adapter;
26107     },
26108     
26109     /**
26110      * Set the adapter this SplitBar uses
26111      * @param {Object} adapter A SplitBar adapter object
26112      */
26113     setAdapter : function(adapter){
26114         this.adapter = adapter;
26115         this.adapter.init(this);
26116     },
26117     
26118     /**
26119      * Gets the minimum size for the resizing element
26120      * @return {Number} The minimum size
26121      */
26122     getMinimumSize : function(){
26123         return this.minSize;
26124     },
26125     
26126     /**
26127      * Sets the minimum size for the resizing element
26128      * @param {Number} minSize The minimum size
26129      */
26130     setMinimumSize : function(minSize){
26131         this.minSize = minSize;
26132     },
26133     
26134     /**
26135      * Gets the maximum size for the resizing element
26136      * @return {Number} The maximum size
26137      */
26138     getMaximumSize : function(){
26139         return this.maxSize;
26140     },
26141     
26142     /**
26143      * Sets the maximum size for the resizing element
26144      * @param {Number} maxSize The maximum size
26145      */
26146     setMaximumSize : function(maxSize){
26147         this.maxSize = maxSize;
26148     },
26149     
26150     /**
26151      * Sets the initialize size for the resizing element
26152      * @param {Number} size The initial size
26153      */
26154     setCurrentSize : function(size){
26155         var oldAnimate = this.animate;
26156         this.animate = false;
26157         this.adapter.setElementSize(this, size);
26158         this.animate = oldAnimate;
26159     },
26160     
26161     /**
26162      * Destroy this splitbar. 
26163      * @param {Boolean} removeEl True to remove the element
26164      */
26165     destroy : function(removeEl){
26166         if(this.shim){
26167             this.shim.remove();
26168         }
26169         this.dd.unreg();
26170         this.proxy.parentNode.removeChild(this.proxy);
26171         if(removeEl){
26172             this.el.remove();
26173         }
26174     }
26175 });
26176
26177 /**
26178  * @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.
26179  */
26180 Roo.SplitBar.createProxy = function(dir){
26181     var proxy = new Roo.Element(document.createElement("div"));
26182     proxy.unselectable();
26183     var cls = 'x-splitbar-proxy';
26184     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26185     document.body.appendChild(proxy.dom);
26186     return proxy.dom;
26187 };
26188
26189 /** 
26190  * @class Roo.SplitBar.BasicLayoutAdapter
26191  * Default Adapter. It assumes the splitter and resizing element are not positioned
26192  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26193  */
26194 Roo.SplitBar.BasicLayoutAdapter = function(){
26195 };
26196
26197 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26198     // do nothing for now
26199     init : function(s){
26200     
26201     },
26202     /**
26203      * Called before drag operations to get the current size of the resizing element. 
26204      * @param {Roo.SplitBar} s The SplitBar using this adapter
26205      */
26206      getElementSize : function(s){
26207         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26208             return s.resizingEl.getWidth();
26209         }else{
26210             return s.resizingEl.getHeight();
26211         }
26212     },
26213     
26214     /**
26215      * Called after drag operations to set the size of the resizing element.
26216      * @param {Roo.SplitBar} s The SplitBar using this adapter
26217      * @param {Number} newSize The new size to set
26218      * @param {Function} onComplete A function to be invoked when resizing is complete
26219      */
26220     setElementSize : function(s, newSize, onComplete){
26221         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26222             if(!s.animate){
26223                 s.resizingEl.setWidth(newSize);
26224                 if(onComplete){
26225                     onComplete(s, newSize);
26226                 }
26227             }else{
26228                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26229             }
26230         }else{
26231             
26232             if(!s.animate){
26233                 s.resizingEl.setHeight(newSize);
26234                 if(onComplete){
26235                     onComplete(s, newSize);
26236                 }
26237             }else{
26238                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26239             }
26240         }
26241     }
26242 };
26243
26244 /** 
26245  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26246  * @extends Roo.SplitBar.BasicLayoutAdapter
26247  * Adapter that  moves the splitter element to align with the resized sizing element. 
26248  * Used with an absolute positioned SplitBar.
26249  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26250  * document.body, make sure you assign an id to the body element.
26251  */
26252 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26253     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26254     this.container = Roo.get(container);
26255 };
26256
26257 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26258     init : function(s){
26259         this.basic.init(s);
26260     },
26261     
26262     getElementSize : function(s){
26263         return this.basic.getElementSize(s);
26264     },
26265     
26266     setElementSize : function(s, newSize, onComplete){
26267         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26268     },
26269     
26270     moveSplitter : function(s){
26271         var yes = Roo.SplitBar;
26272         switch(s.placement){
26273             case yes.LEFT:
26274                 s.el.setX(s.resizingEl.getRight());
26275                 break;
26276             case yes.RIGHT:
26277                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26278                 break;
26279             case yes.TOP:
26280                 s.el.setY(s.resizingEl.getBottom());
26281                 break;
26282             case yes.BOTTOM:
26283                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26284                 break;
26285         }
26286     }
26287 };
26288
26289 /**
26290  * Orientation constant - Create a vertical SplitBar
26291  * @static
26292  * @type Number
26293  */
26294 Roo.SplitBar.VERTICAL = 1;
26295
26296 /**
26297  * Orientation constant - Create a horizontal SplitBar
26298  * @static
26299  * @type Number
26300  */
26301 Roo.SplitBar.HORIZONTAL = 2;
26302
26303 /**
26304  * Placement constant - The resizing element is to the left of the splitter element
26305  * @static
26306  * @type Number
26307  */
26308 Roo.SplitBar.LEFT = 1;
26309
26310 /**
26311  * Placement constant - The resizing element is to the right of the splitter element
26312  * @static
26313  * @type Number
26314  */
26315 Roo.SplitBar.RIGHT = 2;
26316
26317 /**
26318  * Placement constant - The resizing element is positioned above the splitter element
26319  * @static
26320  * @type Number
26321  */
26322 Roo.SplitBar.TOP = 3;
26323
26324 /**
26325  * Placement constant - The resizing element is positioned under splitter element
26326  * @static
26327  * @type Number
26328  */
26329 Roo.SplitBar.BOTTOM = 4;
26330 /*
26331  * Based on:
26332  * Ext JS Library 1.1.1
26333  * Copyright(c) 2006-2007, Ext JS, LLC.
26334  *
26335  * Originally Released Under LGPL - original licence link has changed is not relivant.
26336  *
26337  * Fork - LGPL
26338  * <script type="text/javascript">
26339  */
26340
26341 /**
26342  * @class Roo.View
26343  * @extends Roo.util.Observable
26344  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26345  * This class also supports single and multi selection modes. <br>
26346  * Create a data model bound view:
26347  <pre><code>
26348  var store = new Roo.data.Store(...);
26349
26350  var view = new Roo.View({
26351     el : "my-element",
26352     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26353  
26354     singleSelect: true,
26355     selectedClass: "ydataview-selected",
26356     store: store
26357  });
26358
26359  // listen for node click?
26360  view.on("click", function(vw, index, node, e){
26361  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26362  });
26363
26364  // load XML data
26365  dataModel.load("foobar.xml");
26366  </code></pre>
26367  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26368  * <br><br>
26369  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26370  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26371  * 
26372  * Note: old style constructor is still suported (container, template, config)
26373  * 
26374  * @constructor
26375  * Create a new View
26376  * @param {Object} config The config object
26377  * 
26378  */
26379 Roo.View = function(config, depreciated_tpl, depreciated_config){
26380     
26381     this.parent = false;
26382     
26383     if (typeof(depreciated_tpl) == 'undefined') {
26384         // new way.. - universal constructor.
26385         Roo.apply(this, config);
26386         this.el  = Roo.get(this.el);
26387     } else {
26388         // old format..
26389         this.el  = Roo.get(config);
26390         this.tpl = depreciated_tpl;
26391         Roo.apply(this, depreciated_config);
26392     }
26393     this.wrapEl  = this.el.wrap().wrap();
26394     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26395     
26396     
26397     if(typeof(this.tpl) == "string"){
26398         this.tpl = new Roo.Template(this.tpl);
26399     } else {
26400         // support xtype ctors..
26401         this.tpl = new Roo.factory(this.tpl, Roo);
26402     }
26403     
26404     
26405     this.tpl.compile();
26406     
26407     /** @private */
26408     this.addEvents({
26409         /**
26410          * @event beforeclick
26411          * Fires before a click is processed. Returns false to cancel the default action.
26412          * @param {Roo.View} this
26413          * @param {Number} index The index of the target node
26414          * @param {HTMLElement} node The target node
26415          * @param {Roo.EventObject} e The raw event object
26416          */
26417             "beforeclick" : true,
26418         /**
26419          * @event click
26420          * Fires when a template node is clicked.
26421          * @param {Roo.View} this
26422          * @param {Number} index The index of the target node
26423          * @param {HTMLElement} node The target node
26424          * @param {Roo.EventObject} e The raw event object
26425          */
26426             "click" : true,
26427         /**
26428          * @event dblclick
26429          * Fires when a template node is double clicked.
26430          * @param {Roo.View} this
26431          * @param {Number} index The index of the target node
26432          * @param {HTMLElement} node The target node
26433          * @param {Roo.EventObject} e The raw event object
26434          */
26435             "dblclick" : true,
26436         /**
26437          * @event contextmenu
26438          * Fires when a template node is right clicked.
26439          * @param {Roo.View} this
26440          * @param {Number} index The index of the target node
26441          * @param {HTMLElement} node The target node
26442          * @param {Roo.EventObject} e The raw event object
26443          */
26444             "contextmenu" : true,
26445         /**
26446          * @event selectionchange
26447          * Fires when the selected nodes change.
26448          * @param {Roo.View} this
26449          * @param {Array} selections Array of the selected nodes
26450          */
26451             "selectionchange" : true,
26452     
26453         /**
26454          * @event beforeselect
26455          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26456          * @param {Roo.View} this
26457          * @param {HTMLElement} node The node to be selected
26458          * @param {Array} selections Array of currently selected nodes
26459          */
26460             "beforeselect" : true,
26461         /**
26462          * @event preparedata
26463          * Fires on every row to render, to allow you to change the data.
26464          * @param {Roo.View} this
26465          * @param {Object} data to be rendered (change this)
26466          */
26467           "preparedata" : true
26468           
26469           
26470         });
26471
26472
26473
26474     this.el.on({
26475         "click": this.onClick,
26476         "dblclick": this.onDblClick,
26477         "contextmenu": this.onContextMenu,
26478         scope:this
26479     });
26480
26481     this.selections = [];
26482     this.nodes = [];
26483     this.cmp = new Roo.CompositeElementLite([]);
26484     if(this.store){
26485         this.store = Roo.factory(this.store, Roo.data);
26486         this.setStore(this.store, true);
26487     }
26488     
26489     if ( this.footer && this.footer.xtype) {
26490            
26491          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26492         
26493         this.footer.dataSource = this.store;
26494         this.footer.container = fctr;
26495         this.footer = Roo.factory(this.footer, Roo);
26496         fctr.insertFirst(this.el);
26497         
26498         // this is a bit insane - as the paging toolbar seems to detach the el..
26499 //        dom.parentNode.parentNode.parentNode
26500          // they get detached?
26501     }
26502     
26503     
26504     Roo.View.superclass.constructor.call(this);
26505     
26506     
26507 };
26508
26509 Roo.extend(Roo.View, Roo.util.Observable, {
26510     
26511      /**
26512      * @cfg {Roo.data.Store} store Data store to load data from.
26513      */
26514     store : false,
26515     
26516     /**
26517      * @cfg {String|Roo.Element} el The container element.
26518      */
26519     el : '',
26520     
26521     /**
26522      * @cfg {String|Roo.Template} tpl The template used by this View 
26523      */
26524     tpl : false,
26525     /**
26526      * @cfg {String} dataName the named area of the template to use as the data area
26527      *                          Works with domtemplates roo-name="name"
26528      */
26529     dataName: false,
26530     /**
26531      * @cfg {String} selectedClass The css class to add to selected nodes
26532      */
26533     selectedClass : "x-view-selected",
26534      /**
26535      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26536      */
26537     emptyText : "",
26538     
26539     /**
26540      * @cfg {String} text to display on mask (default Loading)
26541      */
26542     mask : false,
26543     /**
26544      * @cfg {Boolean} multiSelect Allow multiple selection
26545      */
26546     multiSelect : false,
26547     /**
26548      * @cfg {Boolean} singleSelect Allow single selection
26549      */
26550     singleSelect:  false,
26551     
26552     /**
26553      * @cfg {Boolean} toggleSelect - selecting 
26554      */
26555     toggleSelect : false,
26556     
26557     /**
26558      * @cfg {Boolean} tickable - selecting 
26559      */
26560     tickable : false,
26561     
26562     /**
26563      * Returns the element this view is bound to.
26564      * @return {Roo.Element}
26565      */
26566     getEl : function(){
26567         return this.wrapEl;
26568     },
26569     
26570     
26571
26572     /**
26573      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26574      */
26575     refresh : function(){
26576         //Roo.log('refresh');
26577         var t = this.tpl;
26578         
26579         // if we are using something like 'domtemplate', then
26580         // the what gets used is:
26581         // t.applySubtemplate(NAME, data, wrapping data..)
26582         // the outer template then get' applied with
26583         //     the store 'extra data'
26584         // and the body get's added to the
26585         //      roo-name="data" node?
26586         //      <span class='roo-tpl-{name}'></span> ?????
26587         
26588         
26589         
26590         this.clearSelections();
26591         this.el.update("");
26592         var html = [];
26593         var records = this.store.getRange();
26594         if(records.length < 1) {
26595             
26596             // is this valid??  = should it render a template??
26597             
26598             this.el.update(this.emptyText);
26599             return;
26600         }
26601         var el = this.el;
26602         if (this.dataName) {
26603             this.el.update(t.apply(this.store.meta)); //????
26604             el = this.el.child('.roo-tpl-' + this.dataName);
26605         }
26606         
26607         for(var i = 0, len = records.length; i < len; i++){
26608             var data = this.prepareData(records[i].data, i, records[i]);
26609             this.fireEvent("preparedata", this, data, i, records[i]);
26610             
26611             var d = Roo.apply({}, data);
26612             
26613             if(this.tickable){
26614                 Roo.apply(d, {'roo-id' : Roo.id()});
26615                 
26616                 var _this = this;
26617             
26618                 Roo.each(this.parent.item, function(item){
26619                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26620                         return;
26621                     }
26622                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26623                 });
26624             }
26625             
26626             html[html.length] = Roo.util.Format.trim(
26627                 this.dataName ?
26628                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26629                     t.apply(d)
26630             );
26631         }
26632         
26633         
26634         
26635         el.update(html.join(""));
26636         this.nodes = el.dom.childNodes;
26637         this.updateIndexes(0);
26638     },
26639     
26640
26641     /**
26642      * Function to override to reformat the data that is sent to
26643      * the template for each node.
26644      * DEPRICATED - use the preparedata event handler.
26645      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26646      * a JSON object for an UpdateManager bound view).
26647      */
26648     prepareData : function(data, index, record)
26649     {
26650         this.fireEvent("preparedata", this, data, index, record);
26651         return data;
26652     },
26653
26654     onUpdate : function(ds, record){
26655         // Roo.log('on update');   
26656         this.clearSelections();
26657         var index = this.store.indexOf(record);
26658         var n = this.nodes[index];
26659         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26660         n.parentNode.removeChild(n);
26661         this.updateIndexes(index, index);
26662     },
26663
26664     
26665     
26666 // --------- FIXME     
26667     onAdd : function(ds, records, index)
26668     {
26669         //Roo.log(['on Add', ds, records, index] );        
26670         this.clearSelections();
26671         if(this.nodes.length == 0){
26672             this.refresh();
26673             return;
26674         }
26675         var n = this.nodes[index];
26676         for(var i = 0, len = records.length; i < len; i++){
26677             var d = this.prepareData(records[i].data, i, records[i]);
26678             if(n){
26679                 this.tpl.insertBefore(n, d);
26680             }else{
26681                 
26682                 this.tpl.append(this.el, d);
26683             }
26684         }
26685         this.updateIndexes(index);
26686     },
26687
26688     onRemove : function(ds, record, index){
26689        // Roo.log('onRemove');
26690         this.clearSelections();
26691         var el = this.dataName  ?
26692             this.el.child('.roo-tpl-' + this.dataName) :
26693             this.el; 
26694         
26695         el.dom.removeChild(this.nodes[index]);
26696         this.updateIndexes(index);
26697     },
26698
26699     /**
26700      * Refresh an individual node.
26701      * @param {Number} index
26702      */
26703     refreshNode : function(index){
26704         this.onUpdate(this.store, this.store.getAt(index));
26705     },
26706
26707     updateIndexes : function(startIndex, endIndex){
26708         var ns = this.nodes;
26709         startIndex = startIndex || 0;
26710         endIndex = endIndex || ns.length - 1;
26711         for(var i = startIndex; i <= endIndex; i++){
26712             ns[i].nodeIndex = i;
26713         }
26714     },
26715
26716     /**
26717      * Changes the data store this view uses and refresh the view.
26718      * @param {Store} store
26719      */
26720     setStore : function(store, initial){
26721         if(!initial && this.store){
26722             this.store.un("datachanged", this.refresh);
26723             this.store.un("add", this.onAdd);
26724             this.store.un("remove", this.onRemove);
26725             this.store.un("update", this.onUpdate);
26726             this.store.un("clear", this.refresh);
26727             this.store.un("beforeload", this.onBeforeLoad);
26728             this.store.un("load", this.onLoad);
26729             this.store.un("loadexception", this.onLoad);
26730         }
26731         if(store){
26732           
26733             store.on("datachanged", this.refresh, this);
26734             store.on("add", this.onAdd, this);
26735             store.on("remove", this.onRemove, this);
26736             store.on("update", this.onUpdate, this);
26737             store.on("clear", this.refresh, this);
26738             store.on("beforeload", this.onBeforeLoad, this);
26739             store.on("load", this.onLoad, this);
26740             store.on("loadexception", this.onLoad, this);
26741         }
26742         
26743         if(store){
26744             this.refresh();
26745         }
26746     },
26747     /**
26748      * onbeforeLoad - masks the loading area.
26749      *
26750      */
26751     onBeforeLoad : function(store,opts)
26752     {
26753          //Roo.log('onBeforeLoad');   
26754         if (!opts.add) {
26755             this.el.update("");
26756         }
26757         this.el.mask(this.mask ? this.mask : "Loading" ); 
26758     },
26759     onLoad : function ()
26760     {
26761         this.el.unmask();
26762     },
26763     
26764
26765     /**
26766      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26767      * @param {HTMLElement} node
26768      * @return {HTMLElement} The template node
26769      */
26770     findItemFromChild : function(node){
26771         var el = this.dataName  ?
26772             this.el.child('.roo-tpl-' + this.dataName,true) :
26773             this.el.dom; 
26774         
26775         if(!node || node.parentNode == el){
26776                     return node;
26777             }
26778             var p = node.parentNode;
26779             while(p && p != el){
26780             if(p.parentNode == el){
26781                 return p;
26782             }
26783             p = p.parentNode;
26784         }
26785             return null;
26786     },
26787
26788     /** @ignore */
26789     onClick : function(e){
26790         var item = this.findItemFromChild(e.getTarget());
26791         if(item){
26792             var index = this.indexOf(item);
26793             if(this.onItemClick(item, index, e) !== false){
26794                 this.fireEvent("click", this, index, item, e);
26795             }
26796         }else{
26797             this.clearSelections();
26798         }
26799     },
26800
26801     /** @ignore */
26802     onContextMenu : function(e){
26803         var item = this.findItemFromChild(e.getTarget());
26804         if(item){
26805             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26806         }
26807     },
26808
26809     /** @ignore */
26810     onDblClick : function(e){
26811         var item = this.findItemFromChild(e.getTarget());
26812         if(item){
26813             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26814         }
26815     },
26816
26817     onItemClick : function(item, index, e)
26818     {
26819         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26820             return false;
26821         }
26822         if (this.toggleSelect) {
26823             var m = this.isSelected(item) ? 'unselect' : 'select';
26824             //Roo.log(m);
26825             var _t = this;
26826             _t[m](item, true, false);
26827             return true;
26828         }
26829         if(this.multiSelect || this.singleSelect){
26830             if(this.multiSelect && e.shiftKey && this.lastSelection){
26831                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26832             }else{
26833                 this.select(item, this.multiSelect && e.ctrlKey);
26834                 this.lastSelection = item;
26835             }
26836             
26837             if(!this.tickable){
26838                 e.preventDefault();
26839             }
26840             
26841         }
26842         return true;
26843     },
26844
26845     /**
26846      * Get the number of selected nodes.
26847      * @return {Number}
26848      */
26849     getSelectionCount : function(){
26850         return this.selections.length;
26851     },
26852
26853     /**
26854      * Get the currently selected nodes.
26855      * @return {Array} An array of HTMLElements
26856      */
26857     getSelectedNodes : function(){
26858         return this.selections;
26859     },
26860
26861     /**
26862      * Get the indexes of the selected nodes.
26863      * @return {Array}
26864      */
26865     getSelectedIndexes : function(){
26866         var indexes = [], s = this.selections;
26867         for(var i = 0, len = s.length; i < len; i++){
26868             indexes.push(s[i].nodeIndex);
26869         }
26870         return indexes;
26871     },
26872
26873     /**
26874      * Clear all selections
26875      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26876      */
26877     clearSelections : function(suppressEvent){
26878         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26879             this.cmp.elements = this.selections;
26880             this.cmp.removeClass(this.selectedClass);
26881             this.selections = [];
26882             if(!suppressEvent){
26883                 this.fireEvent("selectionchange", this, this.selections);
26884             }
26885         }
26886     },
26887
26888     /**
26889      * Returns true if the passed node is selected
26890      * @param {HTMLElement/Number} node The node or node index
26891      * @return {Boolean}
26892      */
26893     isSelected : function(node){
26894         var s = this.selections;
26895         if(s.length < 1){
26896             return false;
26897         }
26898         node = this.getNode(node);
26899         return s.indexOf(node) !== -1;
26900     },
26901
26902     /**
26903      * Selects nodes.
26904      * @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
26905      * @param {Boolean} keepExisting (optional) true to keep existing selections
26906      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26907      */
26908     select : function(nodeInfo, keepExisting, suppressEvent){
26909         if(nodeInfo instanceof Array){
26910             if(!keepExisting){
26911                 this.clearSelections(true);
26912             }
26913             for(var i = 0, len = nodeInfo.length; i < len; i++){
26914                 this.select(nodeInfo[i], true, true);
26915             }
26916             return;
26917         } 
26918         var node = this.getNode(nodeInfo);
26919         if(!node || this.isSelected(node)){
26920             return; // already selected.
26921         }
26922         if(!keepExisting){
26923             this.clearSelections(true);
26924         }
26925         
26926         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26927             Roo.fly(node).addClass(this.selectedClass);
26928             this.selections.push(node);
26929             if(!suppressEvent){
26930                 this.fireEvent("selectionchange", this, this.selections);
26931             }
26932         }
26933         
26934         
26935     },
26936       /**
26937      * Unselects nodes.
26938      * @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
26939      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26940      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26941      */
26942     unselect : function(nodeInfo, keepExisting, suppressEvent)
26943     {
26944         if(nodeInfo instanceof Array){
26945             Roo.each(this.selections, function(s) {
26946                 this.unselect(s, nodeInfo);
26947             }, this);
26948             return;
26949         }
26950         var node = this.getNode(nodeInfo);
26951         if(!node || !this.isSelected(node)){
26952             //Roo.log("not selected");
26953             return; // not selected.
26954         }
26955         // fireevent???
26956         var ns = [];
26957         Roo.each(this.selections, function(s) {
26958             if (s == node ) {
26959                 Roo.fly(node).removeClass(this.selectedClass);
26960
26961                 return;
26962             }
26963             ns.push(s);
26964         },this);
26965         
26966         this.selections= ns;
26967         this.fireEvent("selectionchange", this, this.selections);
26968     },
26969
26970     /**
26971      * Gets a template node.
26972      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26973      * @return {HTMLElement} The node or null if it wasn't found
26974      */
26975     getNode : function(nodeInfo){
26976         if(typeof nodeInfo == "string"){
26977             return document.getElementById(nodeInfo);
26978         }else if(typeof nodeInfo == "number"){
26979             return this.nodes[nodeInfo];
26980         }
26981         return nodeInfo;
26982     },
26983
26984     /**
26985      * Gets a range template nodes.
26986      * @param {Number} startIndex
26987      * @param {Number} endIndex
26988      * @return {Array} An array of nodes
26989      */
26990     getNodes : function(start, end){
26991         var ns = this.nodes;
26992         start = start || 0;
26993         end = typeof end == "undefined" ? ns.length - 1 : end;
26994         var nodes = [];
26995         if(start <= end){
26996             for(var i = start; i <= end; i++){
26997                 nodes.push(ns[i]);
26998             }
26999         } else{
27000             for(var i = start; i >= end; i--){
27001                 nodes.push(ns[i]);
27002             }
27003         }
27004         return nodes;
27005     },
27006
27007     /**
27008      * Finds the index of the passed node
27009      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27010      * @return {Number} The index of the node or -1
27011      */
27012     indexOf : function(node){
27013         node = this.getNode(node);
27014         if(typeof node.nodeIndex == "number"){
27015             return node.nodeIndex;
27016         }
27017         var ns = this.nodes;
27018         for(var i = 0, len = ns.length; i < len; i++){
27019             if(ns[i] == node){
27020                 return i;
27021             }
27022         }
27023         return -1;
27024     }
27025 });
27026 /*
27027  * Based on:
27028  * Ext JS Library 1.1.1
27029  * Copyright(c) 2006-2007, Ext JS, LLC.
27030  *
27031  * Originally Released Under LGPL - original licence link has changed is not relivant.
27032  *
27033  * Fork - LGPL
27034  * <script type="text/javascript">
27035  */
27036
27037 /**
27038  * @class Roo.JsonView
27039  * @extends Roo.View
27040  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27041 <pre><code>
27042 var view = new Roo.JsonView({
27043     container: "my-element",
27044     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27045     multiSelect: true, 
27046     jsonRoot: "data" 
27047 });
27048
27049 // listen for node click?
27050 view.on("click", function(vw, index, node, e){
27051     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27052 });
27053
27054 // direct load of JSON data
27055 view.load("foobar.php");
27056
27057 // Example from my blog list
27058 var tpl = new Roo.Template(
27059     '&lt;div class="entry"&gt;' +
27060     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27061     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27062     "&lt;/div&gt;&lt;hr /&gt;"
27063 );
27064
27065 var moreView = new Roo.JsonView({
27066     container :  "entry-list", 
27067     template : tpl,
27068     jsonRoot: "posts"
27069 });
27070 moreView.on("beforerender", this.sortEntries, this);
27071 moreView.load({
27072     url: "/blog/get-posts.php",
27073     params: "allposts=true",
27074     text: "Loading Blog Entries..."
27075 });
27076 </code></pre>
27077
27078 * Note: old code is supported with arguments : (container, template, config)
27079
27080
27081  * @constructor
27082  * Create a new JsonView
27083  * 
27084  * @param {Object} config The config object
27085  * 
27086  */
27087 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27088     
27089     
27090     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27091
27092     var um = this.el.getUpdateManager();
27093     um.setRenderer(this);
27094     um.on("update", this.onLoad, this);
27095     um.on("failure", this.onLoadException, this);
27096
27097     /**
27098      * @event beforerender
27099      * Fires before rendering of the downloaded JSON data.
27100      * @param {Roo.JsonView} this
27101      * @param {Object} data The JSON data loaded
27102      */
27103     /**
27104      * @event load
27105      * Fires when data is loaded.
27106      * @param {Roo.JsonView} this
27107      * @param {Object} data The JSON data loaded
27108      * @param {Object} response The raw Connect response object
27109      */
27110     /**
27111      * @event loadexception
27112      * Fires when loading fails.
27113      * @param {Roo.JsonView} this
27114      * @param {Object} response The raw Connect response object
27115      */
27116     this.addEvents({
27117         'beforerender' : true,
27118         'load' : true,
27119         'loadexception' : true
27120     });
27121 };
27122 Roo.extend(Roo.JsonView, Roo.View, {
27123     /**
27124      * @type {String} The root property in the loaded JSON object that contains the data
27125      */
27126     jsonRoot : "",
27127
27128     /**
27129      * Refreshes the view.
27130      */
27131     refresh : function(){
27132         this.clearSelections();
27133         this.el.update("");
27134         var html = [];
27135         var o = this.jsonData;
27136         if(o && o.length > 0){
27137             for(var i = 0, len = o.length; i < len; i++){
27138                 var data = this.prepareData(o[i], i, o);
27139                 html[html.length] = this.tpl.apply(data);
27140             }
27141         }else{
27142             html.push(this.emptyText);
27143         }
27144         this.el.update(html.join(""));
27145         this.nodes = this.el.dom.childNodes;
27146         this.updateIndexes(0);
27147     },
27148
27149     /**
27150      * 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.
27151      * @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:
27152      <pre><code>
27153      view.load({
27154          url: "your-url.php",
27155          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27156          callback: yourFunction,
27157          scope: yourObject, //(optional scope)
27158          discardUrl: false,
27159          nocache: false,
27160          text: "Loading...",
27161          timeout: 30,
27162          scripts: false
27163      });
27164      </code></pre>
27165      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27166      * 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.
27167      * @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}
27168      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27169      * @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.
27170      */
27171     load : function(){
27172         var um = this.el.getUpdateManager();
27173         um.update.apply(um, arguments);
27174     },
27175
27176     // note - render is a standard framework call...
27177     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27178     render : function(el, response){
27179         
27180         this.clearSelections();
27181         this.el.update("");
27182         var o;
27183         try{
27184             if (response != '') {
27185                 o = Roo.util.JSON.decode(response.responseText);
27186                 if(this.jsonRoot){
27187                     
27188                     o = o[this.jsonRoot];
27189                 }
27190             }
27191         } catch(e){
27192         }
27193         /**
27194          * The current JSON data or null
27195          */
27196         this.jsonData = o;
27197         this.beforeRender();
27198         this.refresh();
27199     },
27200
27201 /**
27202  * Get the number of records in the current JSON dataset
27203  * @return {Number}
27204  */
27205     getCount : function(){
27206         return this.jsonData ? this.jsonData.length : 0;
27207     },
27208
27209 /**
27210  * Returns the JSON object for the specified node(s)
27211  * @param {HTMLElement/Array} node The node or an array of nodes
27212  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27213  * you get the JSON object for the node
27214  */
27215     getNodeData : function(node){
27216         if(node instanceof Array){
27217             var data = [];
27218             for(var i = 0, len = node.length; i < len; i++){
27219                 data.push(this.getNodeData(node[i]));
27220             }
27221             return data;
27222         }
27223         return this.jsonData[this.indexOf(node)] || null;
27224     },
27225
27226     beforeRender : function(){
27227         this.snapshot = this.jsonData;
27228         if(this.sortInfo){
27229             this.sort.apply(this, this.sortInfo);
27230         }
27231         this.fireEvent("beforerender", this, this.jsonData);
27232     },
27233
27234     onLoad : function(el, o){
27235         this.fireEvent("load", this, this.jsonData, o);
27236     },
27237
27238     onLoadException : function(el, o){
27239         this.fireEvent("loadexception", this, o);
27240     },
27241
27242 /**
27243  * Filter the data by a specific property.
27244  * @param {String} property A property on your JSON objects
27245  * @param {String/RegExp} value Either string that the property values
27246  * should start with, or a RegExp to test against the property
27247  */
27248     filter : function(property, value){
27249         if(this.jsonData){
27250             var data = [];
27251             var ss = this.snapshot;
27252             if(typeof value == "string"){
27253                 var vlen = value.length;
27254                 if(vlen == 0){
27255                     this.clearFilter();
27256                     return;
27257                 }
27258                 value = value.toLowerCase();
27259                 for(var i = 0, len = ss.length; i < len; i++){
27260                     var o = ss[i];
27261                     if(o[property].substr(0, vlen).toLowerCase() == value){
27262                         data.push(o);
27263                     }
27264                 }
27265             } else if(value.exec){ // regex?
27266                 for(var i = 0, len = ss.length; i < len; i++){
27267                     var o = ss[i];
27268                     if(value.test(o[property])){
27269                         data.push(o);
27270                     }
27271                 }
27272             } else{
27273                 return;
27274             }
27275             this.jsonData = data;
27276             this.refresh();
27277         }
27278     },
27279
27280 /**
27281  * Filter by a function. The passed function will be called with each
27282  * object in the current dataset. If the function returns true the value is kept,
27283  * otherwise it is filtered.
27284  * @param {Function} fn
27285  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27286  */
27287     filterBy : function(fn, scope){
27288         if(this.jsonData){
27289             var data = [];
27290             var ss = this.snapshot;
27291             for(var i = 0, len = ss.length; i < len; i++){
27292                 var o = ss[i];
27293                 if(fn.call(scope || this, o)){
27294                     data.push(o);
27295                 }
27296             }
27297             this.jsonData = data;
27298             this.refresh();
27299         }
27300     },
27301
27302 /**
27303  * Clears the current filter.
27304  */
27305     clearFilter : function(){
27306         if(this.snapshot && this.jsonData != this.snapshot){
27307             this.jsonData = this.snapshot;
27308             this.refresh();
27309         }
27310     },
27311
27312
27313 /**
27314  * Sorts the data for this view and refreshes it.
27315  * @param {String} property A property on your JSON objects to sort on
27316  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27317  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27318  */
27319     sort : function(property, dir, sortType){
27320         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27321         if(this.jsonData){
27322             var p = property;
27323             var dsc = dir && dir.toLowerCase() == "desc";
27324             var f = function(o1, o2){
27325                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27326                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27327                 ;
27328                 if(v1 < v2){
27329                     return dsc ? +1 : -1;
27330                 } else if(v1 > v2){
27331                     return dsc ? -1 : +1;
27332                 } else{
27333                     return 0;
27334                 }
27335             };
27336             this.jsonData.sort(f);
27337             this.refresh();
27338             if(this.jsonData != this.snapshot){
27339                 this.snapshot.sort(f);
27340             }
27341         }
27342     }
27343 });/*
27344  * Based on:
27345  * Ext JS Library 1.1.1
27346  * Copyright(c) 2006-2007, Ext JS, LLC.
27347  *
27348  * Originally Released Under LGPL - original licence link has changed is not relivant.
27349  *
27350  * Fork - LGPL
27351  * <script type="text/javascript">
27352  */
27353  
27354
27355 /**
27356  * @class Roo.ColorPalette
27357  * @extends Roo.Component
27358  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27359  * Here's an example of typical usage:
27360  * <pre><code>
27361 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27362 cp.render('my-div');
27363
27364 cp.on('select', function(palette, selColor){
27365     // do something with selColor
27366 });
27367 </code></pre>
27368  * @constructor
27369  * Create a new ColorPalette
27370  * @param {Object} config The config object
27371  */
27372 Roo.ColorPalette = function(config){
27373     Roo.ColorPalette.superclass.constructor.call(this, config);
27374     this.addEvents({
27375         /**
27376              * @event select
27377              * Fires when a color is selected
27378              * @param {ColorPalette} this
27379              * @param {String} color The 6-digit color hex code (without the # symbol)
27380              */
27381         select: true
27382     });
27383
27384     if(this.handler){
27385         this.on("select", this.handler, this.scope, true);
27386     }
27387 };
27388 Roo.extend(Roo.ColorPalette, Roo.Component, {
27389     /**
27390      * @cfg {String} itemCls
27391      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27392      */
27393     itemCls : "x-color-palette",
27394     /**
27395      * @cfg {String} value
27396      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27397      * the hex codes are case-sensitive.
27398      */
27399     value : null,
27400     clickEvent:'click',
27401     // private
27402     ctype: "Roo.ColorPalette",
27403
27404     /**
27405      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27406      */
27407     allowReselect : false,
27408
27409     /**
27410      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27411      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27412      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27413      * of colors with the width setting until the box is symmetrical.</p>
27414      * <p>You can override individual colors if needed:</p>
27415      * <pre><code>
27416 var cp = new Roo.ColorPalette();
27417 cp.colors[0] = "FF0000";  // change the first box to red
27418 </code></pre>
27419
27420 Or you can provide a custom array of your own for complete control:
27421 <pre><code>
27422 var cp = new Roo.ColorPalette();
27423 cp.colors = ["000000", "993300", "333300"];
27424 </code></pre>
27425      * @type Array
27426      */
27427     colors : [
27428         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27429         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27430         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27431         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27432         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27433     ],
27434
27435     // private
27436     onRender : function(container, position){
27437         var t = new Roo.MasterTemplate(
27438             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27439         );
27440         var c = this.colors;
27441         for(var i = 0, len = c.length; i < len; i++){
27442             t.add([c[i]]);
27443         }
27444         var el = document.createElement("div");
27445         el.className = this.itemCls;
27446         t.overwrite(el);
27447         container.dom.insertBefore(el, position);
27448         this.el = Roo.get(el);
27449         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27450         if(this.clickEvent != 'click'){
27451             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27452         }
27453     },
27454
27455     // private
27456     afterRender : function(){
27457         Roo.ColorPalette.superclass.afterRender.call(this);
27458         if(this.value){
27459             var s = this.value;
27460             this.value = null;
27461             this.select(s);
27462         }
27463     },
27464
27465     // private
27466     handleClick : function(e, t){
27467         e.preventDefault();
27468         if(!this.disabled){
27469             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27470             this.select(c.toUpperCase());
27471         }
27472     },
27473
27474     /**
27475      * Selects the specified color in the palette (fires the select event)
27476      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27477      */
27478     select : function(color){
27479         color = color.replace("#", "");
27480         if(color != this.value || this.allowReselect){
27481             var el = this.el;
27482             if(this.value){
27483                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27484             }
27485             el.child("a.color-"+color).addClass("x-color-palette-sel");
27486             this.value = color;
27487             this.fireEvent("select", this, color);
27488         }
27489     }
27490 });/*
27491  * Based on:
27492  * Ext JS Library 1.1.1
27493  * Copyright(c) 2006-2007, Ext JS, LLC.
27494  *
27495  * Originally Released Under LGPL - original licence link has changed is not relivant.
27496  *
27497  * Fork - LGPL
27498  * <script type="text/javascript">
27499  */
27500  
27501 /**
27502  * @class Roo.DatePicker
27503  * @extends Roo.Component
27504  * Simple date picker class.
27505  * @constructor
27506  * Create a new DatePicker
27507  * @param {Object} config The config object
27508  */
27509 Roo.DatePicker = function(config){
27510     Roo.DatePicker.superclass.constructor.call(this, config);
27511
27512     this.value = config && config.value ?
27513                  config.value.clearTime() : new Date().clearTime();
27514
27515     this.addEvents({
27516         /**
27517              * @event select
27518              * Fires when a date is selected
27519              * @param {DatePicker} this
27520              * @param {Date} date The selected date
27521              */
27522         'select': true,
27523         /**
27524              * @event monthchange
27525              * Fires when the displayed month changes 
27526              * @param {DatePicker} this
27527              * @param {Date} date The selected month
27528              */
27529         'monthchange': true
27530     });
27531
27532     if(this.handler){
27533         this.on("select", this.handler,  this.scope || this);
27534     }
27535     // build the disabledDatesRE
27536     if(!this.disabledDatesRE && this.disabledDates){
27537         var dd = this.disabledDates;
27538         var re = "(?:";
27539         for(var i = 0; i < dd.length; i++){
27540             re += dd[i];
27541             if(i != dd.length-1) {
27542                 re += "|";
27543             }
27544         }
27545         this.disabledDatesRE = new RegExp(re + ")");
27546     }
27547 };
27548
27549 Roo.extend(Roo.DatePicker, Roo.Component, {
27550     /**
27551      * @cfg {String} todayText
27552      * The text to display on the button that selects the current date (defaults to "Today")
27553      */
27554     todayText : "Today",
27555     /**
27556      * @cfg {String} okText
27557      * The text to display on the ok button
27558      */
27559     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27560     /**
27561      * @cfg {String} cancelText
27562      * The text to display on the cancel button
27563      */
27564     cancelText : "Cancel",
27565     /**
27566      * @cfg {String} todayTip
27567      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27568      */
27569     todayTip : "{0} (Spacebar)",
27570     /**
27571      * @cfg {Date} minDate
27572      * Minimum allowable date (JavaScript date object, defaults to null)
27573      */
27574     minDate : null,
27575     /**
27576      * @cfg {Date} maxDate
27577      * Maximum allowable date (JavaScript date object, defaults to null)
27578      */
27579     maxDate : null,
27580     /**
27581      * @cfg {String} minText
27582      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27583      */
27584     minText : "This date is before the minimum date",
27585     /**
27586      * @cfg {String} maxText
27587      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27588      */
27589     maxText : "This date is after the maximum date",
27590     /**
27591      * @cfg {String} format
27592      * The default date format string which can be overriden for localization support.  The format must be
27593      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27594      */
27595     format : "m/d/y",
27596     /**
27597      * @cfg {Array} disabledDays
27598      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27599      */
27600     disabledDays : null,
27601     /**
27602      * @cfg {String} disabledDaysText
27603      * The tooltip to display when the date falls on a disabled day (defaults to "")
27604      */
27605     disabledDaysText : "",
27606     /**
27607      * @cfg {RegExp} disabledDatesRE
27608      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27609      */
27610     disabledDatesRE : null,
27611     /**
27612      * @cfg {String} disabledDatesText
27613      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27614      */
27615     disabledDatesText : "",
27616     /**
27617      * @cfg {Boolean} constrainToViewport
27618      * True to constrain the date picker to the viewport (defaults to true)
27619      */
27620     constrainToViewport : true,
27621     /**
27622      * @cfg {Array} monthNames
27623      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27624      */
27625     monthNames : Date.monthNames,
27626     /**
27627      * @cfg {Array} dayNames
27628      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27629      */
27630     dayNames : Date.dayNames,
27631     /**
27632      * @cfg {String} nextText
27633      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27634      */
27635     nextText: 'Next Month (Control+Right)',
27636     /**
27637      * @cfg {String} prevText
27638      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27639      */
27640     prevText: 'Previous Month (Control+Left)',
27641     /**
27642      * @cfg {String} monthYearText
27643      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27644      */
27645     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27646     /**
27647      * @cfg {Number} startDay
27648      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27649      */
27650     startDay : 0,
27651     /**
27652      * @cfg {Bool} showClear
27653      * Show a clear button (usefull for date form elements that can be blank.)
27654      */
27655     
27656     showClear: false,
27657     
27658     /**
27659      * Sets the value of the date field
27660      * @param {Date} value The date to set
27661      */
27662     setValue : function(value){
27663         var old = this.value;
27664         
27665         if (typeof(value) == 'string') {
27666          
27667             value = Date.parseDate(value, this.format);
27668         }
27669         if (!value) {
27670             value = new Date();
27671         }
27672         
27673         this.value = value.clearTime(true);
27674         if(this.el){
27675             this.update(this.value);
27676         }
27677     },
27678
27679     /**
27680      * Gets the current selected value of the date field
27681      * @return {Date} The selected date
27682      */
27683     getValue : function(){
27684         return this.value;
27685     },
27686
27687     // private
27688     focus : function(){
27689         if(this.el){
27690             this.update(this.activeDate);
27691         }
27692     },
27693
27694     // privateval
27695     onRender : function(container, position){
27696         
27697         var m = [
27698              '<table cellspacing="0">',
27699                 '<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>',
27700                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27701         var dn = this.dayNames;
27702         for(var i = 0; i < 7; i++){
27703             var d = this.startDay+i;
27704             if(d > 6){
27705                 d = d-7;
27706             }
27707             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27708         }
27709         m[m.length] = "</tr></thead><tbody><tr>";
27710         for(var i = 0; i < 42; i++) {
27711             if(i % 7 == 0 && i != 0){
27712                 m[m.length] = "</tr><tr>";
27713             }
27714             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27715         }
27716         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27717             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27718
27719         var el = document.createElement("div");
27720         el.className = "x-date-picker";
27721         el.innerHTML = m.join("");
27722
27723         container.dom.insertBefore(el, position);
27724
27725         this.el = Roo.get(el);
27726         this.eventEl = Roo.get(el.firstChild);
27727
27728         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27729             handler: this.showPrevMonth,
27730             scope: this,
27731             preventDefault:true,
27732             stopDefault:true
27733         });
27734
27735         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27736             handler: this.showNextMonth,
27737             scope: this,
27738             preventDefault:true,
27739             stopDefault:true
27740         });
27741
27742         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27743
27744         this.monthPicker = this.el.down('div.x-date-mp');
27745         this.monthPicker.enableDisplayMode('block');
27746         
27747         var kn = new Roo.KeyNav(this.eventEl, {
27748             "left" : function(e){
27749                 e.ctrlKey ?
27750                     this.showPrevMonth() :
27751                     this.update(this.activeDate.add("d", -1));
27752             },
27753
27754             "right" : function(e){
27755                 e.ctrlKey ?
27756                     this.showNextMonth() :
27757                     this.update(this.activeDate.add("d", 1));
27758             },
27759
27760             "up" : function(e){
27761                 e.ctrlKey ?
27762                     this.showNextYear() :
27763                     this.update(this.activeDate.add("d", -7));
27764             },
27765
27766             "down" : function(e){
27767                 e.ctrlKey ?
27768                     this.showPrevYear() :
27769                     this.update(this.activeDate.add("d", 7));
27770             },
27771
27772             "pageUp" : function(e){
27773                 this.showNextMonth();
27774             },
27775
27776             "pageDown" : function(e){
27777                 this.showPrevMonth();
27778             },
27779
27780             "enter" : function(e){
27781                 e.stopPropagation();
27782                 return true;
27783             },
27784
27785             scope : this
27786         });
27787
27788         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27789
27790         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27791
27792         this.el.unselectable();
27793         
27794         this.cells = this.el.select("table.x-date-inner tbody td");
27795         this.textNodes = this.el.query("table.x-date-inner tbody span");
27796
27797         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27798             text: "&#160;",
27799             tooltip: this.monthYearText
27800         });
27801
27802         this.mbtn.on('click', this.showMonthPicker, this);
27803         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27804
27805
27806         var today = (new Date()).dateFormat(this.format);
27807         
27808         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27809         if (this.showClear) {
27810             baseTb.add( new Roo.Toolbar.Fill());
27811         }
27812         baseTb.add({
27813             text: String.format(this.todayText, today),
27814             tooltip: String.format(this.todayTip, today),
27815             handler: this.selectToday,
27816             scope: this
27817         });
27818         
27819         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27820             
27821         //});
27822         if (this.showClear) {
27823             
27824             baseTb.add( new Roo.Toolbar.Fill());
27825             baseTb.add({
27826                 text: '&#160;',
27827                 cls: 'x-btn-icon x-btn-clear',
27828                 handler: function() {
27829                     //this.value = '';
27830                     this.fireEvent("select", this, '');
27831                 },
27832                 scope: this
27833             });
27834         }
27835         
27836         
27837         if(Roo.isIE){
27838             this.el.repaint();
27839         }
27840         this.update(this.value);
27841     },
27842
27843     createMonthPicker : function(){
27844         if(!this.monthPicker.dom.firstChild){
27845             var buf = ['<table border="0" cellspacing="0">'];
27846             for(var i = 0; i < 6; i++){
27847                 buf.push(
27848                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27849                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27850                     i == 0 ?
27851                     '<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>' :
27852                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27853                 );
27854             }
27855             buf.push(
27856                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27857                     this.okText,
27858                     '</button><button type="button" class="x-date-mp-cancel">',
27859                     this.cancelText,
27860                     '</button></td></tr>',
27861                 '</table>'
27862             );
27863             this.monthPicker.update(buf.join(''));
27864             this.monthPicker.on('click', this.onMonthClick, this);
27865             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27866
27867             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27868             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27869
27870             this.mpMonths.each(function(m, a, i){
27871                 i += 1;
27872                 if((i%2) == 0){
27873                     m.dom.xmonth = 5 + Math.round(i * .5);
27874                 }else{
27875                     m.dom.xmonth = Math.round((i-1) * .5);
27876                 }
27877             });
27878         }
27879     },
27880
27881     showMonthPicker : function(){
27882         this.createMonthPicker();
27883         var size = this.el.getSize();
27884         this.monthPicker.setSize(size);
27885         this.monthPicker.child('table').setSize(size);
27886
27887         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27888         this.updateMPMonth(this.mpSelMonth);
27889         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27890         this.updateMPYear(this.mpSelYear);
27891
27892         this.monthPicker.slideIn('t', {duration:.2});
27893     },
27894
27895     updateMPYear : function(y){
27896         this.mpyear = y;
27897         var ys = this.mpYears.elements;
27898         for(var i = 1; i <= 10; i++){
27899             var td = ys[i-1], y2;
27900             if((i%2) == 0){
27901                 y2 = y + Math.round(i * .5);
27902                 td.firstChild.innerHTML = y2;
27903                 td.xyear = y2;
27904             }else{
27905                 y2 = y - (5-Math.round(i * .5));
27906                 td.firstChild.innerHTML = y2;
27907                 td.xyear = y2;
27908             }
27909             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27910         }
27911     },
27912
27913     updateMPMonth : function(sm){
27914         this.mpMonths.each(function(m, a, i){
27915             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27916         });
27917     },
27918
27919     selectMPMonth: function(m){
27920         
27921     },
27922
27923     onMonthClick : function(e, t){
27924         e.stopEvent();
27925         var el = new Roo.Element(t), pn;
27926         if(el.is('button.x-date-mp-cancel')){
27927             this.hideMonthPicker();
27928         }
27929         else if(el.is('button.x-date-mp-ok')){
27930             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27931             this.hideMonthPicker();
27932         }
27933         else if(pn = el.up('td.x-date-mp-month', 2)){
27934             this.mpMonths.removeClass('x-date-mp-sel');
27935             pn.addClass('x-date-mp-sel');
27936             this.mpSelMonth = pn.dom.xmonth;
27937         }
27938         else if(pn = el.up('td.x-date-mp-year', 2)){
27939             this.mpYears.removeClass('x-date-mp-sel');
27940             pn.addClass('x-date-mp-sel');
27941             this.mpSelYear = pn.dom.xyear;
27942         }
27943         else if(el.is('a.x-date-mp-prev')){
27944             this.updateMPYear(this.mpyear-10);
27945         }
27946         else if(el.is('a.x-date-mp-next')){
27947             this.updateMPYear(this.mpyear+10);
27948         }
27949     },
27950
27951     onMonthDblClick : function(e, t){
27952         e.stopEvent();
27953         var el = new Roo.Element(t), pn;
27954         if(pn = el.up('td.x-date-mp-month', 2)){
27955             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27956             this.hideMonthPicker();
27957         }
27958         else if(pn = el.up('td.x-date-mp-year', 2)){
27959             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27960             this.hideMonthPicker();
27961         }
27962     },
27963
27964     hideMonthPicker : function(disableAnim){
27965         if(this.monthPicker){
27966             if(disableAnim === true){
27967                 this.monthPicker.hide();
27968             }else{
27969                 this.monthPicker.slideOut('t', {duration:.2});
27970             }
27971         }
27972     },
27973
27974     // private
27975     showPrevMonth : function(e){
27976         this.update(this.activeDate.add("mo", -1));
27977     },
27978
27979     // private
27980     showNextMonth : function(e){
27981         this.update(this.activeDate.add("mo", 1));
27982     },
27983
27984     // private
27985     showPrevYear : function(){
27986         this.update(this.activeDate.add("y", -1));
27987     },
27988
27989     // private
27990     showNextYear : function(){
27991         this.update(this.activeDate.add("y", 1));
27992     },
27993
27994     // private
27995     handleMouseWheel : function(e){
27996         var delta = e.getWheelDelta();
27997         if(delta > 0){
27998             this.showPrevMonth();
27999             e.stopEvent();
28000         } else if(delta < 0){
28001             this.showNextMonth();
28002             e.stopEvent();
28003         }
28004     },
28005
28006     // private
28007     handleDateClick : function(e, t){
28008         e.stopEvent();
28009         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28010             this.setValue(new Date(t.dateValue));
28011             this.fireEvent("select", this, this.value);
28012         }
28013     },
28014
28015     // private
28016     selectToday : function(){
28017         this.setValue(new Date().clearTime());
28018         this.fireEvent("select", this, this.value);
28019     },
28020
28021     // private
28022     update : function(date)
28023     {
28024         var vd = this.activeDate;
28025         this.activeDate = date;
28026         if(vd && this.el){
28027             var t = date.getTime();
28028             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28029                 this.cells.removeClass("x-date-selected");
28030                 this.cells.each(function(c){
28031                    if(c.dom.firstChild.dateValue == t){
28032                        c.addClass("x-date-selected");
28033                        setTimeout(function(){
28034                             try{c.dom.firstChild.focus();}catch(e){}
28035                        }, 50);
28036                        return false;
28037                    }
28038                 });
28039                 return;
28040             }
28041         }
28042         
28043         var days = date.getDaysInMonth();
28044         var firstOfMonth = date.getFirstDateOfMonth();
28045         var startingPos = firstOfMonth.getDay()-this.startDay;
28046
28047         if(startingPos <= this.startDay){
28048             startingPos += 7;
28049         }
28050
28051         var pm = date.add("mo", -1);
28052         var prevStart = pm.getDaysInMonth()-startingPos;
28053
28054         var cells = this.cells.elements;
28055         var textEls = this.textNodes;
28056         days += startingPos;
28057
28058         // convert everything to numbers so it's fast
28059         var day = 86400000;
28060         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28061         var today = new Date().clearTime().getTime();
28062         var sel = date.clearTime().getTime();
28063         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28064         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28065         var ddMatch = this.disabledDatesRE;
28066         var ddText = this.disabledDatesText;
28067         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28068         var ddaysText = this.disabledDaysText;
28069         var format = this.format;
28070
28071         var setCellClass = function(cal, cell){
28072             cell.title = "";
28073             var t = d.getTime();
28074             cell.firstChild.dateValue = t;
28075             if(t == today){
28076                 cell.className += " x-date-today";
28077                 cell.title = cal.todayText;
28078             }
28079             if(t == sel){
28080                 cell.className += " x-date-selected";
28081                 setTimeout(function(){
28082                     try{cell.firstChild.focus();}catch(e){}
28083                 }, 50);
28084             }
28085             // disabling
28086             if(t < min) {
28087                 cell.className = " x-date-disabled";
28088                 cell.title = cal.minText;
28089                 return;
28090             }
28091             if(t > max) {
28092                 cell.className = " x-date-disabled";
28093                 cell.title = cal.maxText;
28094                 return;
28095             }
28096             if(ddays){
28097                 if(ddays.indexOf(d.getDay()) != -1){
28098                     cell.title = ddaysText;
28099                     cell.className = " x-date-disabled";
28100                 }
28101             }
28102             if(ddMatch && format){
28103                 var fvalue = d.dateFormat(format);
28104                 if(ddMatch.test(fvalue)){
28105                     cell.title = ddText.replace("%0", fvalue);
28106                     cell.className = " x-date-disabled";
28107                 }
28108             }
28109         };
28110
28111         var i = 0;
28112         for(; i < startingPos; i++) {
28113             textEls[i].innerHTML = (++prevStart);
28114             d.setDate(d.getDate()+1);
28115             cells[i].className = "x-date-prevday";
28116             setCellClass(this, cells[i]);
28117         }
28118         for(; i < days; i++){
28119             intDay = i - startingPos + 1;
28120             textEls[i].innerHTML = (intDay);
28121             d.setDate(d.getDate()+1);
28122             cells[i].className = "x-date-active";
28123             setCellClass(this, cells[i]);
28124         }
28125         var extraDays = 0;
28126         for(; i < 42; i++) {
28127              textEls[i].innerHTML = (++extraDays);
28128              d.setDate(d.getDate()+1);
28129              cells[i].className = "x-date-nextday";
28130              setCellClass(this, cells[i]);
28131         }
28132
28133         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28134         this.fireEvent('monthchange', this, date);
28135         
28136         if(!this.internalRender){
28137             var main = this.el.dom.firstChild;
28138             var w = main.offsetWidth;
28139             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28140             Roo.fly(main).setWidth(w);
28141             this.internalRender = true;
28142             // opera does not respect the auto grow header center column
28143             // then, after it gets a width opera refuses to recalculate
28144             // without a second pass
28145             if(Roo.isOpera && !this.secondPass){
28146                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28147                 this.secondPass = true;
28148                 this.update.defer(10, this, [date]);
28149             }
28150         }
28151         
28152         
28153     }
28154 });        /*
28155  * Based on:
28156  * Ext JS Library 1.1.1
28157  * Copyright(c) 2006-2007, Ext JS, LLC.
28158  *
28159  * Originally Released Under LGPL - original licence link has changed is not relivant.
28160  *
28161  * Fork - LGPL
28162  * <script type="text/javascript">
28163  */
28164 /**
28165  * @class Roo.TabPanel
28166  * @extends Roo.util.Observable
28167  * A lightweight tab container.
28168  * <br><br>
28169  * Usage:
28170  * <pre><code>
28171 // basic tabs 1, built from existing content
28172 var tabs = new Roo.TabPanel("tabs1");
28173 tabs.addTab("script", "View Script");
28174 tabs.addTab("markup", "View Markup");
28175 tabs.activate("script");
28176
28177 // more advanced tabs, built from javascript
28178 var jtabs = new Roo.TabPanel("jtabs");
28179 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28180
28181 // set up the UpdateManager
28182 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28183 var updater = tab2.getUpdateManager();
28184 updater.setDefaultUrl("ajax1.htm");
28185 tab2.on('activate', updater.refresh, updater, true);
28186
28187 // Use setUrl for Ajax loading
28188 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28189 tab3.setUrl("ajax2.htm", null, true);
28190
28191 // Disabled tab
28192 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28193 tab4.disable();
28194
28195 jtabs.activate("jtabs-1");
28196  * </code></pre>
28197  * @constructor
28198  * Create a new TabPanel.
28199  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28200  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28201  */
28202 Roo.TabPanel = function(container, config){
28203     /**
28204     * The container element for this TabPanel.
28205     * @type Roo.Element
28206     */
28207     this.el = Roo.get(container, true);
28208     if(config){
28209         if(typeof config == "boolean"){
28210             this.tabPosition = config ? "bottom" : "top";
28211         }else{
28212             Roo.apply(this, config);
28213         }
28214     }
28215     if(this.tabPosition == "bottom"){
28216         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28217         this.el.addClass("x-tabs-bottom");
28218     }
28219     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28220     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28221     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28222     if(Roo.isIE){
28223         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28224     }
28225     if(this.tabPosition != "bottom"){
28226         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28227          * @type Roo.Element
28228          */
28229         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28230         this.el.addClass("x-tabs-top");
28231     }
28232     this.items = [];
28233
28234     this.bodyEl.setStyle("position", "relative");
28235
28236     this.active = null;
28237     this.activateDelegate = this.activate.createDelegate(this);
28238
28239     this.addEvents({
28240         /**
28241          * @event tabchange
28242          * Fires when the active tab changes
28243          * @param {Roo.TabPanel} this
28244          * @param {Roo.TabPanelItem} activePanel The new active tab
28245          */
28246         "tabchange": true,
28247         /**
28248          * @event beforetabchange
28249          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28250          * @param {Roo.TabPanel} this
28251          * @param {Object} e Set cancel to true on this object to cancel the tab change
28252          * @param {Roo.TabPanelItem} tab The tab being changed to
28253          */
28254         "beforetabchange" : true
28255     });
28256
28257     Roo.EventManager.onWindowResize(this.onResize, this);
28258     this.cpad = this.el.getPadding("lr");
28259     this.hiddenCount = 0;
28260
28261
28262     // toolbar on the tabbar support...
28263     if (this.toolbar) {
28264         var tcfg = this.toolbar;
28265         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28266         this.toolbar = new Roo.Toolbar(tcfg);
28267         if (Roo.isSafari) {
28268             var tbl = tcfg.container.child('table', true);
28269             tbl.setAttribute('width', '100%');
28270         }
28271         
28272     }
28273    
28274
28275
28276     Roo.TabPanel.superclass.constructor.call(this);
28277 };
28278
28279 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28280     /*
28281      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28282      */
28283     tabPosition : "top",
28284     /*
28285      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28286      */
28287     currentTabWidth : 0,
28288     /*
28289      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28290      */
28291     minTabWidth : 40,
28292     /*
28293      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28294      */
28295     maxTabWidth : 250,
28296     /*
28297      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28298      */
28299     preferredTabWidth : 175,
28300     /*
28301      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28302      */
28303     resizeTabs : false,
28304     /*
28305      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28306      */
28307     monitorResize : true,
28308     /*
28309      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28310      */
28311     toolbar : false,
28312
28313     /**
28314      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28315      * @param {String} id The id of the div to use <b>or create</b>
28316      * @param {String} text The text for the tab
28317      * @param {String} content (optional) Content to put in the TabPanelItem body
28318      * @param {Boolean} closable (optional) True to create a close icon on the tab
28319      * @return {Roo.TabPanelItem} The created TabPanelItem
28320      */
28321     addTab : function(id, text, content, closable){
28322         var item = new Roo.TabPanelItem(this, id, text, closable);
28323         this.addTabItem(item);
28324         if(content){
28325             item.setContent(content);
28326         }
28327         return item;
28328     },
28329
28330     /**
28331      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28332      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28333      * @return {Roo.TabPanelItem}
28334      */
28335     getTab : function(id){
28336         return this.items[id];
28337     },
28338
28339     /**
28340      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28341      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28342      */
28343     hideTab : function(id){
28344         var t = this.items[id];
28345         if(!t.isHidden()){
28346            t.setHidden(true);
28347            this.hiddenCount++;
28348            this.autoSizeTabs();
28349         }
28350     },
28351
28352     /**
28353      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28354      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28355      */
28356     unhideTab : function(id){
28357         var t = this.items[id];
28358         if(t.isHidden()){
28359            t.setHidden(false);
28360            this.hiddenCount--;
28361            this.autoSizeTabs();
28362         }
28363     },
28364
28365     /**
28366      * Adds an existing {@link Roo.TabPanelItem}.
28367      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28368      */
28369     addTabItem : function(item){
28370         this.items[item.id] = item;
28371         this.items.push(item);
28372         if(this.resizeTabs){
28373            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28374            this.autoSizeTabs();
28375         }else{
28376             item.autoSize();
28377         }
28378     },
28379
28380     /**
28381      * Removes a {@link Roo.TabPanelItem}.
28382      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28383      */
28384     removeTab : function(id){
28385         var items = this.items;
28386         var tab = items[id];
28387         if(!tab) { return; }
28388         var index = items.indexOf(tab);
28389         if(this.active == tab && items.length > 1){
28390             var newTab = this.getNextAvailable(index);
28391             if(newTab) {
28392                 newTab.activate();
28393             }
28394         }
28395         this.stripEl.dom.removeChild(tab.pnode.dom);
28396         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28397             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28398         }
28399         items.splice(index, 1);
28400         delete this.items[tab.id];
28401         tab.fireEvent("close", tab);
28402         tab.purgeListeners();
28403         this.autoSizeTabs();
28404     },
28405
28406     getNextAvailable : function(start){
28407         var items = this.items;
28408         var index = start;
28409         // look for a next tab that will slide over to
28410         // replace the one being removed
28411         while(index < items.length){
28412             var item = items[++index];
28413             if(item && !item.isHidden()){
28414                 return item;
28415             }
28416         }
28417         // if one isn't found select the previous tab (on the left)
28418         index = start;
28419         while(index >= 0){
28420             var item = items[--index];
28421             if(item && !item.isHidden()){
28422                 return item;
28423             }
28424         }
28425         return null;
28426     },
28427
28428     /**
28429      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28430      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28431      */
28432     disableTab : function(id){
28433         var tab = this.items[id];
28434         if(tab && this.active != tab){
28435             tab.disable();
28436         }
28437     },
28438
28439     /**
28440      * Enables a {@link Roo.TabPanelItem} that is disabled.
28441      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28442      */
28443     enableTab : function(id){
28444         var tab = this.items[id];
28445         tab.enable();
28446     },
28447
28448     /**
28449      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28450      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28451      * @return {Roo.TabPanelItem} The TabPanelItem.
28452      */
28453     activate : function(id){
28454         var tab = this.items[id];
28455         if(!tab){
28456             return null;
28457         }
28458         if(tab == this.active || tab.disabled){
28459             return tab;
28460         }
28461         var e = {};
28462         this.fireEvent("beforetabchange", this, e, tab);
28463         if(e.cancel !== true && !tab.disabled){
28464             if(this.active){
28465                 this.active.hide();
28466             }
28467             this.active = this.items[id];
28468             this.active.show();
28469             this.fireEvent("tabchange", this, this.active);
28470         }
28471         return tab;
28472     },
28473
28474     /**
28475      * Gets the active {@link Roo.TabPanelItem}.
28476      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28477      */
28478     getActiveTab : function(){
28479         return this.active;
28480     },
28481
28482     /**
28483      * Updates the tab body element to fit the height of the container element
28484      * for overflow scrolling
28485      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28486      */
28487     syncHeight : function(targetHeight){
28488         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28489         var bm = this.bodyEl.getMargins();
28490         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28491         this.bodyEl.setHeight(newHeight);
28492         return newHeight;
28493     },
28494
28495     onResize : function(){
28496         if(this.monitorResize){
28497             this.autoSizeTabs();
28498         }
28499     },
28500
28501     /**
28502      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28503      */
28504     beginUpdate : function(){
28505         this.updating = true;
28506     },
28507
28508     /**
28509      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28510      */
28511     endUpdate : function(){
28512         this.updating = false;
28513         this.autoSizeTabs();
28514     },
28515
28516     /**
28517      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28518      */
28519     autoSizeTabs : function(){
28520         var count = this.items.length;
28521         var vcount = count - this.hiddenCount;
28522         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28523             return;
28524         }
28525         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28526         var availWidth = Math.floor(w / vcount);
28527         var b = this.stripBody;
28528         if(b.getWidth() > w){
28529             var tabs = this.items;
28530             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28531             if(availWidth < this.minTabWidth){
28532                 /*if(!this.sleft){    // incomplete scrolling code
28533                     this.createScrollButtons();
28534                 }
28535                 this.showScroll();
28536                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28537             }
28538         }else{
28539             if(this.currentTabWidth < this.preferredTabWidth){
28540                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28541             }
28542         }
28543     },
28544
28545     /**
28546      * Returns the number of tabs in this TabPanel.
28547      * @return {Number}
28548      */
28549      getCount : function(){
28550          return this.items.length;
28551      },
28552
28553     /**
28554      * Resizes all the tabs to the passed width
28555      * @param {Number} The new width
28556      */
28557     setTabWidth : function(width){
28558         this.currentTabWidth = width;
28559         for(var i = 0, len = this.items.length; i < len; i++) {
28560                 if(!this.items[i].isHidden()) {
28561                 this.items[i].setWidth(width);
28562             }
28563         }
28564     },
28565
28566     /**
28567      * Destroys this TabPanel
28568      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28569      */
28570     destroy : function(removeEl){
28571         Roo.EventManager.removeResizeListener(this.onResize, this);
28572         for(var i = 0, len = this.items.length; i < len; i++){
28573             this.items[i].purgeListeners();
28574         }
28575         if(removeEl === true){
28576             this.el.update("");
28577             this.el.remove();
28578         }
28579     }
28580 });
28581
28582 /**
28583  * @class Roo.TabPanelItem
28584  * @extends Roo.util.Observable
28585  * Represents an individual item (tab plus body) in a TabPanel.
28586  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28587  * @param {String} id The id of this TabPanelItem
28588  * @param {String} text The text for the tab of this TabPanelItem
28589  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28590  */
28591 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28592     /**
28593      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28594      * @type Roo.TabPanel
28595      */
28596     this.tabPanel = tabPanel;
28597     /**
28598      * The id for this TabPanelItem
28599      * @type String
28600      */
28601     this.id = id;
28602     /** @private */
28603     this.disabled = false;
28604     /** @private */
28605     this.text = text;
28606     /** @private */
28607     this.loaded = false;
28608     this.closable = closable;
28609
28610     /**
28611      * The body element for this TabPanelItem.
28612      * @type Roo.Element
28613      */
28614     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28615     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28616     this.bodyEl.setStyle("display", "block");
28617     this.bodyEl.setStyle("zoom", "1");
28618     this.hideAction();
28619
28620     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28621     /** @private */
28622     this.el = Roo.get(els.el, true);
28623     this.inner = Roo.get(els.inner, true);
28624     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28625     this.pnode = Roo.get(els.el.parentNode, true);
28626     this.el.on("mousedown", this.onTabMouseDown, this);
28627     this.el.on("click", this.onTabClick, this);
28628     /** @private */
28629     if(closable){
28630         var c = Roo.get(els.close, true);
28631         c.dom.title = this.closeText;
28632         c.addClassOnOver("close-over");
28633         c.on("click", this.closeClick, this);
28634      }
28635
28636     this.addEvents({
28637          /**
28638          * @event activate
28639          * Fires when this tab becomes the active tab.
28640          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28641          * @param {Roo.TabPanelItem} this
28642          */
28643         "activate": true,
28644         /**
28645          * @event beforeclose
28646          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28647          * @param {Roo.TabPanelItem} this
28648          * @param {Object} e Set cancel to true on this object to cancel the close.
28649          */
28650         "beforeclose": true,
28651         /**
28652          * @event close
28653          * Fires when this tab is closed.
28654          * @param {Roo.TabPanelItem} this
28655          */
28656          "close": true,
28657         /**
28658          * @event deactivate
28659          * Fires when this tab is no longer the active tab.
28660          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28661          * @param {Roo.TabPanelItem} this
28662          */
28663          "deactivate" : true
28664     });
28665     this.hidden = false;
28666
28667     Roo.TabPanelItem.superclass.constructor.call(this);
28668 };
28669
28670 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28671     purgeListeners : function(){
28672        Roo.util.Observable.prototype.purgeListeners.call(this);
28673        this.el.removeAllListeners();
28674     },
28675     /**
28676      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28677      */
28678     show : function(){
28679         this.pnode.addClass("on");
28680         this.showAction();
28681         if(Roo.isOpera){
28682             this.tabPanel.stripWrap.repaint();
28683         }
28684         this.fireEvent("activate", this.tabPanel, this);
28685     },
28686
28687     /**
28688      * Returns true if this tab is the active tab.
28689      * @return {Boolean}
28690      */
28691     isActive : function(){
28692         return this.tabPanel.getActiveTab() == this;
28693     },
28694
28695     /**
28696      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28697      */
28698     hide : function(){
28699         this.pnode.removeClass("on");
28700         this.hideAction();
28701         this.fireEvent("deactivate", this.tabPanel, this);
28702     },
28703
28704     hideAction : function(){
28705         this.bodyEl.hide();
28706         this.bodyEl.setStyle("position", "absolute");
28707         this.bodyEl.setLeft("-20000px");
28708         this.bodyEl.setTop("-20000px");
28709     },
28710
28711     showAction : function(){
28712         this.bodyEl.setStyle("position", "relative");
28713         this.bodyEl.setTop("");
28714         this.bodyEl.setLeft("");
28715         this.bodyEl.show();
28716     },
28717
28718     /**
28719      * Set the tooltip for the tab.
28720      * @param {String} tooltip The tab's tooltip
28721      */
28722     setTooltip : function(text){
28723         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28724             this.textEl.dom.qtip = text;
28725             this.textEl.dom.removeAttribute('title');
28726         }else{
28727             this.textEl.dom.title = text;
28728         }
28729     },
28730
28731     onTabClick : function(e){
28732         e.preventDefault();
28733         this.tabPanel.activate(this.id);
28734     },
28735
28736     onTabMouseDown : function(e){
28737         e.preventDefault();
28738         this.tabPanel.activate(this.id);
28739     },
28740
28741     getWidth : function(){
28742         return this.inner.getWidth();
28743     },
28744
28745     setWidth : function(width){
28746         var iwidth = width - this.pnode.getPadding("lr");
28747         this.inner.setWidth(iwidth);
28748         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28749         this.pnode.setWidth(width);
28750     },
28751
28752     /**
28753      * Show or hide the tab
28754      * @param {Boolean} hidden True to hide or false to show.
28755      */
28756     setHidden : function(hidden){
28757         this.hidden = hidden;
28758         this.pnode.setStyle("display", hidden ? "none" : "");
28759     },
28760
28761     /**
28762      * Returns true if this tab is "hidden"
28763      * @return {Boolean}
28764      */
28765     isHidden : function(){
28766         return this.hidden;
28767     },
28768
28769     /**
28770      * Returns the text for this tab
28771      * @return {String}
28772      */
28773     getText : function(){
28774         return this.text;
28775     },
28776
28777     autoSize : function(){
28778         //this.el.beginMeasure();
28779         this.textEl.setWidth(1);
28780         /*
28781          *  #2804 [new] Tabs in Roojs
28782          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28783          */
28784         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28785         //this.el.endMeasure();
28786     },
28787
28788     /**
28789      * Sets the text for the tab (Note: this also sets the tooltip text)
28790      * @param {String} text The tab's text and tooltip
28791      */
28792     setText : function(text){
28793         this.text = text;
28794         this.textEl.update(text);
28795         this.setTooltip(text);
28796         if(!this.tabPanel.resizeTabs){
28797             this.autoSize();
28798         }
28799     },
28800     /**
28801      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28802      */
28803     activate : function(){
28804         this.tabPanel.activate(this.id);
28805     },
28806
28807     /**
28808      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28809      */
28810     disable : function(){
28811         if(this.tabPanel.active != this){
28812             this.disabled = true;
28813             this.pnode.addClass("disabled");
28814         }
28815     },
28816
28817     /**
28818      * Enables this TabPanelItem if it was previously disabled.
28819      */
28820     enable : function(){
28821         this.disabled = false;
28822         this.pnode.removeClass("disabled");
28823     },
28824
28825     /**
28826      * Sets the content for this TabPanelItem.
28827      * @param {String} content The content
28828      * @param {Boolean} loadScripts true to look for and load scripts
28829      */
28830     setContent : function(content, loadScripts){
28831         this.bodyEl.update(content, loadScripts);
28832     },
28833
28834     /**
28835      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28836      * @return {Roo.UpdateManager} The UpdateManager
28837      */
28838     getUpdateManager : function(){
28839         return this.bodyEl.getUpdateManager();
28840     },
28841
28842     /**
28843      * Set a URL to be used to load the content for this TabPanelItem.
28844      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28845      * @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)
28846      * @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)
28847      * @return {Roo.UpdateManager} The UpdateManager
28848      */
28849     setUrl : function(url, params, loadOnce){
28850         if(this.refreshDelegate){
28851             this.un('activate', this.refreshDelegate);
28852         }
28853         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28854         this.on("activate", this.refreshDelegate);
28855         return this.bodyEl.getUpdateManager();
28856     },
28857
28858     /** @private */
28859     _handleRefresh : function(url, params, loadOnce){
28860         if(!loadOnce || !this.loaded){
28861             var updater = this.bodyEl.getUpdateManager();
28862             updater.update(url, params, this._setLoaded.createDelegate(this));
28863         }
28864     },
28865
28866     /**
28867      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28868      *   Will fail silently if the setUrl method has not been called.
28869      *   This does not activate the panel, just updates its content.
28870      */
28871     refresh : function(){
28872         if(this.refreshDelegate){
28873            this.loaded = false;
28874            this.refreshDelegate();
28875         }
28876     },
28877
28878     /** @private */
28879     _setLoaded : function(){
28880         this.loaded = true;
28881     },
28882
28883     /** @private */
28884     closeClick : function(e){
28885         var o = {};
28886         e.stopEvent();
28887         this.fireEvent("beforeclose", this, o);
28888         if(o.cancel !== true){
28889             this.tabPanel.removeTab(this.id);
28890         }
28891     },
28892     /**
28893      * The text displayed in the tooltip for the close icon.
28894      * @type String
28895      */
28896     closeText : "Close this tab"
28897 });
28898
28899 /** @private */
28900 Roo.TabPanel.prototype.createStrip = function(container){
28901     var strip = document.createElement("div");
28902     strip.className = "x-tabs-wrap";
28903     container.appendChild(strip);
28904     return strip;
28905 };
28906 /** @private */
28907 Roo.TabPanel.prototype.createStripList = function(strip){
28908     // div wrapper for retard IE
28909     // returns the "tr" element.
28910     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28911         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28912         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28913     return strip.firstChild.firstChild.firstChild.firstChild;
28914 };
28915 /** @private */
28916 Roo.TabPanel.prototype.createBody = function(container){
28917     var body = document.createElement("div");
28918     Roo.id(body, "tab-body");
28919     Roo.fly(body).addClass("x-tabs-body");
28920     container.appendChild(body);
28921     return body;
28922 };
28923 /** @private */
28924 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28925     var body = Roo.getDom(id);
28926     if(!body){
28927         body = document.createElement("div");
28928         body.id = id;
28929     }
28930     Roo.fly(body).addClass("x-tabs-item-body");
28931     bodyEl.insertBefore(body, bodyEl.firstChild);
28932     return body;
28933 };
28934 /** @private */
28935 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28936     var td = document.createElement("td");
28937     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28938     //stripEl.appendChild(td);
28939     if(closable){
28940         td.className = "x-tabs-closable";
28941         if(!this.closeTpl){
28942             this.closeTpl = new Roo.Template(
28943                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28944                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28945                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28946             );
28947         }
28948         var el = this.closeTpl.overwrite(td, {"text": text});
28949         var close = el.getElementsByTagName("div")[0];
28950         var inner = el.getElementsByTagName("em")[0];
28951         return {"el": el, "close": close, "inner": inner};
28952     } else {
28953         if(!this.tabTpl){
28954             this.tabTpl = new Roo.Template(
28955                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28956                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28957             );
28958         }
28959         var el = this.tabTpl.overwrite(td, {"text": text});
28960         var inner = el.getElementsByTagName("em")[0];
28961         return {"el": el, "inner": inner};
28962     }
28963 };/*
28964  * Based on:
28965  * Ext JS Library 1.1.1
28966  * Copyright(c) 2006-2007, Ext JS, LLC.
28967  *
28968  * Originally Released Under LGPL - original licence link has changed is not relivant.
28969  *
28970  * Fork - LGPL
28971  * <script type="text/javascript">
28972  */
28973
28974 /**
28975  * @class Roo.Button
28976  * @extends Roo.util.Observable
28977  * Simple Button class
28978  * @cfg {String} text The button text
28979  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28980  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28981  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28982  * @cfg {Object} scope The scope of the handler
28983  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28984  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28985  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28986  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28987  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28988  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28989    applies if enableToggle = true)
28990  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28991  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28992   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28993  * @constructor
28994  * Create a new button
28995  * @param {Object} config The config object
28996  */
28997 Roo.Button = function(renderTo, config)
28998 {
28999     if (!config) {
29000         config = renderTo;
29001         renderTo = config.renderTo || false;
29002     }
29003     
29004     Roo.apply(this, config);
29005     this.addEvents({
29006         /**
29007              * @event click
29008              * Fires when this button is clicked
29009              * @param {Button} this
29010              * @param {EventObject} e The click event
29011              */
29012             "click" : true,
29013         /**
29014              * @event toggle
29015              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29016              * @param {Button} this
29017              * @param {Boolean} pressed
29018              */
29019             "toggle" : true,
29020         /**
29021              * @event mouseover
29022              * Fires when the mouse hovers over the button
29023              * @param {Button} this
29024              * @param {Event} e The event object
29025              */
29026         'mouseover' : true,
29027         /**
29028              * @event mouseout
29029              * Fires when the mouse exits the button
29030              * @param {Button} this
29031              * @param {Event} e The event object
29032              */
29033         'mouseout': true,
29034          /**
29035              * @event render
29036              * Fires when the button is rendered
29037              * @param {Button} this
29038              */
29039         'render': true
29040     });
29041     if(this.menu){
29042         this.menu = Roo.menu.MenuMgr.get(this.menu);
29043     }
29044     // register listeners first!!  - so render can be captured..
29045     Roo.util.Observable.call(this);
29046     if(renderTo){
29047         this.render(renderTo);
29048     }
29049     
29050   
29051 };
29052
29053 Roo.extend(Roo.Button, Roo.util.Observable, {
29054     /**
29055      * 
29056      */
29057     
29058     /**
29059      * Read-only. True if this button is hidden
29060      * @type Boolean
29061      */
29062     hidden : false,
29063     /**
29064      * Read-only. True if this button is disabled
29065      * @type Boolean
29066      */
29067     disabled : false,
29068     /**
29069      * Read-only. True if this button is pressed (only if enableToggle = true)
29070      * @type Boolean
29071      */
29072     pressed : false,
29073
29074     /**
29075      * @cfg {Number} tabIndex 
29076      * The DOM tabIndex for this button (defaults to undefined)
29077      */
29078     tabIndex : undefined,
29079
29080     /**
29081      * @cfg {Boolean} enableToggle
29082      * True to enable pressed/not pressed toggling (defaults to false)
29083      */
29084     enableToggle: false,
29085     /**
29086      * @cfg {Mixed} menu
29087      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29088      */
29089     menu : undefined,
29090     /**
29091      * @cfg {String} menuAlign
29092      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29093      */
29094     menuAlign : "tl-bl?",
29095
29096     /**
29097      * @cfg {String} iconCls
29098      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29099      */
29100     iconCls : undefined,
29101     /**
29102      * @cfg {String} type
29103      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29104      */
29105     type : 'button',
29106
29107     // private
29108     menuClassTarget: 'tr',
29109
29110     /**
29111      * @cfg {String} clickEvent
29112      * The type of event to map to the button's event handler (defaults to 'click')
29113      */
29114     clickEvent : 'click',
29115
29116     /**
29117      * @cfg {Boolean} handleMouseEvents
29118      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29119      */
29120     handleMouseEvents : true,
29121
29122     /**
29123      * @cfg {String} tooltipType
29124      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29125      */
29126     tooltipType : 'qtip',
29127
29128     /**
29129      * @cfg {String} cls
29130      * A CSS class to apply to the button's main element.
29131      */
29132     
29133     /**
29134      * @cfg {Roo.Template} template (Optional)
29135      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29136      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29137      * require code modifications if required elements (e.g. a button) aren't present.
29138      */
29139
29140     // private
29141     render : function(renderTo){
29142         var btn;
29143         if(this.hideParent){
29144             this.parentEl = Roo.get(renderTo);
29145         }
29146         if(!this.dhconfig){
29147             if(!this.template){
29148                 if(!Roo.Button.buttonTemplate){
29149                     // hideous table template
29150                     Roo.Button.buttonTemplate = new Roo.Template(
29151                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29152                         '<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>',
29153                         "</tr></tbody></table>");
29154                 }
29155                 this.template = Roo.Button.buttonTemplate;
29156             }
29157             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29158             var btnEl = btn.child("button:first");
29159             btnEl.on('focus', this.onFocus, this);
29160             btnEl.on('blur', this.onBlur, this);
29161             if(this.cls){
29162                 btn.addClass(this.cls);
29163             }
29164             if(this.icon){
29165                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29166             }
29167             if(this.iconCls){
29168                 btnEl.addClass(this.iconCls);
29169                 if(!this.cls){
29170                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29171                 }
29172             }
29173             if(this.tabIndex !== undefined){
29174                 btnEl.dom.tabIndex = this.tabIndex;
29175             }
29176             if(this.tooltip){
29177                 if(typeof this.tooltip == 'object'){
29178                     Roo.QuickTips.tips(Roo.apply({
29179                           target: btnEl.id
29180                     }, this.tooltip));
29181                 } else {
29182                     btnEl.dom[this.tooltipType] = this.tooltip;
29183                 }
29184             }
29185         }else{
29186             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29187         }
29188         this.el = btn;
29189         if(this.id){
29190             this.el.dom.id = this.el.id = this.id;
29191         }
29192         if(this.menu){
29193             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29194             this.menu.on("show", this.onMenuShow, this);
29195             this.menu.on("hide", this.onMenuHide, this);
29196         }
29197         btn.addClass("x-btn");
29198         if(Roo.isIE && !Roo.isIE7){
29199             this.autoWidth.defer(1, this);
29200         }else{
29201             this.autoWidth();
29202         }
29203         if(this.handleMouseEvents){
29204             btn.on("mouseover", this.onMouseOver, this);
29205             btn.on("mouseout", this.onMouseOut, this);
29206             btn.on("mousedown", this.onMouseDown, this);
29207         }
29208         btn.on(this.clickEvent, this.onClick, this);
29209         //btn.on("mouseup", this.onMouseUp, this);
29210         if(this.hidden){
29211             this.hide();
29212         }
29213         if(this.disabled){
29214             this.disable();
29215         }
29216         Roo.ButtonToggleMgr.register(this);
29217         if(this.pressed){
29218             this.el.addClass("x-btn-pressed");
29219         }
29220         if(this.repeat){
29221             var repeater = new Roo.util.ClickRepeater(btn,
29222                 typeof this.repeat == "object" ? this.repeat : {}
29223             );
29224             repeater.on("click", this.onClick,  this);
29225         }
29226         
29227         this.fireEvent('render', this);
29228         
29229     },
29230     /**
29231      * Returns the button's underlying element
29232      * @return {Roo.Element} The element
29233      */
29234     getEl : function(){
29235         return this.el;  
29236     },
29237     
29238     /**
29239      * Destroys this Button and removes any listeners.
29240      */
29241     destroy : function(){
29242         Roo.ButtonToggleMgr.unregister(this);
29243         this.el.removeAllListeners();
29244         this.purgeListeners();
29245         this.el.remove();
29246     },
29247
29248     // private
29249     autoWidth : function(){
29250         if(this.el){
29251             this.el.setWidth("auto");
29252             if(Roo.isIE7 && Roo.isStrict){
29253                 var ib = this.el.child('button');
29254                 if(ib && ib.getWidth() > 20){
29255                     ib.clip();
29256                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29257                 }
29258             }
29259             if(this.minWidth){
29260                 if(this.hidden){
29261                     this.el.beginMeasure();
29262                 }
29263                 if(this.el.getWidth() < this.minWidth){
29264                     this.el.setWidth(this.minWidth);
29265                 }
29266                 if(this.hidden){
29267                     this.el.endMeasure();
29268                 }
29269             }
29270         }
29271     },
29272
29273     /**
29274      * Assigns this button's click handler
29275      * @param {Function} handler The function to call when the button is clicked
29276      * @param {Object} scope (optional) Scope for the function passed in
29277      */
29278     setHandler : function(handler, scope){
29279         this.handler = handler;
29280         this.scope = scope;  
29281     },
29282     
29283     /**
29284      * Sets this button's text
29285      * @param {String} text The button text
29286      */
29287     setText : function(text){
29288         this.text = text;
29289         if(this.el){
29290             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29291         }
29292         this.autoWidth();
29293     },
29294     
29295     /**
29296      * Gets the text for this button
29297      * @return {String} The button text
29298      */
29299     getText : function(){
29300         return this.text;  
29301     },
29302     
29303     /**
29304      * Show this button
29305      */
29306     show: function(){
29307         this.hidden = false;
29308         if(this.el){
29309             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29310         }
29311     },
29312     
29313     /**
29314      * Hide this button
29315      */
29316     hide: function(){
29317         this.hidden = true;
29318         if(this.el){
29319             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29320         }
29321     },
29322     
29323     /**
29324      * Convenience function for boolean show/hide
29325      * @param {Boolean} visible True to show, false to hide
29326      */
29327     setVisible: function(visible){
29328         if(visible) {
29329             this.show();
29330         }else{
29331             this.hide();
29332         }
29333     },
29334     
29335     /**
29336      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29337      * @param {Boolean} state (optional) Force a particular state
29338      */
29339     toggle : function(state){
29340         state = state === undefined ? !this.pressed : state;
29341         if(state != this.pressed){
29342             if(state){
29343                 this.el.addClass("x-btn-pressed");
29344                 this.pressed = true;
29345                 this.fireEvent("toggle", this, true);
29346             }else{
29347                 this.el.removeClass("x-btn-pressed");
29348                 this.pressed = false;
29349                 this.fireEvent("toggle", this, false);
29350             }
29351             if(this.toggleHandler){
29352                 this.toggleHandler.call(this.scope || this, this, state);
29353             }
29354         }
29355     },
29356     
29357     /**
29358      * Focus the button
29359      */
29360     focus : function(){
29361         this.el.child('button:first').focus();
29362     },
29363     
29364     /**
29365      * Disable this button
29366      */
29367     disable : function(){
29368         if(this.el){
29369             this.el.addClass("x-btn-disabled");
29370         }
29371         this.disabled = true;
29372     },
29373     
29374     /**
29375      * Enable this button
29376      */
29377     enable : function(){
29378         if(this.el){
29379             this.el.removeClass("x-btn-disabled");
29380         }
29381         this.disabled = false;
29382     },
29383
29384     /**
29385      * Convenience function for boolean enable/disable
29386      * @param {Boolean} enabled True to enable, false to disable
29387      */
29388     setDisabled : function(v){
29389         this[v !== true ? "enable" : "disable"]();
29390     },
29391
29392     // private
29393     onClick : function(e)
29394     {
29395         if(e){
29396             e.preventDefault();
29397         }
29398         if(e.button != 0){
29399             return;
29400         }
29401         if(!this.disabled){
29402             if(this.enableToggle){
29403                 this.toggle();
29404             }
29405             if(this.menu && !this.menu.isVisible()){
29406                 this.menu.show(this.el, this.menuAlign);
29407             }
29408             this.fireEvent("click", this, e);
29409             if(this.handler){
29410                 this.el.removeClass("x-btn-over");
29411                 this.handler.call(this.scope || this, this, e);
29412             }
29413         }
29414     },
29415     // private
29416     onMouseOver : function(e){
29417         if(!this.disabled){
29418             this.el.addClass("x-btn-over");
29419             this.fireEvent('mouseover', this, e);
29420         }
29421     },
29422     // private
29423     onMouseOut : function(e){
29424         if(!e.within(this.el,  true)){
29425             this.el.removeClass("x-btn-over");
29426             this.fireEvent('mouseout', this, e);
29427         }
29428     },
29429     // private
29430     onFocus : function(e){
29431         if(!this.disabled){
29432             this.el.addClass("x-btn-focus");
29433         }
29434     },
29435     // private
29436     onBlur : function(e){
29437         this.el.removeClass("x-btn-focus");
29438     },
29439     // private
29440     onMouseDown : function(e){
29441         if(!this.disabled && e.button == 0){
29442             this.el.addClass("x-btn-click");
29443             Roo.get(document).on('mouseup', this.onMouseUp, this);
29444         }
29445     },
29446     // private
29447     onMouseUp : function(e){
29448         if(e.button == 0){
29449             this.el.removeClass("x-btn-click");
29450             Roo.get(document).un('mouseup', this.onMouseUp, this);
29451         }
29452     },
29453     // private
29454     onMenuShow : function(e){
29455         this.el.addClass("x-btn-menu-active");
29456     },
29457     // private
29458     onMenuHide : function(e){
29459         this.el.removeClass("x-btn-menu-active");
29460     }   
29461 });
29462
29463 // Private utility class used by Button
29464 Roo.ButtonToggleMgr = function(){
29465    var groups = {};
29466    
29467    function toggleGroup(btn, state){
29468        if(state){
29469            var g = groups[btn.toggleGroup];
29470            for(var i = 0, l = g.length; i < l; i++){
29471                if(g[i] != btn){
29472                    g[i].toggle(false);
29473                }
29474            }
29475        }
29476    }
29477    
29478    return {
29479        register : function(btn){
29480            if(!btn.toggleGroup){
29481                return;
29482            }
29483            var g = groups[btn.toggleGroup];
29484            if(!g){
29485                g = groups[btn.toggleGroup] = [];
29486            }
29487            g.push(btn);
29488            btn.on("toggle", toggleGroup);
29489        },
29490        
29491        unregister : function(btn){
29492            if(!btn.toggleGroup){
29493                return;
29494            }
29495            var g = groups[btn.toggleGroup];
29496            if(g){
29497                g.remove(btn);
29498                btn.un("toggle", toggleGroup);
29499            }
29500        }
29501    };
29502 }();/*
29503  * Based on:
29504  * Ext JS Library 1.1.1
29505  * Copyright(c) 2006-2007, Ext JS, LLC.
29506  *
29507  * Originally Released Under LGPL - original licence link has changed is not relivant.
29508  *
29509  * Fork - LGPL
29510  * <script type="text/javascript">
29511  */
29512  
29513 /**
29514  * @class Roo.SplitButton
29515  * @extends Roo.Button
29516  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29517  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29518  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29519  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29520  * @cfg {String} arrowTooltip The title attribute of the arrow
29521  * @constructor
29522  * Create a new menu button
29523  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29524  * @param {Object} config The config object
29525  */
29526 Roo.SplitButton = function(renderTo, config){
29527     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29528     /**
29529      * @event arrowclick
29530      * Fires when this button's arrow is clicked
29531      * @param {SplitButton} this
29532      * @param {EventObject} e The click event
29533      */
29534     this.addEvents({"arrowclick":true});
29535 };
29536
29537 Roo.extend(Roo.SplitButton, Roo.Button, {
29538     render : function(renderTo){
29539         // this is one sweet looking template!
29540         var tpl = new Roo.Template(
29541             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29542             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29543             '<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>',
29544             "</tbody></table></td><td>",
29545             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29546             '<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>',
29547             "</tbody></table></td></tr></table>"
29548         );
29549         var btn = tpl.append(renderTo, [this.text, this.type], true);
29550         var btnEl = btn.child("button");
29551         if(this.cls){
29552             btn.addClass(this.cls);
29553         }
29554         if(this.icon){
29555             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29556         }
29557         if(this.iconCls){
29558             btnEl.addClass(this.iconCls);
29559             if(!this.cls){
29560                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29561             }
29562         }
29563         this.el = btn;
29564         if(this.handleMouseEvents){
29565             btn.on("mouseover", this.onMouseOver, this);
29566             btn.on("mouseout", this.onMouseOut, this);
29567             btn.on("mousedown", this.onMouseDown, this);
29568             btn.on("mouseup", this.onMouseUp, this);
29569         }
29570         btn.on(this.clickEvent, this.onClick, this);
29571         if(this.tooltip){
29572             if(typeof this.tooltip == 'object'){
29573                 Roo.QuickTips.tips(Roo.apply({
29574                       target: btnEl.id
29575                 }, this.tooltip));
29576             } else {
29577                 btnEl.dom[this.tooltipType] = this.tooltip;
29578             }
29579         }
29580         if(this.arrowTooltip){
29581             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29582         }
29583         if(this.hidden){
29584             this.hide();
29585         }
29586         if(this.disabled){
29587             this.disable();
29588         }
29589         if(this.pressed){
29590             this.el.addClass("x-btn-pressed");
29591         }
29592         if(Roo.isIE && !Roo.isIE7){
29593             this.autoWidth.defer(1, this);
29594         }else{
29595             this.autoWidth();
29596         }
29597         if(this.menu){
29598             this.menu.on("show", this.onMenuShow, this);
29599             this.menu.on("hide", this.onMenuHide, this);
29600         }
29601         this.fireEvent('render', this);
29602     },
29603
29604     // private
29605     autoWidth : function(){
29606         if(this.el){
29607             var tbl = this.el.child("table:first");
29608             var tbl2 = this.el.child("table:last");
29609             this.el.setWidth("auto");
29610             tbl.setWidth("auto");
29611             if(Roo.isIE7 && Roo.isStrict){
29612                 var ib = this.el.child('button:first');
29613                 if(ib && ib.getWidth() > 20){
29614                     ib.clip();
29615                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29616                 }
29617             }
29618             if(this.minWidth){
29619                 if(this.hidden){
29620                     this.el.beginMeasure();
29621                 }
29622                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29623                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29624                 }
29625                 if(this.hidden){
29626                     this.el.endMeasure();
29627                 }
29628             }
29629             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29630         } 
29631     },
29632     /**
29633      * Sets this button's click handler
29634      * @param {Function} handler The function to call when the button is clicked
29635      * @param {Object} scope (optional) Scope for the function passed above
29636      */
29637     setHandler : function(handler, scope){
29638         this.handler = handler;
29639         this.scope = scope;  
29640     },
29641     
29642     /**
29643      * Sets this button's arrow click handler
29644      * @param {Function} handler The function to call when the arrow is clicked
29645      * @param {Object} scope (optional) Scope for the function passed above
29646      */
29647     setArrowHandler : function(handler, scope){
29648         this.arrowHandler = handler;
29649         this.scope = scope;  
29650     },
29651     
29652     /**
29653      * Focus the button
29654      */
29655     focus : function(){
29656         if(this.el){
29657             this.el.child("button:first").focus();
29658         }
29659     },
29660
29661     // private
29662     onClick : function(e){
29663         e.preventDefault();
29664         if(!this.disabled){
29665             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29666                 if(this.menu && !this.menu.isVisible()){
29667                     this.menu.show(this.el, this.menuAlign);
29668                 }
29669                 this.fireEvent("arrowclick", this, e);
29670                 if(this.arrowHandler){
29671                     this.arrowHandler.call(this.scope || this, this, e);
29672                 }
29673             }else{
29674                 this.fireEvent("click", this, e);
29675                 if(this.handler){
29676                     this.handler.call(this.scope || this, this, e);
29677                 }
29678             }
29679         }
29680     },
29681     // private
29682     onMouseDown : function(e){
29683         if(!this.disabled){
29684             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29685         }
29686     },
29687     // private
29688     onMouseUp : function(e){
29689         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29690     }   
29691 });
29692
29693
29694 // backwards compat
29695 Roo.MenuButton = Roo.SplitButton;/*
29696  * Based on:
29697  * Ext JS Library 1.1.1
29698  * Copyright(c) 2006-2007, Ext JS, LLC.
29699  *
29700  * Originally Released Under LGPL - original licence link has changed is not relivant.
29701  *
29702  * Fork - LGPL
29703  * <script type="text/javascript">
29704  */
29705
29706 /**
29707  * @class Roo.Toolbar
29708  * Basic Toolbar class.
29709  * @constructor
29710  * Creates a new Toolbar
29711  * @param {Object} container The config object
29712  */ 
29713 Roo.Toolbar = function(container, buttons, config)
29714 {
29715     /// old consturctor format still supported..
29716     if(container instanceof Array){ // omit the container for later rendering
29717         buttons = container;
29718         config = buttons;
29719         container = null;
29720     }
29721     if (typeof(container) == 'object' && container.xtype) {
29722         config = container;
29723         container = config.container;
29724         buttons = config.buttons || []; // not really - use items!!
29725     }
29726     var xitems = [];
29727     if (config && config.items) {
29728         xitems = config.items;
29729         delete config.items;
29730     }
29731     Roo.apply(this, config);
29732     this.buttons = buttons;
29733     
29734     if(container){
29735         this.render(container);
29736     }
29737     this.xitems = xitems;
29738     Roo.each(xitems, function(b) {
29739         this.add(b);
29740     }, this);
29741     
29742 };
29743
29744 Roo.Toolbar.prototype = {
29745     /**
29746      * @cfg {Array} items
29747      * array of button configs or elements to add (will be converted to a MixedCollection)
29748      */
29749     
29750     /**
29751      * @cfg {String/HTMLElement/Element} container
29752      * The id or element that will contain the toolbar
29753      */
29754     // private
29755     render : function(ct){
29756         this.el = Roo.get(ct);
29757         if(this.cls){
29758             this.el.addClass(this.cls);
29759         }
29760         // using a table allows for vertical alignment
29761         // 100% width is needed by Safari...
29762         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29763         this.tr = this.el.child("tr", true);
29764         var autoId = 0;
29765         this.items = new Roo.util.MixedCollection(false, function(o){
29766             return o.id || ("item" + (++autoId));
29767         });
29768         if(this.buttons){
29769             this.add.apply(this, this.buttons);
29770             delete this.buttons;
29771         }
29772     },
29773
29774     /**
29775      * Adds element(s) to the toolbar -- this function takes a variable number of 
29776      * arguments of mixed type and adds them to the toolbar.
29777      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29778      * <ul>
29779      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29780      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29781      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29782      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29783      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29784      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29785      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29786      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29787      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29788      * </ul>
29789      * @param {Mixed} arg2
29790      * @param {Mixed} etc.
29791      */
29792     add : function(){
29793         var a = arguments, l = a.length;
29794         for(var i = 0; i < l; i++){
29795             this._add(a[i]);
29796         }
29797     },
29798     // private..
29799     _add : function(el) {
29800         
29801         if (el.xtype) {
29802             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29803         }
29804         
29805         if (el.applyTo){ // some kind of form field
29806             return this.addField(el);
29807         } 
29808         if (el.render){ // some kind of Toolbar.Item
29809             return this.addItem(el);
29810         }
29811         if (typeof el == "string"){ // string
29812             if(el == "separator" || el == "-"){
29813                 return this.addSeparator();
29814             }
29815             if (el == " "){
29816                 return this.addSpacer();
29817             }
29818             if(el == "->"){
29819                 return this.addFill();
29820             }
29821             return this.addText(el);
29822             
29823         }
29824         if(el.tagName){ // element
29825             return this.addElement(el);
29826         }
29827         if(typeof el == "object"){ // must be button config?
29828             return this.addButton(el);
29829         }
29830         // and now what?!?!
29831         return false;
29832         
29833     },
29834     
29835     /**
29836      * Add an Xtype element
29837      * @param {Object} xtype Xtype Object
29838      * @return {Object} created Object
29839      */
29840     addxtype : function(e){
29841         return this.add(e);  
29842     },
29843     
29844     /**
29845      * Returns the Element for this toolbar.
29846      * @return {Roo.Element}
29847      */
29848     getEl : function(){
29849         return this.el;  
29850     },
29851     
29852     /**
29853      * Adds a separator
29854      * @return {Roo.Toolbar.Item} The separator item
29855      */
29856     addSeparator : function(){
29857         return this.addItem(new Roo.Toolbar.Separator());
29858     },
29859
29860     /**
29861      * Adds a spacer element
29862      * @return {Roo.Toolbar.Spacer} The spacer item
29863      */
29864     addSpacer : function(){
29865         return this.addItem(new Roo.Toolbar.Spacer());
29866     },
29867
29868     /**
29869      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29870      * @return {Roo.Toolbar.Fill} The fill item
29871      */
29872     addFill : function(){
29873         return this.addItem(new Roo.Toolbar.Fill());
29874     },
29875
29876     /**
29877      * Adds any standard HTML element to the toolbar
29878      * @param {String/HTMLElement/Element} el The element or id of the element to add
29879      * @return {Roo.Toolbar.Item} The element's item
29880      */
29881     addElement : function(el){
29882         return this.addItem(new Roo.Toolbar.Item(el));
29883     },
29884     /**
29885      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29886      * @type Roo.util.MixedCollection  
29887      */
29888     items : false,
29889      
29890     /**
29891      * Adds any Toolbar.Item or subclass
29892      * @param {Roo.Toolbar.Item} item
29893      * @return {Roo.Toolbar.Item} The item
29894      */
29895     addItem : function(item){
29896         var td = this.nextBlock();
29897         item.render(td);
29898         this.items.add(item);
29899         return item;
29900     },
29901     
29902     /**
29903      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29904      * @param {Object/Array} config A button config or array of configs
29905      * @return {Roo.Toolbar.Button/Array}
29906      */
29907     addButton : function(config){
29908         if(config instanceof Array){
29909             var buttons = [];
29910             for(var i = 0, len = config.length; i < len; i++) {
29911                 buttons.push(this.addButton(config[i]));
29912             }
29913             return buttons;
29914         }
29915         var b = config;
29916         if(!(config instanceof Roo.Toolbar.Button)){
29917             b = config.split ?
29918                 new Roo.Toolbar.SplitButton(config) :
29919                 new Roo.Toolbar.Button(config);
29920         }
29921         var td = this.nextBlock();
29922         b.render(td);
29923         this.items.add(b);
29924         return b;
29925     },
29926     
29927     /**
29928      * Adds text to the toolbar
29929      * @param {String} text The text to add
29930      * @return {Roo.Toolbar.Item} The element's item
29931      */
29932     addText : function(text){
29933         return this.addItem(new Roo.Toolbar.TextItem(text));
29934     },
29935     
29936     /**
29937      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29938      * @param {Number} index The index where the item is to be inserted
29939      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29940      * @return {Roo.Toolbar.Button/Item}
29941      */
29942     insertButton : function(index, item){
29943         if(item instanceof Array){
29944             var buttons = [];
29945             for(var i = 0, len = item.length; i < len; i++) {
29946                buttons.push(this.insertButton(index + i, item[i]));
29947             }
29948             return buttons;
29949         }
29950         if (!(item instanceof Roo.Toolbar.Button)){
29951            item = new Roo.Toolbar.Button(item);
29952         }
29953         var td = document.createElement("td");
29954         this.tr.insertBefore(td, this.tr.childNodes[index]);
29955         item.render(td);
29956         this.items.insert(index, item);
29957         return item;
29958     },
29959     
29960     /**
29961      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29962      * @param {Object} config
29963      * @return {Roo.Toolbar.Item} The element's item
29964      */
29965     addDom : function(config, returnEl){
29966         var td = this.nextBlock();
29967         Roo.DomHelper.overwrite(td, config);
29968         var ti = new Roo.Toolbar.Item(td.firstChild);
29969         ti.render(td);
29970         this.items.add(ti);
29971         return ti;
29972     },
29973
29974     /**
29975      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29976      * @type Roo.util.MixedCollection  
29977      */
29978     fields : false,
29979     
29980     /**
29981      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29982      * Note: the field should not have been rendered yet. For a field that has already been
29983      * rendered, use {@link #addElement}.
29984      * @param {Roo.form.Field} field
29985      * @return {Roo.ToolbarItem}
29986      */
29987      
29988       
29989     addField : function(field) {
29990         if (!this.fields) {
29991             var autoId = 0;
29992             this.fields = new Roo.util.MixedCollection(false, function(o){
29993                 return o.id || ("item" + (++autoId));
29994             });
29995
29996         }
29997         
29998         var td = this.nextBlock();
29999         field.render(td);
30000         var ti = new Roo.Toolbar.Item(td.firstChild);
30001         ti.render(td);
30002         this.items.add(ti);
30003         this.fields.add(field);
30004         return ti;
30005     },
30006     /**
30007      * Hide the toolbar
30008      * @method hide
30009      */
30010      
30011       
30012     hide : function()
30013     {
30014         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30015         this.el.child('div').hide();
30016     },
30017     /**
30018      * Show the toolbar
30019      * @method show
30020      */
30021     show : function()
30022     {
30023         this.el.child('div').show();
30024     },
30025       
30026     // private
30027     nextBlock : function(){
30028         var td = document.createElement("td");
30029         this.tr.appendChild(td);
30030         return td;
30031     },
30032
30033     // private
30034     destroy : function(){
30035         if(this.items){ // rendered?
30036             Roo.destroy.apply(Roo, this.items.items);
30037         }
30038         if(this.fields){ // rendered?
30039             Roo.destroy.apply(Roo, this.fields.items);
30040         }
30041         Roo.Element.uncache(this.el, this.tr);
30042     }
30043 };
30044
30045 /**
30046  * @class Roo.Toolbar.Item
30047  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30048  * @constructor
30049  * Creates a new Item
30050  * @param {HTMLElement} el 
30051  */
30052 Roo.Toolbar.Item = function(el){
30053     var cfg = {};
30054     if (typeof (el.xtype) != 'undefined') {
30055         cfg = el;
30056         el = cfg.el;
30057     }
30058     
30059     this.el = Roo.getDom(el);
30060     this.id = Roo.id(this.el);
30061     this.hidden = false;
30062     
30063     this.addEvents({
30064          /**
30065              * @event render
30066              * Fires when the button is rendered
30067              * @param {Button} this
30068              */
30069         'render': true
30070     });
30071     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30072 };
30073 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30074 //Roo.Toolbar.Item.prototype = {
30075     
30076     /**
30077      * Get this item's HTML Element
30078      * @return {HTMLElement}
30079      */
30080     getEl : function(){
30081        return this.el;  
30082     },
30083
30084     // private
30085     render : function(td){
30086         
30087          this.td = td;
30088         td.appendChild(this.el);
30089         
30090         this.fireEvent('render', this);
30091     },
30092     
30093     /**
30094      * Removes and destroys this item.
30095      */
30096     destroy : function(){
30097         this.td.parentNode.removeChild(this.td);
30098     },
30099     
30100     /**
30101      * Shows this item.
30102      */
30103     show: function(){
30104         this.hidden = false;
30105         this.td.style.display = "";
30106     },
30107     
30108     /**
30109      * Hides this item.
30110      */
30111     hide: function(){
30112         this.hidden = true;
30113         this.td.style.display = "none";
30114     },
30115     
30116     /**
30117      * Convenience function for boolean show/hide.
30118      * @param {Boolean} visible true to show/false to hide
30119      */
30120     setVisible: function(visible){
30121         if(visible) {
30122             this.show();
30123         }else{
30124             this.hide();
30125         }
30126     },
30127     
30128     /**
30129      * Try to focus this item.
30130      */
30131     focus : function(){
30132         Roo.fly(this.el).focus();
30133     },
30134     
30135     /**
30136      * Disables this item.
30137      */
30138     disable : function(){
30139         Roo.fly(this.td).addClass("x-item-disabled");
30140         this.disabled = true;
30141         this.el.disabled = true;
30142     },
30143     
30144     /**
30145      * Enables this item.
30146      */
30147     enable : function(){
30148         Roo.fly(this.td).removeClass("x-item-disabled");
30149         this.disabled = false;
30150         this.el.disabled = false;
30151     }
30152 });
30153
30154
30155 /**
30156  * @class Roo.Toolbar.Separator
30157  * @extends Roo.Toolbar.Item
30158  * A simple toolbar separator class
30159  * @constructor
30160  * Creates a new Separator
30161  */
30162 Roo.Toolbar.Separator = function(cfg){
30163     
30164     var s = document.createElement("span");
30165     s.className = "ytb-sep";
30166     if (cfg) {
30167         cfg.el = s;
30168     }
30169     
30170     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30171 };
30172 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30173     enable:Roo.emptyFn,
30174     disable:Roo.emptyFn,
30175     focus:Roo.emptyFn
30176 });
30177
30178 /**
30179  * @class Roo.Toolbar.Spacer
30180  * @extends Roo.Toolbar.Item
30181  * A simple element that adds extra horizontal space to a toolbar.
30182  * @constructor
30183  * Creates a new Spacer
30184  */
30185 Roo.Toolbar.Spacer = function(cfg){
30186     var s = document.createElement("div");
30187     s.className = "ytb-spacer";
30188     if (cfg) {
30189         cfg.el = s;
30190     }
30191     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30192 };
30193 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30194     enable:Roo.emptyFn,
30195     disable:Roo.emptyFn,
30196     focus:Roo.emptyFn
30197 });
30198
30199 /**
30200  * @class Roo.Toolbar.Fill
30201  * @extends Roo.Toolbar.Spacer
30202  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30203  * @constructor
30204  * Creates a new Spacer
30205  */
30206 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30207     // private
30208     render : function(td){
30209         td.style.width = '100%';
30210         Roo.Toolbar.Fill.superclass.render.call(this, td);
30211     }
30212 });
30213
30214 /**
30215  * @class Roo.Toolbar.TextItem
30216  * @extends Roo.Toolbar.Item
30217  * A simple class that renders text directly into a toolbar.
30218  * @constructor
30219  * Creates a new TextItem
30220  * @param {String} text
30221  */
30222 Roo.Toolbar.TextItem = function(cfg){
30223     var  text = cfg || "";
30224     if (typeof(cfg) == 'object') {
30225         text = cfg.text || "";
30226     }  else {
30227         cfg = null;
30228     }
30229     var s = document.createElement("span");
30230     s.className = "ytb-text";
30231     s.innerHTML = text;
30232     if (cfg) {
30233         cfg.el  = s;
30234     }
30235     
30236     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30237 };
30238 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30239     
30240      
30241     enable:Roo.emptyFn,
30242     disable:Roo.emptyFn,
30243     focus:Roo.emptyFn
30244 });
30245
30246 /**
30247  * @class Roo.Toolbar.Button
30248  * @extends Roo.Button
30249  * A button that renders into a toolbar.
30250  * @constructor
30251  * Creates a new Button
30252  * @param {Object} config A standard {@link Roo.Button} config object
30253  */
30254 Roo.Toolbar.Button = function(config){
30255     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30256 };
30257 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30258     render : function(td){
30259         this.td = td;
30260         Roo.Toolbar.Button.superclass.render.call(this, td);
30261     },
30262     
30263     /**
30264      * Removes and destroys this button
30265      */
30266     destroy : function(){
30267         Roo.Toolbar.Button.superclass.destroy.call(this);
30268         this.td.parentNode.removeChild(this.td);
30269     },
30270     
30271     /**
30272      * Shows this button
30273      */
30274     show: function(){
30275         this.hidden = false;
30276         this.td.style.display = "";
30277     },
30278     
30279     /**
30280      * Hides this button
30281      */
30282     hide: function(){
30283         this.hidden = true;
30284         this.td.style.display = "none";
30285     },
30286
30287     /**
30288      * Disables this item
30289      */
30290     disable : function(){
30291         Roo.fly(this.td).addClass("x-item-disabled");
30292         this.disabled = true;
30293     },
30294
30295     /**
30296      * Enables this item
30297      */
30298     enable : function(){
30299         Roo.fly(this.td).removeClass("x-item-disabled");
30300         this.disabled = false;
30301     }
30302 });
30303 // backwards compat
30304 Roo.ToolbarButton = Roo.Toolbar.Button;
30305
30306 /**
30307  * @class Roo.Toolbar.SplitButton
30308  * @extends Roo.SplitButton
30309  * A menu button that renders into a toolbar.
30310  * @constructor
30311  * Creates a new SplitButton
30312  * @param {Object} config A standard {@link Roo.SplitButton} config object
30313  */
30314 Roo.Toolbar.SplitButton = function(config){
30315     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30316 };
30317 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30318     render : function(td){
30319         this.td = td;
30320         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30321     },
30322     
30323     /**
30324      * Removes and destroys this button
30325      */
30326     destroy : function(){
30327         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30328         this.td.parentNode.removeChild(this.td);
30329     },
30330     
30331     /**
30332      * Shows this button
30333      */
30334     show: function(){
30335         this.hidden = false;
30336         this.td.style.display = "";
30337     },
30338     
30339     /**
30340      * Hides this button
30341      */
30342     hide: function(){
30343         this.hidden = true;
30344         this.td.style.display = "none";
30345     }
30346 });
30347
30348 // backwards compat
30349 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30350  * Based on:
30351  * Ext JS Library 1.1.1
30352  * Copyright(c) 2006-2007, Ext JS, LLC.
30353  *
30354  * Originally Released Under LGPL - original licence link has changed is not relivant.
30355  *
30356  * Fork - LGPL
30357  * <script type="text/javascript">
30358  */
30359  
30360 /**
30361  * @class Roo.PagingToolbar
30362  * @extends Roo.Toolbar
30363  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30364  * @constructor
30365  * Create a new PagingToolbar
30366  * @param {Object} config The config object
30367  */
30368 Roo.PagingToolbar = function(el, ds, config)
30369 {
30370     // old args format still supported... - xtype is prefered..
30371     if (typeof(el) == 'object' && el.xtype) {
30372         // created from xtype...
30373         config = el;
30374         ds = el.dataSource;
30375         el = config.container;
30376     }
30377     var items = [];
30378     if (config.items) {
30379         items = config.items;
30380         config.items = [];
30381     }
30382     
30383     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30384     this.ds = ds;
30385     this.cursor = 0;
30386     this.renderButtons(this.el);
30387     this.bind(ds);
30388     
30389     // supprot items array.
30390    
30391     Roo.each(items, function(e) {
30392         this.add(Roo.factory(e));
30393     },this);
30394     
30395 };
30396
30397 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30398     /**
30399      * @cfg {Roo.data.Store} dataSource
30400      * The underlying data store providing the paged data
30401      */
30402     /**
30403      * @cfg {String/HTMLElement/Element} container
30404      * container The id or element that will contain the toolbar
30405      */
30406     /**
30407      * @cfg {Boolean} displayInfo
30408      * True to display the displayMsg (defaults to false)
30409      */
30410     /**
30411      * @cfg {Number} pageSize
30412      * The number of records to display per page (defaults to 20)
30413      */
30414     pageSize: 20,
30415     /**
30416      * @cfg {String} displayMsg
30417      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30418      */
30419     displayMsg : 'Displaying {0} - {1} of {2}',
30420     /**
30421      * @cfg {String} emptyMsg
30422      * The message to display when no records are found (defaults to "No data to display")
30423      */
30424     emptyMsg : 'No data to display',
30425     /**
30426      * Customizable piece of the default paging text (defaults to "Page")
30427      * @type String
30428      */
30429     beforePageText : "Page",
30430     /**
30431      * Customizable piece of the default paging text (defaults to "of %0")
30432      * @type String
30433      */
30434     afterPageText : "of {0}",
30435     /**
30436      * Customizable piece of the default paging text (defaults to "First Page")
30437      * @type String
30438      */
30439     firstText : "First Page",
30440     /**
30441      * Customizable piece of the default paging text (defaults to "Previous Page")
30442      * @type String
30443      */
30444     prevText : "Previous Page",
30445     /**
30446      * Customizable piece of the default paging text (defaults to "Next Page")
30447      * @type String
30448      */
30449     nextText : "Next Page",
30450     /**
30451      * Customizable piece of the default paging text (defaults to "Last Page")
30452      * @type String
30453      */
30454     lastText : "Last Page",
30455     /**
30456      * Customizable piece of the default paging text (defaults to "Refresh")
30457      * @type String
30458      */
30459     refreshText : "Refresh",
30460
30461     // private
30462     renderButtons : function(el){
30463         Roo.PagingToolbar.superclass.render.call(this, el);
30464         this.first = this.addButton({
30465             tooltip: this.firstText,
30466             cls: "x-btn-icon x-grid-page-first",
30467             disabled: true,
30468             handler: this.onClick.createDelegate(this, ["first"])
30469         });
30470         this.prev = this.addButton({
30471             tooltip: this.prevText,
30472             cls: "x-btn-icon x-grid-page-prev",
30473             disabled: true,
30474             handler: this.onClick.createDelegate(this, ["prev"])
30475         });
30476         //this.addSeparator();
30477         this.add(this.beforePageText);
30478         this.field = Roo.get(this.addDom({
30479            tag: "input",
30480            type: "text",
30481            size: "3",
30482            value: "1",
30483            cls: "x-grid-page-number"
30484         }).el);
30485         this.field.on("keydown", this.onPagingKeydown, this);
30486         this.field.on("focus", function(){this.dom.select();});
30487         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30488         this.field.setHeight(18);
30489         //this.addSeparator();
30490         this.next = this.addButton({
30491             tooltip: this.nextText,
30492             cls: "x-btn-icon x-grid-page-next",
30493             disabled: true,
30494             handler: this.onClick.createDelegate(this, ["next"])
30495         });
30496         this.last = this.addButton({
30497             tooltip: this.lastText,
30498             cls: "x-btn-icon x-grid-page-last",
30499             disabled: true,
30500             handler: this.onClick.createDelegate(this, ["last"])
30501         });
30502         //this.addSeparator();
30503         this.loading = this.addButton({
30504             tooltip: this.refreshText,
30505             cls: "x-btn-icon x-grid-loading",
30506             handler: this.onClick.createDelegate(this, ["refresh"])
30507         });
30508
30509         if(this.displayInfo){
30510             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30511         }
30512     },
30513
30514     // private
30515     updateInfo : function(){
30516         if(this.displayEl){
30517             var count = this.ds.getCount();
30518             var msg = count == 0 ?
30519                 this.emptyMsg :
30520                 String.format(
30521                     this.displayMsg,
30522                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30523                 );
30524             this.displayEl.update(msg);
30525         }
30526     },
30527
30528     // private
30529     onLoad : function(ds, r, o){
30530        this.cursor = o.params ? o.params.start : 0;
30531        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30532
30533        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30534        this.field.dom.value = ap;
30535        this.first.setDisabled(ap == 1);
30536        this.prev.setDisabled(ap == 1);
30537        this.next.setDisabled(ap == ps);
30538        this.last.setDisabled(ap == ps);
30539        this.loading.enable();
30540        this.updateInfo();
30541     },
30542
30543     // private
30544     getPageData : function(){
30545         var total = this.ds.getTotalCount();
30546         return {
30547             total : total,
30548             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30549             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30550         };
30551     },
30552
30553     // private
30554     onLoadError : function(){
30555         this.loading.enable();
30556     },
30557
30558     // private
30559     onPagingKeydown : function(e){
30560         var k = e.getKey();
30561         var d = this.getPageData();
30562         if(k == e.RETURN){
30563             var v = this.field.dom.value, pageNum;
30564             if(!v || isNaN(pageNum = parseInt(v, 10))){
30565                 this.field.dom.value = d.activePage;
30566                 return;
30567             }
30568             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30569             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30570             e.stopEvent();
30571         }
30572         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))
30573         {
30574           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30575           this.field.dom.value = pageNum;
30576           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30577           e.stopEvent();
30578         }
30579         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30580         {
30581           var v = this.field.dom.value, pageNum; 
30582           var increment = (e.shiftKey) ? 10 : 1;
30583           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30584             increment *= -1;
30585           }
30586           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30587             this.field.dom.value = d.activePage;
30588             return;
30589           }
30590           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30591           {
30592             this.field.dom.value = parseInt(v, 10) + increment;
30593             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30594             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30595           }
30596           e.stopEvent();
30597         }
30598     },
30599
30600     // private
30601     beforeLoad : function(){
30602         if(this.loading){
30603             this.loading.disable();
30604         }
30605     },
30606
30607     // private
30608     onClick : function(which){
30609         var ds = this.ds;
30610         switch(which){
30611             case "first":
30612                 ds.load({params:{start: 0, limit: this.pageSize}});
30613             break;
30614             case "prev":
30615                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30616             break;
30617             case "next":
30618                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30619             break;
30620             case "last":
30621                 var total = ds.getTotalCount();
30622                 var extra = total % this.pageSize;
30623                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30624                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30625             break;
30626             case "refresh":
30627                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30628             break;
30629         }
30630     },
30631
30632     /**
30633      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30634      * @param {Roo.data.Store} store The data store to unbind
30635      */
30636     unbind : function(ds){
30637         ds.un("beforeload", this.beforeLoad, this);
30638         ds.un("load", this.onLoad, this);
30639         ds.un("loadexception", this.onLoadError, this);
30640         ds.un("remove", this.updateInfo, this);
30641         ds.un("add", this.updateInfo, this);
30642         this.ds = undefined;
30643     },
30644
30645     /**
30646      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30647      * @param {Roo.data.Store} store The data store to bind
30648      */
30649     bind : function(ds){
30650         ds.on("beforeload", this.beforeLoad, this);
30651         ds.on("load", this.onLoad, this);
30652         ds.on("loadexception", this.onLoadError, this);
30653         ds.on("remove", this.updateInfo, this);
30654         ds.on("add", this.updateInfo, this);
30655         this.ds = ds;
30656     }
30657 });/*
30658  * Based on:
30659  * Ext JS Library 1.1.1
30660  * Copyright(c) 2006-2007, Ext JS, LLC.
30661  *
30662  * Originally Released Under LGPL - original licence link has changed is not relivant.
30663  *
30664  * Fork - LGPL
30665  * <script type="text/javascript">
30666  */
30667
30668 /**
30669  * @class Roo.Resizable
30670  * @extends Roo.util.Observable
30671  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30672  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30673  * 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
30674  * the element will be wrapped for you automatically.</p>
30675  * <p>Here is the list of valid resize handles:</p>
30676  * <pre>
30677 Value   Description
30678 ------  -------------------
30679  'n'     north
30680  's'     south
30681  'e'     east
30682  'w'     west
30683  'nw'    northwest
30684  'sw'    southwest
30685  'se'    southeast
30686  'ne'    northeast
30687  'hd'    horizontal drag
30688  'all'   all
30689 </pre>
30690  * <p>Here's an example showing the creation of a typical Resizable:</p>
30691  * <pre><code>
30692 var resizer = new Roo.Resizable("element-id", {
30693     handles: 'all',
30694     minWidth: 200,
30695     minHeight: 100,
30696     maxWidth: 500,
30697     maxHeight: 400,
30698     pinned: true
30699 });
30700 resizer.on("resize", myHandler);
30701 </code></pre>
30702  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30703  * resizer.east.setDisplayed(false);</p>
30704  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30705  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30706  * resize operation's new size (defaults to [0, 0])
30707  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30708  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30709  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30710  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30711  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30712  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30713  * @cfg {Number} width The width of the element in pixels (defaults to null)
30714  * @cfg {Number} height The height of the element in pixels (defaults to null)
30715  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30716  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30717  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30718  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30719  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30720  * in favor of the handles config option (defaults to false)
30721  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30722  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30723  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30724  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30725  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30726  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30727  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30728  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30729  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30730  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30731  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30732  * @constructor
30733  * Create a new resizable component
30734  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30735  * @param {Object} config configuration options
30736   */
30737 Roo.Resizable = function(el, config)
30738 {
30739     this.el = Roo.get(el);
30740
30741     if(config && config.wrap){
30742         config.resizeChild = this.el;
30743         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30744         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30745         this.el.setStyle("overflow", "hidden");
30746         this.el.setPositioning(config.resizeChild.getPositioning());
30747         config.resizeChild.clearPositioning();
30748         if(!config.width || !config.height){
30749             var csize = config.resizeChild.getSize();
30750             this.el.setSize(csize.width, csize.height);
30751         }
30752         if(config.pinned && !config.adjustments){
30753             config.adjustments = "auto";
30754         }
30755     }
30756
30757     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30758     this.proxy.unselectable();
30759     this.proxy.enableDisplayMode('block');
30760
30761     Roo.apply(this, config);
30762
30763     if(this.pinned){
30764         this.disableTrackOver = true;
30765         this.el.addClass("x-resizable-pinned");
30766     }
30767     // if the element isn't positioned, make it relative
30768     var position = this.el.getStyle("position");
30769     if(position != "absolute" && position != "fixed"){
30770         this.el.setStyle("position", "relative");
30771     }
30772     if(!this.handles){ // no handles passed, must be legacy style
30773         this.handles = 's,e,se';
30774         if(this.multiDirectional){
30775             this.handles += ',n,w';
30776         }
30777     }
30778     if(this.handles == "all"){
30779         this.handles = "n s e w ne nw se sw";
30780     }
30781     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30782     var ps = Roo.Resizable.positions;
30783     for(var i = 0, len = hs.length; i < len; i++){
30784         if(hs[i] && ps[hs[i]]){
30785             var pos = ps[hs[i]];
30786             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30787         }
30788     }
30789     // legacy
30790     this.corner = this.southeast;
30791     
30792     // updateBox = the box can move..
30793     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30794         this.updateBox = true;
30795     }
30796
30797     this.activeHandle = null;
30798
30799     if(this.resizeChild){
30800         if(typeof this.resizeChild == "boolean"){
30801             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30802         }else{
30803             this.resizeChild = Roo.get(this.resizeChild, true);
30804         }
30805     }
30806     
30807     if(this.adjustments == "auto"){
30808         var rc = this.resizeChild;
30809         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30810         if(rc && (hw || hn)){
30811             rc.position("relative");
30812             rc.setLeft(hw ? hw.el.getWidth() : 0);
30813             rc.setTop(hn ? hn.el.getHeight() : 0);
30814         }
30815         this.adjustments = [
30816             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30817             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30818         ];
30819     }
30820
30821     if(this.draggable){
30822         this.dd = this.dynamic ?
30823             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30824         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30825     }
30826
30827     // public events
30828     this.addEvents({
30829         /**
30830          * @event beforeresize
30831          * Fired before resize is allowed. Set enabled to false to cancel resize.
30832          * @param {Roo.Resizable} this
30833          * @param {Roo.EventObject} e The mousedown event
30834          */
30835         "beforeresize" : true,
30836         /**
30837          * @event resizing
30838          * Fired a resizing.
30839          * @param {Roo.Resizable} this
30840          * @param {Number} x The new x position
30841          * @param {Number} y The new y position
30842          * @param {Number} w The new w width
30843          * @param {Number} h The new h hight
30844          * @param {Roo.EventObject} e The mouseup event
30845          */
30846         "resizing" : true,
30847         /**
30848          * @event resize
30849          * Fired after a resize.
30850          * @param {Roo.Resizable} this
30851          * @param {Number} width The new width
30852          * @param {Number} height The new height
30853          * @param {Roo.EventObject} e The mouseup event
30854          */
30855         "resize" : true
30856     });
30857
30858     if(this.width !== null && this.height !== null){
30859         this.resizeTo(this.width, this.height);
30860     }else{
30861         this.updateChildSize();
30862     }
30863     if(Roo.isIE){
30864         this.el.dom.style.zoom = 1;
30865     }
30866     Roo.Resizable.superclass.constructor.call(this);
30867 };
30868
30869 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30870         resizeChild : false,
30871         adjustments : [0, 0],
30872         minWidth : 5,
30873         minHeight : 5,
30874         maxWidth : 10000,
30875         maxHeight : 10000,
30876         enabled : true,
30877         animate : false,
30878         duration : .35,
30879         dynamic : false,
30880         handles : false,
30881         multiDirectional : false,
30882         disableTrackOver : false,
30883         easing : 'easeOutStrong',
30884         widthIncrement : 0,
30885         heightIncrement : 0,
30886         pinned : false,
30887         width : null,
30888         height : null,
30889         preserveRatio : false,
30890         transparent: false,
30891         minX: 0,
30892         minY: 0,
30893         draggable: false,
30894
30895         /**
30896          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30897          */
30898         constrainTo: undefined,
30899         /**
30900          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30901          */
30902         resizeRegion: undefined,
30903
30904
30905     /**
30906      * Perform a manual resize
30907      * @param {Number} width
30908      * @param {Number} height
30909      */
30910     resizeTo : function(width, height){
30911         this.el.setSize(width, height);
30912         this.updateChildSize();
30913         this.fireEvent("resize", this, width, height, null);
30914     },
30915
30916     // private
30917     startSizing : function(e, handle){
30918         this.fireEvent("beforeresize", this, e);
30919         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30920
30921             if(!this.overlay){
30922                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30923                 this.overlay.unselectable();
30924                 this.overlay.enableDisplayMode("block");
30925                 this.overlay.on("mousemove", this.onMouseMove, this);
30926                 this.overlay.on("mouseup", this.onMouseUp, this);
30927             }
30928             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30929
30930             this.resizing = true;
30931             this.startBox = this.el.getBox();
30932             this.startPoint = e.getXY();
30933             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30934                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30935
30936             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30937             this.overlay.show();
30938
30939             if(this.constrainTo) {
30940                 var ct = Roo.get(this.constrainTo);
30941                 this.resizeRegion = ct.getRegion().adjust(
30942                     ct.getFrameWidth('t'),
30943                     ct.getFrameWidth('l'),
30944                     -ct.getFrameWidth('b'),
30945                     -ct.getFrameWidth('r')
30946                 );
30947             }
30948
30949             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30950             this.proxy.show();
30951             this.proxy.setBox(this.startBox);
30952             if(!this.dynamic){
30953                 this.proxy.setStyle('visibility', 'visible');
30954             }
30955         }
30956     },
30957
30958     // private
30959     onMouseDown : function(handle, e){
30960         if(this.enabled){
30961             e.stopEvent();
30962             this.activeHandle = handle;
30963             this.startSizing(e, handle);
30964         }
30965     },
30966
30967     // private
30968     onMouseUp : function(e){
30969         var size = this.resizeElement();
30970         this.resizing = false;
30971         this.handleOut();
30972         this.overlay.hide();
30973         this.proxy.hide();
30974         this.fireEvent("resize", this, size.width, size.height, e);
30975     },
30976
30977     // private
30978     updateChildSize : function(){
30979         
30980         if(this.resizeChild){
30981             var el = this.el;
30982             var child = this.resizeChild;
30983             var adj = this.adjustments;
30984             if(el.dom.offsetWidth){
30985                 var b = el.getSize(true);
30986                 child.setSize(b.width+adj[0], b.height+adj[1]);
30987             }
30988             // Second call here for IE
30989             // The first call enables instant resizing and
30990             // the second call corrects scroll bars if they
30991             // exist
30992             if(Roo.isIE){
30993                 setTimeout(function(){
30994                     if(el.dom.offsetWidth){
30995                         var b = el.getSize(true);
30996                         child.setSize(b.width+adj[0], b.height+adj[1]);
30997                     }
30998                 }, 10);
30999             }
31000         }
31001     },
31002
31003     // private
31004     snap : function(value, inc, min){
31005         if(!inc || !value) {
31006             return value;
31007         }
31008         var newValue = value;
31009         var m = value % inc;
31010         if(m > 0){
31011             if(m > (inc/2)){
31012                 newValue = value + (inc-m);
31013             }else{
31014                 newValue = value - m;
31015             }
31016         }
31017         return Math.max(min, newValue);
31018     },
31019
31020     // private
31021     resizeElement : function(){
31022         var box = this.proxy.getBox();
31023         if(this.updateBox){
31024             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31025         }else{
31026             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31027         }
31028         this.updateChildSize();
31029         if(!this.dynamic){
31030             this.proxy.hide();
31031         }
31032         return box;
31033     },
31034
31035     // private
31036     constrain : function(v, diff, m, mx){
31037         if(v - diff < m){
31038             diff = v - m;
31039         }else if(v - diff > mx){
31040             diff = mx - v;
31041         }
31042         return diff;
31043     },
31044
31045     // private
31046     onMouseMove : function(e){
31047         
31048         if(this.enabled){
31049             try{// try catch so if something goes wrong the user doesn't get hung
31050
31051             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31052                 return;
31053             }
31054
31055             //var curXY = this.startPoint;
31056             var curSize = this.curSize || this.startBox;
31057             var x = this.startBox.x, y = this.startBox.y;
31058             var ox = x, oy = y;
31059             var w = curSize.width, h = curSize.height;
31060             var ow = w, oh = h;
31061             var mw = this.minWidth, mh = this.minHeight;
31062             var mxw = this.maxWidth, mxh = this.maxHeight;
31063             var wi = this.widthIncrement;
31064             var hi = this.heightIncrement;
31065
31066             var eventXY = e.getXY();
31067             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31068             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31069
31070             var pos = this.activeHandle.position;
31071
31072             switch(pos){
31073                 case "east":
31074                     w += diffX;
31075                     w = Math.min(Math.max(mw, w), mxw);
31076                     break;
31077              
31078                 case "south":
31079                     h += diffY;
31080                     h = Math.min(Math.max(mh, h), mxh);
31081                     break;
31082                 case "southeast":
31083                     w += diffX;
31084                     h += diffY;
31085                     w = Math.min(Math.max(mw, w), mxw);
31086                     h = Math.min(Math.max(mh, h), mxh);
31087                     break;
31088                 case "north":
31089                     diffY = this.constrain(h, diffY, mh, mxh);
31090                     y += diffY;
31091                     h -= diffY;
31092                     break;
31093                 case "hdrag":
31094                     
31095                     if (wi) {
31096                         var adiffX = Math.abs(diffX);
31097                         var sub = (adiffX % wi); // how much 
31098                         if (sub > (wi/2)) { // far enough to snap
31099                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31100                         } else {
31101                             // remove difference.. 
31102                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31103                         }
31104                     }
31105                     x += diffX;
31106                     x = Math.max(this.minX, x);
31107                     break;
31108                 case "west":
31109                     diffX = this.constrain(w, diffX, mw, mxw);
31110                     x += diffX;
31111                     w -= diffX;
31112                     break;
31113                 case "northeast":
31114                     w += diffX;
31115                     w = Math.min(Math.max(mw, w), mxw);
31116                     diffY = this.constrain(h, diffY, mh, mxh);
31117                     y += diffY;
31118                     h -= diffY;
31119                     break;
31120                 case "northwest":
31121                     diffX = this.constrain(w, diffX, mw, mxw);
31122                     diffY = this.constrain(h, diffY, mh, mxh);
31123                     y += diffY;
31124                     h -= diffY;
31125                     x += diffX;
31126                     w -= diffX;
31127                     break;
31128                case "southwest":
31129                     diffX = this.constrain(w, diffX, mw, mxw);
31130                     h += diffY;
31131                     h = Math.min(Math.max(mh, h), mxh);
31132                     x += diffX;
31133                     w -= diffX;
31134                     break;
31135             }
31136
31137             var sw = this.snap(w, wi, mw);
31138             var sh = this.snap(h, hi, mh);
31139             if(sw != w || sh != h){
31140                 switch(pos){
31141                     case "northeast":
31142                         y -= sh - h;
31143                     break;
31144                     case "north":
31145                         y -= sh - h;
31146                         break;
31147                     case "southwest":
31148                         x -= sw - w;
31149                     break;
31150                     case "west":
31151                         x -= sw - w;
31152                         break;
31153                     case "northwest":
31154                         x -= sw - w;
31155                         y -= sh - h;
31156                     break;
31157                 }
31158                 w = sw;
31159                 h = sh;
31160             }
31161
31162             if(this.preserveRatio){
31163                 switch(pos){
31164                     case "southeast":
31165                     case "east":
31166                         h = oh * (w/ow);
31167                         h = Math.min(Math.max(mh, h), mxh);
31168                         w = ow * (h/oh);
31169                        break;
31170                     case "south":
31171                         w = ow * (h/oh);
31172                         w = Math.min(Math.max(mw, w), mxw);
31173                         h = oh * (w/ow);
31174                         break;
31175                     case "northeast":
31176                         w = ow * (h/oh);
31177                         w = Math.min(Math.max(mw, w), mxw);
31178                         h = oh * (w/ow);
31179                     break;
31180                     case "north":
31181                         var tw = w;
31182                         w = ow * (h/oh);
31183                         w = Math.min(Math.max(mw, w), mxw);
31184                         h = oh * (w/ow);
31185                         x += (tw - w) / 2;
31186                         break;
31187                     case "southwest":
31188                         h = oh * (w/ow);
31189                         h = Math.min(Math.max(mh, h), mxh);
31190                         var tw = w;
31191                         w = ow * (h/oh);
31192                         x += tw - w;
31193                         break;
31194                     case "west":
31195                         var th = h;
31196                         h = oh * (w/ow);
31197                         h = Math.min(Math.max(mh, h), mxh);
31198                         y += (th - h) / 2;
31199                         var tw = w;
31200                         w = ow * (h/oh);
31201                         x += tw - w;
31202                        break;
31203                     case "northwest":
31204                         var tw = w;
31205                         var th = h;
31206                         h = oh * (w/ow);
31207                         h = Math.min(Math.max(mh, h), mxh);
31208                         w = ow * (h/oh);
31209                         y += th - h;
31210                         x += tw - w;
31211                        break;
31212
31213                 }
31214             }
31215             if (pos == 'hdrag') {
31216                 w = ow;
31217             }
31218             this.proxy.setBounds(x, y, w, h);
31219             if(this.dynamic){
31220                 this.resizeElement();
31221             }
31222             }catch(e){}
31223         }
31224         this.fireEvent("resizing", this, x, y, w, h, e);
31225     },
31226
31227     // private
31228     handleOver : function(){
31229         if(this.enabled){
31230             this.el.addClass("x-resizable-over");
31231         }
31232     },
31233
31234     // private
31235     handleOut : function(){
31236         if(!this.resizing){
31237             this.el.removeClass("x-resizable-over");
31238         }
31239     },
31240
31241     /**
31242      * Returns the element this component is bound to.
31243      * @return {Roo.Element}
31244      */
31245     getEl : function(){
31246         return this.el;
31247     },
31248
31249     /**
31250      * Returns the resizeChild element (or null).
31251      * @return {Roo.Element}
31252      */
31253     getResizeChild : function(){
31254         return this.resizeChild;
31255     },
31256     groupHandler : function()
31257     {
31258         
31259     },
31260     /**
31261      * Destroys this resizable. If the element was wrapped and
31262      * removeEl is not true then the element remains.
31263      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31264      */
31265     destroy : function(removeEl){
31266         this.proxy.remove();
31267         if(this.overlay){
31268             this.overlay.removeAllListeners();
31269             this.overlay.remove();
31270         }
31271         var ps = Roo.Resizable.positions;
31272         for(var k in ps){
31273             if(typeof ps[k] != "function" && this[ps[k]]){
31274                 var h = this[ps[k]];
31275                 h.el.removeAllListeners();
31276                 h.el.remove();
31277             }
31278         }
31279         if(removeEl){
31280             this.el.update("");
31281             this.el.remove();
31282         }
31283     }
31284 });
31285
31286 // private
31287 // hash to map config positions to true positions
31288 Roo.Resizable.positions = {
31289     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31290     hd: "hdrag"
31291 };
31292
31293 // private
31294 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31295     if(!this.tpl){
31296         // only initialize the template if resizable is used
31297         var tpl = Roo.DomHelper.createTemplate(
31298             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31299         );
31300         tpl.compile();
31301         Roo.Resizable.Handle.prototype.tpl = tpl;
31302     }
31303     this.position = pos;
31304     this.rz = rz;
31305     // show north drag fro topdra
31306     var handlepos = pos == 'hdrag' ? 'north' : pos;
31307     
31308     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31309     if (pos == 'hdrag') {
31310         this.el.setStyle('cursor', 'pointer');
31311     }
31312     this.el.unselectable();
31313     if(transparent){
31314         this.el.setOpacity(0);
31315     }
31316     this.el.on("mousedown", this.onMouseDown, this);
31317     if(!disableTrackOver){
31318         this.el.on("mouseover", this.onMouseOver, this);
31319         this.el.on("mouseout", this.onMouseOut, this);
31320     }
31321 };
31322
31323 // private
31324 Roo.Resizable.Handle.prototype = {
31325     afterResize : function(rz){
31326         Roo.log('after?');
31327         // do nothing
31328     },
31329     // private
31330     onMouseDown : function(e){
31331         this.rz.onMouseDown(this, e);
31332     },
31333     // private
31334     onMouseOver : function(e){
31335         this.rz.handleOver(this, e);
31336     },
31337     // private
31338     onMouseOut : function(e){
31339         this.rz.handleOut(this, e);
31340     }
31341 };/*
31342  * Based on:
31343  * Ext JS Library 1.1.1
31344  * Copyright(c) 2006-2007, Ext JS, LLC.
31345  *
31346  * Originally Released Under LGPL - original licence link has changed is not relivant.
31347  *
31348  * Fork - LGPL
31349  * <script type="text/javascript">
31350  */
31351
31352 /**
31353  * @class Roo.Editor
31354  * @extends Roo.Component
31355  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31356  * @constructor
31357  * Create a new Editor
31358  * @param {Roo.form.Field} field The Field object (or descendant)
31359  * @param {Object} config The config object
31360  */
31361 Roo.Editor = function(field, config){
31362     Roo.Editor.superclass.constructor.call(this, config);
31363     this.field = field;
31364     this.addEvents({
31365         /**
31366              * @event beforestartedit
31367              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31368              * false from the handler of this event.
31369              * @param {Editor} this
31370              * @param {Roo.Element} boundEl The underlying element bound to this editor
31371              * @param {Mixed} value The field value being set
31372              */
31373         "beforestartedit" : true,
31374         /**
31375              * @event startedit
31376              * Fires when this editor is displayed
31377              * @param {Roo.Element} boundEl The underlying element bound to this editor
31378              * @param {Mixed} value The starting field value
31379              */
31380         "startedit" : true,
31381         /**
31382              * @event beforecomplete
31383              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31384              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31385              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31386              * event will not fire since no edit actually occurred.
31387              * @param {Editor} this
31388              * @param {Mixed} value The current field value
31389              * @param {Mixed} startValue The original field value
31390              */
31391         "beforecomplete" : true,
31392         /**
31393              * @event complete
31394              * Fires after editing is complete and any changed value has been written to the underlying field.
31395              * @param {Editor} this
31396              * @param {Mixed} value The current field value
31397              * @param {Mixed} startValue The original field value
31398              */
31399         "complete" : true,
31400         /**
31401          * @event specialkey
31402          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31403          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31404          * @param {Roo.form.Field} this
31405          * @param {Roo.EventObject} e The event object
31406          */
31407         "specialkey" : true
31408     });
31409 };
31410
31411 Roo.extend(Roo.Editor, Roo.Component, {
31412     /**
31413      * @cfg {Boolean/String} autosize
31414      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31415      * or "height" to adopt the height only (defaults to false)
31416      */
31417     /**
31418      * @cfg {Boolean} revertInvalid
31419      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31420      * validation fails (defaults to true)
31421      */
31422     /**
31423      * @cfg {Boolean} ignoreNoChange
31424      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31425      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31426      * will never be ignored.
31427      */
31428     /**
31429      * @cfg {Boolean} hideEl
31430      * False to keep the bound element visible while the editor is displayed (defaults to true)
31431      */
31432     /**
31433      * @cfg {Mixed} value
31434      * The data value of the underlying field (defaults to "")
31435      */
31436     value : "",
31437     /**
31438      * @cfg {String} alignment
31439      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31440      */
31441     alignment: "c-c?",
31442     /**
31443      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31444      * for bottom-right shadow (defaults to "frame")
31445      */
31446     shadow : "frame",
31447     /**
31448      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31449      */
31450     constrain : false,
31451     /**
31452      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31453      */
31454     completeOnEnter : false,
31455     /**
31456      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31457      */
31458     cancelOnEsc : false,
31459     /**
31460      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31461      */
31462     updateEl : false,
31463
31464     // private
31465     onRender : function(ct, position){
31466         this.el = new Roo.Layer({
31467             shadow: this.shadow,
31468             cls: "x-editor",
31469             parentEl : ct,
31470             shim : this.shim,
31471             shadowOffset:4,
31472             id: this.id,
31473             constrain: this.constrain
31474         });
31475         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31476         if(this.field.msgTarget != 'title'){
31477             this.field.msgTarget = 'qtip';
31478         }
31479         this.field.render(this.el);
31480         if(Roo.isGecko){
31481             this.field.el.dom.setAttribute('autocomplete', 'off');
31482         }
31483         this.field.on("specialkey", this.onSpecialKey, this);
31484         if(this.swallowKeys){
31485             this.field.el.swallowEvent(['keydown','keypress']);
31486         }
31487         this.field.show();
31488         this.field.on("blur", this.onBlur, this);
31489         if(this.field.grow){
31490             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31491         }
31492     },
31493
31494     onSpecialKey : function(field, e)
31495     {
31496         //Roo.log('editor onSpecialKey');
31497         if(this.completeOnEnter && e.getKey() == e.ENTER){
31498             e.stopEvent();
31499             this.completeEdit();
31500             return;
31501         }
31502         // do not fire special key otherwise it might hide close the editor...
31503         if(e.getKey() == e.ENTER){    
31504             return;
31505         }
31506         if(this.cancelOnEsc && e.getKey() == e.ESC){
31507             this.cancelEdit();
31508             return;
31509         } 
31510         this.fireEvent('specialkey', field, e);
31511     
31512     },
31513
31514     /**
31515      * Starts the editing process and shows the editor.
31516      * @param {String/HTMLElement/Element} el The element to edit
31517      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31518       * to the innerHTML of el.
31519      */
31520     startEdit : function(el, value){
31521         if(this.editing){
31522             this.completeEdit();
31523         }
31524         this.boundEl = Roo.get(el);
31525         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31526         if(!this.rendered){
31527             this.render(this.parentEl || document.body);
31528         }
31529         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31530             return;
31531         }
31532         this.startValue = v;
31533         this.field.setValue(v);
31534         if(this.autoSize){
31535             var sz = this.boundEl.getSize();
31536             switch(this.autoSize){
31537                 case "width":
31538                 this.setSize(sz.width,  "");
31539                 break;
31540                 case "height":
31541                 this.setSize("",  sz.height);
31542                 break;
31543                 default:
31544                 this.setSize(sz.width,  sz.height);
31545             }
31546         }
31547         this.el.alignTo(this.boundEl, this.alignment);
31548         this.editing = true;
31549         if(Roo.QuickTips){
31550             Roo.QuickTips.disable();
31551         }
31552         this.show();
31553     },
31554
31555     /**
31556      * Sets the height and width of this editor.
31557      * @param {Number} width The new width
31558      * @param {Number} height The new height
31559      */
31560     setSize : function(w, h){
31561         this.field.setSize(w, h);
31562         if(this.el){
31563             this.el.sync();
31564         }
31565     },
31566
31567     /**
31568      * Realigns the editor to the bound field based on the current alignment config value.
31569      */
31570     realign : function(){
31571         this.el.alignTo(this.boundEl, this.alignment);
31572     },
31573
31574     /**
31575      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31576      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31577      */
31578     completeEdit : function(remainVisible){
31579         if(!this.editing){
31580             return;
31581         }
31582         var v = this.getValue();
31583         if(this.revertInvalid !== false && !this.field.isValid()){
31584             v = this.startValue;
31585             this.cancelEdit(true);
31586         }
31587         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31588             this.editing = false;
31589             this.hide();
31590             return;
31591         }
31592         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31593             this.editing = false;
31594             if(this.updateEl && this.boundEl){
31595                 this.boundEl.update(v);
31596             }
31597             if(remainVisible !== true){
31598                 this.hide();
31599             }
31600             this.fireEvent("complete", this, v, this.startValue);
31601         }
31602     },
31603
31604     // private
31605     onShow : function(){
31606         this.el.show();
31607         if(this.hideEl !== false){
31608             this.boundEl.hide();
31609         }
31610         this.field.show();
31611         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31612             this.fixIEFocus = true;
31613             this.deferredFocus.defer(50, this);
31614         }else{
31615             this.field.focus();
31616         }
31617         this.fireEvent("startedit", this.boundEl, this.startValue);
31618     },
31619
31620     deferredFocus : function(){
31621         if(this.editing){
31622             this.field.focus();
31623         }
31624     },
31625
31626     /**
31627      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31628      * reverted to the original starting value.
31629      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31630      * cancel (defaults to false)
31631      */
31632     cancelEdit : function(remainVisible){
31633         if(this.editing){
31634             this.setValue(this.startValue);
31635             if(remainVisible !== true){
31636                 this.hide();
31637             }
31638         }
31639     },
31640
31641     // private
31642     onBlur : function(){
31643         if(this.allowBlur !== true && this.editing){
31644             this.completeEdit();
31645         }
31646     },
31647
31648     // private
31649     onHide : function(){
31650         if(this.editing){
31651             this.completeEdit();
31652             return;
31653         }
31654         this.field.blur();
31655         if(this.field.collapse){
31656             this.field.collapse();
31657         }
31658         this.el.hide();
31659         if(this.hideEl !== false){
31660             this.boundEl.show();
31661         }
31662         if(Roo.QuickTips){
31663             Roo.QuickTips.enable();
31664         }
31665     },
31666
31667     /**
31668      * Sets the data value of the editor
31669      * @param {Mixed} value Any valid value supported by the underlying field
31670      */
31671     setValue : function(v){
31672         this.field.setValue(v);
31673     },
31674
31675     /**
31676      * Gets the data value of the editor
31677      * @return {Mixed} The data value
31678      */
31679     getValue : function(){
31680         return this.field.getValue();
31681     }
31682 });/*
31683  * Based on:
31684  * Ext JS Library 1.1.1
31685  * Copyright(c) 2006-2007, Ext JS, LLC.
31686  *
31687  * Originally Released Under LGPL - original licence link has changed is not relivant.
31688  *
31689  * Fork - LGPL
31690  * <script type="text/javascript">
31691  */
31692  
31693 /**
31694  * @class Roo.BasicDialog
31695  * @extends Roo.util.Observable
31696  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31697  * <pre><code>
31698 var dlg = new Roo.BasicDialog("my-dlg", {
31699     height: 200,
31700     width: 300,
31701     minHeight: 100,
31702     minWidth: 150,
31703     modal: true,
31704     proxyDrag: true,
31705     shadow: true
31706 });
31707 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31708 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31709 dlg.addButton('Cancel', dlg.hide, dlg);
31710 dlg.show();
31711 </code></pre>
31712   <b>A Dialog should always be a direct child of the body element.</b>
31713  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31714  * @cfg {String} title Default text to display in the title bar (defaults to null)
31715  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31716  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31717  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31718  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31719  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31720  * (defaults to null with no animation)
31721  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31722  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31723  * property for valid values (defaults to 'all')
31724  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31725  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31726  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31727  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31728  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31729  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31730  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31731  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31732  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31733  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31734  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31735  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31736  * draggable = true (defaults to false)
31737  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31738  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31739  * shadow (defaults to false)
31740  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31741  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31742  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31743  * @cfg {Array} buttons Array of buttons
31744  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31745  * @constructor
31746  * Create a new BasicDialog.
31747  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31748  * @param {Object} config Configuration options
31749  */
31750 Roo.BasicDialog = function(el, config){
31751     this.el = Roo.get(el);
31752     var dh = Roo.DomHelper;
31753     if(!this.el && config && config.autoCreate){
31754         if(typeof config.autoCreate == "object"){
31755             if(!config.autoCreate.id){
31756                 config.autoCreate.id = el;
31757             }
31758             this.el = dh.append(document.body,
31759                         config.autoCreate, true);
31760         }else{
31761             this.el = dh.append(document.body,
31762                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31763         }
31764     }
31765     el = this.el;
31766     el.setDisplayed(true);
31767     el.hide = this.hideAction;
31768     this.id = el.id;
31769     el.addClass("x-dlg");
31770
31771     Roo.apply(this, config);
31772
31773     this.proxy = el.createProxy("x-dlg-proxy");
31774     this.proxy.hide = this.hideAction;
31775     this.proxy.setOpacity(.5);
31776     this.proxy.hide();
31777
31778     if(config.width){
31779         el.setWidth(config.width);
31780     }
31781     if(config.height){
31782         el.setHeight(config.height);
31783     }
31784     this.size = el.getSize();
31785     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31786         this.xy = [config.x,config.y];
31787     }else{
31788         this.xy = el.getCenterXY(true);
31789     }
31790     /** The header element @type Roo.Element */
31791     this.header = el.child("> .x-dlg-hd");
31792     /** The body element @type Roo.Element */
31793     this.body = el.child("> .x-dlg-bd");
31794     /** The footer element @type Roo.Element */
31795     this.footer = el.child("> .x-dlg-ft");
31796
31797     if(!this.header){
31798         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31799     }
31800     if(!this.body){
31801         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31802     }
31803
31804     this.header.unselectable();
31805     if(this.title){
31806         this.header.update(this.title);
31807     }
31808     // this element allows the dialog to be focused for keyboard event
31809     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31810     this.focusEl.swallowEvent("click", true);
31811
31812     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31813
31814     // wrap the body and footer for special rendering
31815     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31816     if(this.footer){
31817         this.bwrap.dom.appendChild(this.footer.dom);
31818     }
31819
31820     this.bg = this.el.createChild({
31821         tag: "div", cls:"x-dlg-bg",
31822         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31823     });
31824     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31825
31826
31827     if(this.autoScroll !== false && !this.autoTabs){
31828         this.body.setStyle("overflow", "auto");
31829     }
31830
31831     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31832
31833     if(this.closable !== false){
31834         this.el.addClass("x-dlg-closable");
31835         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31836         this.close.on("click", this.closeClick, this);
31837         this.close.addClassOnOver("x-dlg-close-over");
31838     }
31839     if(this.collapsible !== false){
31840         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31841         this.collapseBtn.on("click", this.collapseClick, this);
31842         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31843         this.header.on("dblclick", this.collapseClick, this);
31844     }
31845     if(this.resizable !== false){
31846         this.el.addClass("x-dlg-resizable");
31847         this.resizer = new Roo.Resizable(el, {
31848             minWidth: this.minWidth || 80,
31849             minHeight:this.minHeight || 80,
31850             handles: this.resizeHandles || "all",
31851             pinned: true
31852         });
31853         this.resizer.on("beforeresize", this.beforeResize, this);
31854         this.resizer.on("resize", this.onResize, this);
31855     }
31856     if(this.draggable !== false){
31857         el.addClass("x-dlg-draggable");
31858         if (!this.proxyDrag) {
31859             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31860         }
31861         else {
31862             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31863         }
31864         dd.setHandleElId(this.header.id);
31865         dd.endDrag = this.endMove.createDelegate(this);
31866         dd.startDrag = this.startMove.createDelegate(this);
31867         dd.onDrag = this.onDrag.createDelegate(this);
31868         dd.scroll = false;
31869         this.dd = dd;
31870     }
31871     if(this.modal){
31872         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31873         this.mask.enableDisplayMode("block");
31874         this.mask.hide();
31875         this.el.addClass("x-dlg-modal");
31876     }
31877     if(this.shadow){
31878         this.shadow = new Roo.Shadow({
31879             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31880             offset : this.shadowOffset
31881         });
31882     }else{
31883         this.shadowOffset = 0;
31884     }
31885     if(Roo.useShims && this.shim !== false){
31886         this.shim = this.el.createShim();
31887         this.shim.hide = this.hideAction;
31888         this.shim.hide();
31889     }else{
31890         this.shim = false;
31891     }
31892     if(this.autoTabs){
31893         this.initTabs();
31894     }
31895     if (this.buttons) { 
31896         var bts= this.buttons;
31897         this.buttons = [];
31898         Roo.each(bts, function(b) {
31899             this.addButton(b);
31900         }, this);
31901     }
31902     
31903     
31904     this.addEvents({
31905         /**
31906          * @event keydown
31907          * Fires when a key is pressed
31908          * @param {Roo.BasicDialog} this
31909          * @param {Roo.EventObject} e
31910          */
31911         "keydown" : true,
31912         /**
31913          * @event move
31914          * Fires when this dialog is moved by the user.
31915          * @param {Roo.BasicDialog} this
31916          * @param {Number} x The new page X
31917          * @param {Number} y The new page Y
31918          */
31919         "move" : true,
31920         /**
31921          * @event resize
31922          * Fires when this dialog is resized by the user.
31923          * @param {Roo.BasicDialog} this
31924          * @param {Number} width The new width
31925          * @param {Number} height The new height
31926          */
31927         "resize" : true,
31928         /**
31929          * @event beforehide
31930          * Fires before this dialog is hidden.
31931          * @param {Roo.BasicDialog} this
31932          */
31933         "beforehide" : true,
31934         /**
31935          * @event hide
31936          * Fires when this dialog is hidden.
31937          * @param {Roo.BasicDialog} this
31938          */
31939         "hide" : true,
31940         /**
31941          * @event beforeshow
31942          * Fires before this dialog is shown.
31943          * @param {Roo.BasicDialog} this
31944          */
31945         "beforeshow" : true,
31946         /**
31947          * @event show
31948          * Fires when this dialog is shown.
31949          * @param {Roo.BasicDialog} this
31950          */
31951         "show" : true
31952     });
31953     el.on("keydown", this.onKeyDown, this);
31954     el.on("mousedown", this.toFront, this);
31955     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31956     this.el.hide();
31957     Roo.DialogManager.register(this);
31958     Roo.BasicDialog.superclass.constructor.call(this);
31959 };
31960
31961 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31962     shadowOffset: Roo.isIE ? 6 : 5,
31963     minHeight: 80,
31964     minWidth: 200,
31965     minButtonWidth: 75,
31966     defaultButton: null,
31967     buttonAlign: "right",
31968     tabTag: 'div',
31969     firstShow: true,
31970
31971     /**
31972      * Sets the dialog title text
31973      * @param {String} text The title text to display
31974      * @return {Roo.BasicDialog} this
31975      */
31976     setTitle : function(text){
31977         this.header.update(text);
31978         return this;
31979     },
31980
31981     // private
31982     closeClick : function(){
31983         this.hide();
31984     },
31985
31986     // private
31987     collapseClick : function(){
31988         this[this.collapsed ? "expand" : "collapse"]();
31989     },
31990
31991     /**
31992      * Collapses the dialog to its minimized state (only the title bar is visible).
31993      * Equivalent to the user clicking the collapse dialog button.
31994      */
31995     collapse : function(){
31996         if(!this.collapsed){
31997             this.collapsed = true;
31998             this.el.addClass("x-dlg-collapsed");
31999             this.restoreHeight = this.el.getHeight();
32000             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32001         }
32002     },
32003
32004     /**
32005      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32006      * clicking the expand dialog button.
32007      */
32008     expand : function(){
32009         if(this.collapsed){
32010             this.collapsed = false;
32011             this.el.removeClass("x-dlg-collapsed");
32012             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32013         }
32014     },
32015
32016     /**
32017      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32018      * @return {Roo.TabPanel} The tabs component
32019      */
32020     initTabs : function(){
32021         var tabs = this.getTabs();
32022         while(tabs.getTab(0)){
32023             tabs.removeTab(0);
32024         }
32025         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32026             var dom = el.dom;
32027             tabs.addTab(Roo.id(dom), dom.title);
32028             dom.title = "";
32029         });
32030         tabs.activate(0);
32031         return tabs;
32032     },
32033
32034     // private
32035     beforeResize : function(){
32036         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32037     },
32038
32039     // private
32040     onResize : function(){
32041         this.refreshSize();
32042         this.syncBodyHeight();
32043         this.adjustAssets();
32044         this.focus();
32045         this.fireEvent("resize", this, this.size.width, this.size.height);
32046     },
32047
32048     // private
32049     onKeyDown : function(e){
32050         if(this.isVisible()){
32051             this.fireEvent("keydown", this, e);
32052         }
32053     },
32054
32055     /**
32056      * Resizes the dialog.
32057      * @param {Number} width
32058      * @param {Number} height
32059      * @return {Roo.BasicDialog} this
32060      */
32061     resizeTo : function(width, height){
32062         this.el.setSize(width, height);
32063         this.size = {width: width, height: height};
32064         this.syncBodyHeight();
32065         if(this.fixedcenter){
32066             this.center();
32067         }
32068         if(this.isVisible()){
32069             this.constrainXY();
32070             this.adjustAssets();
32071         }
32072         this.fireEvent("resize", this, width, height);
32073         return this;
32074     },
32075
32076
32077     /**
32078      * Resizes the dialog to fit the specified content size.
32079      * @param {Number} width
32080      * @param {Number} height
32081      * @return {Roo.BasicDialog} this
32082      */
32083     setContentSize : function(w, h){
32084         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32085         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32086         //if(!this.el.isBorderBox()){
32087             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32088             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32089         //}
32090         if(this.tabs){
32091             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32092             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32093         }
32094         this.resizeTo(w, h);
32095         return this;
32096     },
32097
32098     /**
32099      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32100      * executed in response to a particular key being pressed while the dialog is active.
32101      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32102      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32103      * @param {Function} fn The function to call
32104      * @param {Object} scope (optional) The scope of the function
32105      * @return {Roo.BasicDialog} this
32106      */
32107     addKeyListener : function(key, fn, scope){
32108         var keyCode, shift, ctrl, alt;
32109         if(typeof key == "object" && !(key instanceof Array)){
32110             keyCode = key["key"];
32111             shift = key["shift"];
32112             ctrl = key["ctrl"];
32113             alt = key["alt"];
32114         }else{
32115             keyCode = key;
32116         }
32117         var handler = function(dlg, e){
32118             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32119                 var k = e.getKey();
32120                 if(keyCode instanceof Array){
32121                     for(var i = 0, len = keyCode.length; i < len; i++){
32122                         if(keyCode[i] == k){
32123                           fn.call(scope || window, dlg, k, e);
32124                           return;
32125                         }
32126                     }
32127                 }else{
32128                     if(k == keyCode){
32129                         fn.call(scope || window, dlg, k, e);
32130                     }
32131                 }
32132             }
32133         };
32134         this.on("keydown", handler);
32135         return this;
32136     },
32137
32138     /**
32139      * Returns the TabPanel component (creates it if it doesn't exist).
32140      * Note: If you wish to simply check for the existence of tabs without creating them,
32141      * check for a null 'tabs' property.
32142      * @return {Roo.TabPanel} The tabs component
32143      */
32144     getTabs : function(){
32145         if(!this.tabs){
32146             this.el.addClass("x-dlg-auto-tabs");
32147             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32148             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32149         }
32150         return this.tabs;
32151     },
32152
32153     /**
32154      * Adds a button to the footer section of the dialog.
32155      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32156      * object or a valid Roo.DomHelper element config
32157      * @param {Function} handler The function called when the button is clicked
32158      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32159      * @return {Roo.Button} The new button
32160      */
32161     addButton : function(config, handler, scope){
32162         var dh = Roo.DomHelper;
32163         if(!this.footer){
32164             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32165         }
32166         if(!this.btnContainer){
32167             var tb = this.footer.createChild({
32168
32169                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32170                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32171             }, null, true);
32172             this.btnContainer = tb.firstChild.firstChild.firstChild;
32173         }
32174         var bconfig = {
32175             handler: handler,
32176             scope: scope,
32177             minWidth: this.minButtonWidth,
32178             hideParent:true
32179         };
32180         if(typeof config == "string"){
32181             bconfig.text = config;
32182         }else{
32183             if(config.tag){
32184                 bconfig.dhconfig = config;
32185             }else{
32186                 Roo.apply(bconfig, config);
32187             }
32188         }
32189         var fc = false;
32190         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32191             bconfig.position = Math.max(0, bconfig.position);
32192             fc = this.btnContainer.childNodes[bconfig.position];
32193         }
32194          
32195         var btn = new Roo.Button(
32196             fc ? 
32197                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32198                 : this.btnContainer.appendChild(document.createElement("td")),
32199             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32200             bconfig
32201         );
32202         this.syncBodyHeight();
32203         if(!this.buttons){
32204             /**
32205              * Array of all the buttons that have been added to this dialog via addButton
32206              * @type Array
32207              */
32208             this.buttons = [];
32209         }
32210         this.buttons.push(btn);
32211         return btn;
32212     },
32213
32214     /**
32215      * Sets the default button to be focused when the dialog is displayed.
32216      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32217      * @return {Roo.BasicDialog} this
32218      */
32219     setDefaultButton : function(btn){
32220         this.defaultButton = btn;
32221         return this;
32222     },
32223
32224     // private
32225     getHeaderFooterHeight : function(safe){
32226         var height = 0;
32227         if(this.header){
32228            height += this.header.getHeight();
32229         }
32230         if(this.footer){
32231            var fm = this.footer.getMargins();
32232             height += (this.footer.getHeight()+fm.top+fm.bottom);
32233         }
32234         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32235         height += this.centerBg.getPadding("tb");
32236         return height;
32237     },
32238
32239     // private
32240     syncBodyHeight : function()
32241     {
32242         var bd = this.body, // the text
32243             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32244             bw = this.bwrap;
32245         var height = this.size.height - this.getHeaderFooterHeight(false);
32246         bd.setHeight(height-bd.getMargins("tb"));
32247         var hh = this.header.getHeight();
32248         var h = this.size.height-hh;
32249         cb.setHeight(h);
32250         
32251         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32252         bw.setHeight(h-cb.getPadding("tb"));
32253         
32254         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32255         bd.setWidth(bw.getWidth(true));
32256         if(this.tabs){
32257             this.tabs.syncHeight();
32258             if(Roo.isIE){
32259                 this.tabs.el.repaint();
32260             }
32261         }
32262     },
32263
32264     /**
32265      * Restores the previous state of the dialog if Roo.state is configured.
32266      * @return {Roo.BasicDialog} this
32267      */
32268     restoreState : function(){
32269         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32270         if(box && box.width){
32271             this.xy = [box.x, box.y];
32272             this.resizeTo(box.width, box.height);
32273         }
32274         return this;
32275     },
32276
32277     // private
32278     beforeShow : function(){
32279         this.expand();
32280         if(this.fixedcenter){
32281             this.xy = this.el.getCenterXY(true);
32282         }
32283         if(this.modal){
32284             Roo.get(document.body).addClass("x-body-masked");
32285             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32286             this.mask.show();
32287         }
32288         this.constrainXY();
32289     },
32290
32291     // private
32292     animShow : function(){
32293         var b = Roo.get(this.animateTarget).getBox();
32294         this.proxy.setSize(b.width, b.height);
32295         this.proxy.setLocation(b.x, b.y);
32296         this.proxy.show();
32297         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32298                     true, .35, this.showEl.createDelegate(this));
32299     },
32300
32301     /**
32302      * Shows the dialog.
32303      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32304      * @return {Roo.BasicDialog} this
32305      */
32306     show : function(animateTarget){
32307         if (this.fireEvent("beforeshow", this) === false){
32308             return;
32309         }
32310         if(this.syncHeightBeforeShow){
32311             this.syncBodyHeight();
32312         }else if(this.firstShow){
32313             this.firstShow = false;
32314             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32315         }
32316         this.animateTarget = animateTarget || this.animateTarget;
32317         if(!this.el.isVisible()){
32318             this.beforeShow();
32319             if(this.animateTarget && Roo.get(this.animateTarget)){
32320                 this.animShow();
32321             }else{
32322                 this.showEl();
32323             }
32324         }
32325         return this;
32326     },
32327
32328     // private
32329     showEl : function(){
32330         this.proxy.hide();
32331         this.el.setXY(this.xy);
32332         this.el.show();
32333         this.adjustAssets(true);
32334         this.toFront();
32335         this.focus();
32336         // IE peekaboo bug - fix found by Dave Fenwick
32337         if(Roo.isIE){
32338             this.el.repaint();
32339         }
32340         this.fireEvent("show", this);
32341     },
32342
32343     /**
32344      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32345      * dialog itself will receive focus.
32346      */
32347     focus : function(){
32348         if(this.defaultButton){
32349             this.defaultButton.focus();
32350         }else{
32351             this.focusEl.focus();
32352         }
32353     },
32354
32355     // private
32356     constrainXY : function(){
32357         if(this.constraintoviewport !== false){
32358             if(!this.viewSize){
32359                 if(this.container){
32360                     var s = this.container.getSize();
32361                     this.viewSize = [s.width, s.height];
32362                 }else{
32363                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32364                 }
32365             }
32366             var s = Roo.get(this.container||document).getScroll();
32367
32368             var x = this.xy[0], y = this.xy[1];
32369             var w = this.size.width, h = this.size.height;
32370             var vw = this.viewSize[0], vh = this.viewSize[1];
32371             // only move it if it needs it
32372             var moved = false;
32373             // first validate right/bottom
32374             if(x + w > vw+s.left){
32375                 x = vw - w;
32376                 moved = true;
32377             }
32378             if(y + h > vh+s.top){
32379                 y = vh - h;
32380                 moved = true;
32381             }
32382             // then make sure top/left isn't negative
32383             if(x < s.left){
32384                 x = s.left;
32385                 moved = true;
32386             }
32387             if(y < s.top){
32388                 y = s.top;
32389                 moved = true;
32390             }
32391             if(moved){
32392                 // cache xy
32393                 this.xy = [x, y];
32394                 if(this.isVisible()){
32395                     this.el.setLocation(x, y);
32396                     this.adjustAssets();
32397                 }
32398             }
32399         }
32400     },
32401
32402     // private
32403     onDrag : function(){
32404         if(!this.proxyDrag){
32405             this.xy = this.el.getXY();
32406             this.adjustAssets();
32407         }
32408     },
32409
32410     // private
32411     adjustAssets : function(doShow){
32412         var x = this.xy[0], y = this.xy[1];
32413         var w = this.size.width, h = this.size.height;
32414         if(doShow === true){
32415             if(this.shadow){
32416                 this.shadow.show(this.el);
32417             }
32418             if(this.shim){
32419                 this.shim.show();
32420             }
32421         }
32422         if(this.shadow && this.shadow.isVisible()){
32423             this.shadow.show(this.el);
32424         }
32425         if(this.shim && this.shim.isVisible()){
32426             this.shim.setBounds(x, y, w, h);
32427         }
32428     },
32429
32430     // private
32431     adjustViewport : function(w, h){
32432         if(!w || !h){
32433             w = Roo.lib.Dom.getViewWidth();
32434             h = Roo.lib.Dom.getViewHeight();
32435         }
32436         // cache the size
32437         this.viewSize = [w, h];
32438         if(this.modal && this.mask.isVisible()){
32439             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32440             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32441         }
32442         if(this.isVisible()){
32443             this.constrainXY();
32444         }
32445     },
32446
32447     /**
32448      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32449      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32450      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32451      */
32452     destroy : function(removeEl){
32453         if(this.isVisible()){
32454             this.animateTarget = null;
32455             this.hide();
32456         }
32457         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32458         if(this.tabs){
32459             this.tabs.destroy(removeEl);
32460         }
32461         Roo.destroy(
32462              this.shim,
32463              this.proxy,
32464              this.resizer,
32465              this.close,
32466              this.mask
32467         );
32468         if(this.dd){
32469             this.dd.unreg();
32470         }
32471         if(this.buttons){
32472            for(var i = 0, len = this.buttons.length; i < len; i++){
32473                this.buttons[i].destroy();
32474            }
32475         }
32476         this.el.removeAllListeners();
32477         if(removeEl === true){
32478             this.el.update("");
32479             this.el.remove();
32480         }
32481         Roo.DialogManager.unregister(this);
32482     },
32483
32484     // private
32485     startMove : function(){
32486         if(this.proxyDrag){
32487             this.proxy.show();
32488         }
32489         if(this.constraintoviewport !== false){
32490             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32491         }
32492     },
32493
32494     // private
32495     endMove : function(){
32496         if(!this.proxyDrag){
32497             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32498         }else{
32499             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32500             this.proxy.hide();
32501         }
32502         this.refreshSize();
32503         this.adjustAssets();
32504         this.focus();
32505         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32506     },
32507
32508     /**
32509      * Brings this dialog to the front of any other visible dialogs
32510      * @return {Roo.BasicDialog} this
32511      */
32512     toFront : function(){
32513         Roo.DialogManager.bringToFront(this);
32514         return this;
32515     },
32516
32517     /**
32518      * Sends this dialog to the back (under) of any other visible dialogs
32519      * @return {Roo.BasicDialog} this
32520      */
32521     toBack : function(){
32522         Roo.DialogManager.sendToBack(this);
32523         return this;
32524     },
32525
32526     /**
32527      * Centers this dialog in the viewport
32528      * @return {Roo.BasicDialog} this
32529      */
32530     center : function(){
32531         var xy = this.el.getCenterXY(true);
32532         this.moveTo(xy[0], xy[1]);
32533         return this;
32534     },
32535
32536     /**
32537      * Moves the dialog's top-left corner to the specified point
32538      * @param {Number} x
32539      * @param {Number} y
32540      * @return {Roo.BasicDialog} this
32541      */
32542     moveTo : function(x, y){
32543         this.xy = [x,y];
32544         if(this.isVisible()){
32545             this.el.setXY(this.xy);
32546             this.adjustAssets();
32547         }
32548         return this;
32549     },
32550
32551     /**
32552      * Aligns the dialog to the specified element
32553      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32554      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32555      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32556      * @return {Roo.BasicDialog} this
32557      */
32558     alignTo : function(element, position, offsets){
32559         this.xy = this.el.getAlignToXY(element, position, offsets);
32560         if(this.isVisible()){
32561             this.el.setXY(this.xy);
32562             this.adjustAssets();
32563         }
32564         return this;
32565     },
32566
32567     /**
32568      * Anchors an element to another element and realigns it when the window is resized.
32569      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32570      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32571      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32572      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32573      * is a number, it is used as the buffer delay (defaults to 50ms).
32574      * @return {Roo.BasicDialog} this
32575      */
32576     anchorTo : function(el, alignment, offsets, monitorScroll){
32577         var action = function(){
32578             this.alignTo(el, alignment, offsets);
32579         };
32580         Roo.EventManager.onWindowResize(action, this);
32581         var tm = typeof monitorScroll;
32582         if(tm != 'undefined'){
32583             Roo.EventManager.on(window, 'scroll', action, this,
32584                 {buffer: tm == 'number' ? monitorScroll : 50});
32585         }
32586         action.call(this);
32587         return this;
32588     },
32589
32590     /**
32591      * Returns true if the dialog is visible
32592      * @return {Boolean}
32593      */
32594     isVisible : function(){
32595         return this.el.isVisible();
32596     },
32597
32598     // private
32599     animHide : function(callback){
32600         var b = Roo.get(this.animateTarget).getBox();
32601         this.proxy.show();
32602         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32603         this.el.hide();
32604         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32605                     this.hideEl.createDelegate(this, [callback]));
32606     },
32607
32608     /**
32609      * Hides the dialog.
32610      * @param {Function} callback (optional) Function to call when the dialog is hidden
32611      * @return {Roo.BasicDialog} this
32612      */
32613     hide : function(callback){
32614         if (this.fireEvent("beforehide", this) === false){
32615             return;
32616         }
32617         if(this.shadow){
32618             this.shadow.hide();
32619         }
32620         if(this.shim) {
32621           this.shim.hide();
32622         }
32623         // sometimes animateTarget seems to get set.. causing problems...
32624         // this just double checks..
32625         if(this.animateTarget && Roo.get(this.animateTarget)) {
32626            this.animHide(callback);
32627         }else{
32628             this.el.hide();
32629             this.hideEl(callback);
32630         }
32631         return this;
32632     },
32633
32634     // private
32635     hideEl : function(callback){
32636         this.proxy.hide();
32637         if(this.modal){
32638             this.mask.hide();
32639             Roo.get(document.body).removeClass("x-body-masked");
32640         }
32641         this.fireEvent("hide", this);
32642         if(typeof callback == "function"){
32643             callback();
32644         }
32645     },
32646
32647     // private
32648     hideAction : function(){
32649         this.setLeft("-10000px");
32650         this.setTop("-10000px");
32651         this.setStyle("visibility", "hidden");
32652     },
32653
32654     // private
32655     refreshSize : function(){
32656         this.size = this.el.getSize();
32657         this.xy = this.el.getXY();
32658         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32659     },
32660
32661     // private
32662     // z-index is managed by the DialogManager and may be overwritten at any time
32663     setZIndex : function(index){
32664         if(this.modal){
32665             this.mask.setStyle("z-index", index);
32666         }
32667         if(this.shim){
32668             this.shim.setStyle("z-index", ++index);
32669         }
32670         if(this.shadow){
32671             this.shadow.setZIndex(++index);
32672         }
32673         this.el.setStyle("z-index", ++index);
32674         if(this.proxy){
32675             this.proxy.setStyle("z-index", ++index);
32676         }
32677         if(this.resizer){
32678             this.resizer.proxy.setStyle("z-index", ++index);
32679         }
32680
32681         this.lastZIndex = index;
32682     },
32683
32684     /**
32685      * Returns the element for this dialog
32686      * @return {Roo.Element} The underlying dialog Element
32687      */
32688     getEl : function(){
32689         return this.el;
32690     }
32691 });
32692
32693 /**
32694  * @class Roo.DialogManager
32695  * Provides global access to BasicDialogs that have been created and
32696  * support for z-indexing (layering) multiple open dialogs.
32697  */
32698 Roo.DialogManager = function(){
32699     var list = {};
32700     var accessList = [];
32701     var front = null;
32702
32703     // private
32704     var sortDialogs = function(d1, d2){
32705         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32706     };
32707
32708     // private
32709     var orderDialogs = function(){
32710         accessList.sort(sortDialogs);
32711         var seed = Roo.DialogManager.zseed;
32712         for(var i = 0, len = accessList.length; i < len; i++){
32713             var dlg = accessList[i];
32714             if(dlg){
32715                 dlg.setZIndex(seed + (i*10));
32716             }
32717         }
32718     };
32719
32720     return {
32721         /**
32722          * The starting z-index for BasicDialogs (defaults to 9000)
32723          * @type Number The z-index value
32724          */
32725         zseed : 9000,
32726
32727         // private
32728         register : function(dlg){
32729             list[dlg.id] = dlg;
32730             accessList.push(dlg);
32731         },
32732
32733         // private
32734         unregister : function(dlg){
32735             delete list[dlg.id];
32736             var i=0;
32737             var len=0;
32738             if(!accessList.indexOf){
32739                 for(  i = 0, len = accessList.length; i < len; i++){
32740                     if(accessList[i] == dlg){
32741                         accessList.splice(i, 1);
32742                         return;
32743                     }
32744                 }
32745             }else{
32746                  i = accessList.indexOf(dlg);
32747                 if(i != -1){
32748                     accessList.splice(i, 1);
32749                 }
32750             }
32751         },
32752
32753         /**
32754          * Gets a registered dialog by id
32755          * @param {String/Object} id The id of the dialog or a dialog
32756          * @return {Roo.BasicDialog} this
32757          */
32758         get : function(id){
32759             return typeof id == "object" ? id : list[id];
32760         },
32761
32762         /**
32763          * Brings the specified dialog to the front
32764          * @param {String/Object} dlg The id of the dialog or a dialog
32765          * @return {Roo.BasicDialog} this
32766          */
32767         bringToFront : function(dlg){
32768             dlg = this.get(dlg);
32769             if(dlg != front){
32770                 front = dlg;
32771                 dlg._lastAccess = new Date().getTime();
32772                 orderDialogs();
32773             }
32774             return dlg;
32775         },
32776
32777         /**
32778          * Sends the specified dialog to the back
32779          * @param {String/Object} dlg The id of the dialog or a dialog
32780          * @return {Roo.BasicDialog} this
32781          */
32782         sendToBack : function(dlg){
32783             dlg = this.get(dlg);
32784             dlg._lastAccess = -(new Date().getTime());
32785             orderDialogs();
32786             return dlg;
32787         },
32788
32789         /**
32790          * Hides all dialogs
32791          */
32792         hideAll : function(){
32793             for(var id in list){
32794                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32795                     list[id].hide();
32796                 }
32797             }
32798         }
32799     };
32800 }();
32801
32802 /**
32803  * @class Roo.LayoutDialog
32804  * @extends Roo.BasicDialog
32805  * Dialog which provides adjustments for working with a layout in a Dialog.
32806  * Add your necessary layout config options to the dialog's config.<br>
32807  * Example usage (including a nested layout):
32808  * <pre><code>
32809 if(!dialog){
32810     dialog = new Roo.LayoutDialog("download-dlg", {
32811         modal: true,
32812         width:600,
32813         height:450,
32814         shadow:true,
32815         minWidth:500,
32816         minHeight:350,
32817         autoTabs:true,
32818         proxyDrag:true,
32819         // layout config merges with the dialog config
32820         center:{
32821             tabPosition: "top",
32822             alwaysShowTabs: true
32823         }
32824     });
32825     dialog.addKeyListener(27, dialog.hide, dialog);
32826     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32827     dialog.addButton("Build It!", this.getDownload, this);
32828
32829     // we can even add nested layouts
32830     var innerLayout = new Roo.BorderLayout("dl-inner", {
32831         east: {
32832             initialSize: 200,
32833             autoScroll:true,
32834             split:true
32835         },
32836         center: {
32837             autoScroll:true
32838         }
32839     });
32840     innerLayout.beginUpdate();
32841     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32842     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32843     innerLayout.endUpdate(true);
32844
32845     var layout = dialog.getLayout();
32846     layout.beginUpdate();
32847     layout.add("center", new Roo.ContentPanel("standard-panel",
32848                         {title: "Download the Source", fitToFrame:true}));
32849     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32850                {title: "Build your own roo.js"}));
32851     layout.getRegion("center").showPanel(sp);
32852     layout.endUpdate();
32853 }
32854 </code></pre>
32855     * @constructor
32856     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32857     * @param {Object} config configuration options
32858   */
32859 Roo.LayoutDialog = function(el, cfg){
32860     
32861     var config=  cfg;
32862     if (typeof(cfg) == 'undefined') {
32863         config = Roo.apply({}, el);
32864         // not sure why we use documentElement here.. - it should always be body.
32865         // IE7 borks horribly if we use documentElement.
32866         // webkit also does not like documentElement - it creates a body element...
32867         el = Roo.get( document.body || document.documentElement ).createChild();
32868         //config.autoCreate = true;
32869     }
32870     
32871     
32872     config.autoTabs = false;
32873     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32874     this.body.setStyle({overflow:"hidden", position:"relative"});
32875     this.layout = new Roo.BorderLayout(this.body.dom, config);
32876     this.layout.monitorWindowResize = false;
32877     this.el.addClass("x-dlg-auto-layout");
32878     // fix case when center region overwrites center function
32879     this.center = Roo.BasicDialog.prototype.center;
32880     this.on("show", this.layout.layout, this.layout, true);
32881     if (config.items) {
32882         var xitems = config.items;
32883         delete config.items;
32884         Roo.each(xitems, this.addxtype, this);
32885     }
32886     
32887     
32888 };
32889 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32890     /**
32891      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32892      * @deprecated
32893      */
32894     endUpdate : function(){
32895         this.layout.endUpdate();
32896     },
32897
32898     /**
32899      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32900      *  @deprecated
32901      */
32902     beginUpdate : function(){
32903         this.layout.beginUpdate();
32904     },
32905
32906     /**
32907      * Get the BorderLayout for this dialog
32908      * @return {Roo.BorderLayout}
32909      */
32910     getLayout : function(){
32911         return this.layout;
32912     },
32913
32914     showEl : function(){
32915         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32916         if(Roo.isIE7){
32917             this.layout.layout();
32918         }
32919     },
32920
32921     // private
32922     // Use the syncHeightBeforeShow config option to control this automatically
32923     syncBodyHeight : function(){
32924         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32925         if(this.layout){this.layout.layout();}
32926     },
32927     
32928       /**
32929      * Add an xtype element (actually adds to the layout.)
32930      * @return {Object} xdata xtype object data.
32931      */
32932     
32933     addxtype : function(c) {
32934         return this.layout.addxtype(c);
32935     }
32936 });/*
32937  * Based on:
32938  * Ext JS Library 1.1.1
32939  * Copyright(c) 2006-2007, Ext JS, LLC.
32940  *
32941  * Originally Released Under LGPL - original licence link has changed is not relivant.
32942  *
32943  * Fork - LGPL
32944  * <script type="text/javascript">
32945  */
32946  
32947 /**
32948  * @class Roo.MessageBox
32949  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32950  * Example usage:
32951  *<pre><code>
32952 // Basic alert:
32953 Roo.Msg.alert('Status', 'Changes saved successfully.');
32954
32955 // Prompt for user data:
32956 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32957     if (btn == 'ok'){
32958         // process text value...
32959     }
32960 });
32961
32962 // Show a dialog using config options:
32963 Roo.Msg.show({
32964    title:'Save Changes?',
32965    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32966    buttons: Roo.Msg.YESNOCANCEL,
32967    fn: processResult,
32968    animEl: 'elId'
32969 });
32970 </code></pre>
32971  * @singleton
32972  */
32973 Roo.MessageBox = function(){
32974     var dlg, opt, mask, waitTimer;
32975     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32976     var buttons, activeTextEl, bwidth;
32977
32978     // private
32979     var handleButton = function(button){
32980         dlg.hide();
32981         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32982     };
32983
32984     // private
32985     var handleHide = function(){
32986         if(opt && opt.cls){
32987             dlg.el.removeClass(opt.cls);
32988         }
32989         if(waitTimer){
32990             Roo.TaskMgr.stop(waitTimer);
32991             waitTimer = null;
32992         }
32993     };
32994
32995     // private
32996     var updateButtons = function(b){
32997         var width = 0;
32998         if(!b){
32999             buttons["ok"].hide();
33000             buttons["cancel"].hide();
33001             buttons["yes"].hide();
33002             buttons["no"].hide();
33003             dlg.footer.dom.style.display = 'none';
33004             return width;
33005         }
33006         dlg.footer.dom.style.display = '';
33007         for(var k in buttons){
33008             if(typeof buttons[k] != "function"){
33009                 if(b[k]){
33010                     buttons[k].show();
33011                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33012                     width += buttons[k].el.getWidth()+15;
33013                 }else{
33014                     buttons[k].hide();
33015                 }
33016             }
33017         }
33018         return width;
33019     };
33020
33021     // private
33022     var handleEsc = function(d, k, e){
33023         if(opt && opt.closable !== false){
33024             dlg.hide();
33025         }
33026         if(e){
33027             e.stopEvent();
33028         }
33029     };
33030
33031     return {
33032         /**
33033          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33034          * @return {Roo.BasicDialog} The BasicDialog element
33035          */
33036         getDialog : function(){
33037            if(!dlg){
33038                 dlg = new Roo.BasicDialog("x-msg-box", {
33039                     autoCreate : true,
33040                     shadow: true,
33041                     draggable: true,
33042                     resizable:false,
33043                     constraintoviewport:false,
33044                     fixedcenter:true,
33045                     collapsible : false,
33046                     shim:true,
33047                     modal: true,
33048                     width:400, height:100,
33049                     buttonAlign:"center",
33050                     closeClick : function(){
33051                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33052                             handleButton("no");
33053                         }else{
33054                             handleButton("cancel");
33055                         }
33056                     }
33057                 });
33058                 dlg.on("hide", handleHide);
33059                 mask = dlg.mask;
33060                 dlg.addKeyListener(27, handleEsc);
33061                 buttons = {};
33062                 var bt = this.buttonText;
33063                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33064                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33065                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33066                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33067                 bodyEl = dlg.body.createChild({
33068
33069                     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>'
33070                 });
33071                 msgEl = bodyEl.dom.firstChild;
33072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33073                 textboxEl.enableDisplayMode();
33074                 textboxEl.addKeyListener([10,13], function(){
33075                     if(dlg.isVisible() && opt && opt.buttons){
33076                         if(opt.buttons.ok){
33077                             handleButton("ok");
33078                         }else if(opt.buttons.yes){
33079                             handleButton("yes");
33080                         }
33081                     }
33082                 });
33083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33084                 textareaEl.enableDisplayMode();
33085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33086                 progressEl.enableDisplayMode();
33087                 var pf = progressEl.dom.firstChild;
33088                 if (pf) {
33089                     pp = Roo.get(pf.firstChild);
33090                     pp.setHeight(pf.offsetHeight);
33091                 }
33092                 
33093             }
33094             return dlg;
33095         },
33096
33097         /**
33098          * Updates the message box body text
33099          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33100          * the XHTML-compliant non-breaking space character '&amp;#160;')
33101          * @return {Roo.MessageBox} This message box
33102          */
33103         updateText : function(text){
33104             if(!dlg.isVisible() && !opt.width){
33105                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33106             }
33107             msgEl.innerHTML = text || '&#160;';
33108       
33109             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33110             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33111             var w = Math.max(
33112                     Math.min(opt.width || cw , this.maxWidth), 
33113                     Math.max(opt.minWidth || this.minWidth, bwidth)
33114             );
33115             if(opt.prompt){
33116                 activeTextEl.setWidth(w);
33117             }
33118             if(dlg.isVisible()){
33119                 dlg.fixedcenter = false;
33120             }
33121             // to big, make it scroll. = But as usual stupid IE does not support
33122             // !important..
33123             
33124             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33125                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33126                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33127             } else {
33128                 bodyEl.dom.style.height = '';
33129                 bodyEl.dom.style.overflowY = '';
33130             }
33131             if (cw > w) {
33132                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33133             } else {
33134                 bodyEl.dom.style.overflowX = '';
33135             }
33136             
33137             dlg.setContentSize(w, bodyEl.getHeight());
33138             if(dlg.isVisible()){
33139                 dlg.fixedcenter = true;
33140             }
33141             return this;
33142         },
33143
33144         /**
33145          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33146          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33147          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33148          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33149          * @return {Roo.MessageBox} This message box
33150          */
33151         updateProgress : function(value, text){
33152             if(text){
33153                 this.updateText(text);
33154             }
33155             if (pp) { // weird bug on my firefox - for some reason this is not defined
33156                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33157             }
33158             return this;
33159         },        
33160
33161         /**
33162          * Returns true if the message box is currently displayed
33163          * @return {Boolean} True if the message box is visible, else false
33164          */
33165         isVisible : function(){
33166             return dlg && dlg.isVisible();  
33167         },
33168
33169         /**
33170          * Hides the message box if it is displayed
33171          */
33172         hide : function(){
33173             if(this.isVisible()){
33174                 dlg.hide();
33175             }  
33176         },
33177
33178         /**
33179          * Displays a new message box, or reinitializes an existing message box, based on the config options
33180          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33181          * The following config object properties are supported:
33182          * <pre>
33183 Property    Type             Description
33184 ----------  ---------------  ------------------------------------------------------------------------------------
33185 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33186                                    closes (defaults to undefined)
33187 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33188                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33189 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33190                                    progress and wait dialogs will ignore this property and always hide the
33191                                    close button as they can only be closed programmatically.
33192 cls               String           A custom CSS class to apply to the message box element
33193 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33194                                    displayed (defaults to 75)
33195 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33196                                    function will be btn (the name of the button that was clicked, if applicable,
33197                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33198                                    Progress and wait dialogs will ignore this option since they do not respond to
33199                                    user actions and can only be closed programmatically, so any required function
33200                                    should be called by the same code after it closes the dialog.
33201 icon              String           A CSS class that provides a background image to be used as an icon for
33202                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33203 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33204 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33205 modal             Boolean          False to allow user interaction with the page while the message box is
33206                                    displayed (defaults to true)
33207 msg               String           A string that will replace the existing message box body text (defaults
33208                                    to the XHTML-compliant non-breaking space character '&#160;')
33209 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33210 progress          Boolean          True to display a progress bar (defaults to false)
33211 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33212 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33213 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33214 title             String           The title text
33215 value             String           The string value to set into the active textbox element if displayed
33216 wait              Boolean          True to display a progress bar (defaults to false)
33217 width             Number           The width of the dialog in pixels
33218 </pre>
33219          *
33220          * Example usage:
33221          * <pre><code>
33222 Roo.Msg.show({
33223    title: 'Address',
33224    msg: 'Please enter your address:',
33225    width: 300,
33226    buttons: Roo.MessageBox.OKCANCEL,
33227    multiline: true,
33228    fn: saveAddress,
33229    animEl: 'addAddressBtn'
33230 });
33231 </code></pre>
33232          * @param {Object} config Configuration options
33233          * @return {Roo.MessageBox} This message box
33234          */
33235         show : function(options)
33236         {
33237             
33238             // this causes nightmares if you show one dialog after another
33239             // especially on callbacks..
33240              
33241             if(this.isVisible()){
33242                 
33243                 this.hide();
33244                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33245                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33246                 Roo.log("New Dialog Message:" +  options.msg )
33247                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33248                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33249                 
33250             }
33251             var d = this.getDialog();
33252             opt = options;
33253             d.setTitle(opt.title || "&#160;");
33254             d.close.setDisplayed(opt.closable !== false);
33255             activeTextEl = textboxEl;
33256             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33257             if(opt.prompt){
33258                 if(opt.multiline){
33259                     textboxEl.hide();
33260                     textareaEl.show();
33261                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33262                         opt.multiline : this.defaultTextHeight);
33263                     activeTextEl = textareaEl;
33264                 }else{
33265                     textboxEl.show();
33266                     textareaEl.hide();
33267                 }
33268             }else{
33269                 textboxEl.hide();
33270                 textareaEl.hide();
33271             }
33272             progressEl.setDisplayed(opt.progress === true);
33273             this.updateProgress(0);
33274             activeTextEl.dom.value = opt.value || "";
33275             if(opt.prompt){
33276                 dlg.setDefaultButton(activeTextEl);
33277             }else{
33278                 var bs = opt.buttons;
33279                 var db = null;
33280                 if(bs && bs.ok){
33281                     db = buttons["ok"];
33282                 }else if(bs && bs.yes){
33283                     db = buttons["yes"];
33284                 }
33285                 dlg.setDefaultButton(db);
33286             }
33287             bwidth = updateButtons(opt.buttons);
33288             this.updateText(opt.msg);
33289             if(opt.cls){
33290                 d.el.addClass(opt.cls);
33291             }
33292             d.proxyDrag = opt.proxyDrag === true;
33293             d.modal = opt.modal !== false;
33294             d.mask = opt.modal !== false ? mask : false;
33295             if(!d.isVisible()){
33296                 // force it to the end of the z-index stack so it gets a cursor in FF
33297                 document.body.appendChild(dlg.el.dom);
33298                 d.animateTarget = null;
33299                 d.show(options.animEl);
33300             }
33301             return this;
33302         },
33303
33304         /**
33305          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33306          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33307          * and closing the message box when the process is complete.
33308          * @param {String} title The title bar text
33309          * @param {String} msg The message box body text
33310          * @return {Roo.MessageBox} This message box
33311          */
33312         progress : function(title, msg){
33313             this.show({
33314                 title : title,
33315                 msg : msg,
33316                 buttons: false,
33317                 progress:true,
33318                 closable:false,
33319                 minWidth: this.minProgressWidth,
33320                 modal : true
33321             });
33322             return this;
33323         },
33324
33325         /**
33326          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33327          * If a callback function is passed it will be called after the user clicks the button, and the
33328          * id of the button that was clicked will be passed as the only parameter to the callback
33329          * (could also be the top-right close button).
33330          * @param {String} title The title bar text
33331          * @param {String} msg The message box body text
33332          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33333          * @param {Object} scope (optional) The scope of the callback function
33334          * @return {Roo.MessageBox} This message box
33335          */
33336         alert : function(title, msg, fn, scope){
33337             this.show({
33338                 title : title,
33339                 msg : msg,
33340                 buttons: this.OK,
33341                 fn: fn,
33342                 scope : scope,
33343                 modal : true
33344             });
33345             return this;
33346         },
33347
33348         /**
33349          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33350          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33351          * You are responsible for closing the message box when the process is complete.
33352          * @param {String} msg The message box body text
33353          * @param {String} title (optional) The title bar text
33354          * @return {Roo.MessageBox} This message box
33355          */
33356         wait : function(msg, title){
33357             this.show({
33358                 title : title,
33359                 msg : msg,
33360                 buttons: false,
33361                 closable:false,
33362                 progress:true,
33363                 modal:true,
33364                 width:300,
33365                 wait:true
33366             });
33367             waitTimer = Roo.TaskMgr.start({
33368                 run: function(i){
33369                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33370                 },
33371                 interval: 1000
33372             });
33373             return this;
33374         },
33375
33376         /**
33377          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33378          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33379          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33380          * @param {String} title The title bar text
33381          * @param {String} msg The message box body text
33382          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33383          * @param {Object} scope (optional) The scope of the callback function
33384          * @return {Roo.MessageBox} This message box
33385          */
33386         confirm : function(title, msg, fn, scope){
33387             this.show({
33388                 title : title,
33389                 msg : msg,
33390                 buttons: this.YESNO,
33391                 fn: fn,
33392                 scope : scope,
33393                 modal : true
33394             });
33395             return this;
33396         },
33397
33398         /**
33399          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33400          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33401          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33402          * (could also be the top-right close button) and the text that was entered will be passed as the two
33403          * parameters to the callback.
33404          * @param {String} title The title bar text
33405          * @param {String} msg The message box body text
33406          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33407          * @param {Object} scope (optional) The scope of the callback function
33408          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33409          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33410          * @return {Roo.MessageBox} This message box
33411          */
33412         prompt : function(title, msg, fn, scope, multiline){
33413             this.show({
33414                 title : title,
33415                 msg : msg,
33416                 buttons: this.OKCANCEL,
33417                 fn: fn,
33418                 minWidth:250,
33419                 scope : scope,
33420                 prompt:true,
33421                 multiline: multiline,
33422                 modal : true
33423             });
33424             return this;
33425         },
33426
33427         /**
33428          * Button config that displays a single OK button
33429          * @type Object
33430          */
33431         OK : {ok:true},
33432         /**
33433          * Button config that displays Yes and No buttons
33434          * @type Object
33435          */
33436         YESNO : {yes:true, no:true},
33437         /**
33438          * Button config that displays OK and Cancel buttons
33439          * @type Object
33440          */
33441         OKCANCEL : {ok:true, cancel:true},
33442         /**
33443          * Button config that displays Yes, No and Cancel buttons
33444          * @type Object
33445          */
33446         YESNOCANCEL : {yes:true, no:true, cancel:true},
33447
33448         /**
33449          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33450          * @type Number
33451          */
33452         defaultTextHeight : 75,
33453         /**
33454          * The maximum width in pixels of the message box (defaults to 600)
33455          * @type Number
33456          */
33457         maxWidth : 600,
33458         /**
33459          * The minimum width in pixels of the message box (defaults to 100)
33460          * @type Number
33461          */
33462         minWidth : 100,
33463         /**
33464          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33465          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33466          * @type Number
33467          */
33468         minProgressWidth : 250,
33469         /**
33470          * An object containing the default button text strings that can be overriden for localized language support.
33471          * Supported properties are: ok, cancel, yes and no.
33472          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33473          * @type Object
33474          */
33475         buttonText : {
33476             ok : "OK",
33477             cancel : "Cancel",
33478             yes : "Yes",
33479             no : "No"
33480         }
33481     };
33482 }();
33483
33484 /**
33485  * Shorthand for {@link Roo.MessageBox}
33486  */
33487 Roo.Msg = Roo.MessageBox;/*
33488  * Based on:
33489  * Ext JS Library 1.1.1
33490  * Copyright(c) 2006-2007, Ext JS, LLC.
33491  *
33492  * Originally Released Under LGPL - original licence link has changed is not relivant.
33493  *
33494  * Fork - LGPL
33495  * <script type="text/javascript">
33496  */
33497 /**
33498  * @class Roo.QuickTips
33499  * Provides attractive and customizable tooltips for any element.
33500  * @singleton
33501  */
33502 Roo.QuickTips = function(){
33503     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33504     var ce, bd, xy, dd;
33505     var visible = false, disabled = true, inited = false;
33506     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33507     
33508     var onOver = function(e){
33509         if(disabled){
33510             return;
33511         }
33512         var t = e.getTarget();
33513         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33514             return;
33515         }
33516         if(ce && t == ce.el){
33517             clearTimeout(hideProc);
33518             return;
33519         }
33520         if(t && tagEls[t.id]){
33521             tagEls[t.id].el = t;
33522             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33523             return;
33524         }
33525         var ttp, et = Roo.fly(t);
33526         var ns = cfg.namespace;
33527         if(tm.interceptTitles && t.title){
33528             ttp = t.title;
33529             t.qtip = ttp;
33530             t.removeAttribute("title");
33531             e.preventDefault();
33532         }else{
33533             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33534         }
33535         if(ttp){
33536             showProc = show.defer(tm.showDelay, tm, [{
33537                 el: t, 
33538                 text: ttp, 
33539                 width: et.getAttributeNS(ns, cfg.width),
33540                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33541                 title: et.getAttributeNS(ns, cfg.title),
33542                     cls: et.getAttributeNS(ns, cfg.cls)
33543             }]);
33544         }
33545     };
33546     
33547     var onOut = function(e){
33548         clearTimeout(showProc);
33549         var t = e.getTarget();
33550         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33551             hideProc = setTimeout(hide, tm.hideDelay);
33552         }
33553     };
33554     
33555     var onMove = function(e){
33556         if(disabled){
33557             return;
33558         }
33559         xy = e.getXY();
33560         xy[1] += 18;
33561         if(tm.trackMouse && ce){
33562             el.setXY(xy);
33563         }
33564     };
33565     
33566     var onDown = function(e){
33567         clearTimeout(showProc);
33568         clearTimeout(hideProc);
33569         if(!e.within(el)){
33570             if(tm.hideOnClick){
33571                 hide();
33572                 tm.disable();
33573                 tm.enable.defer(100, tm);
33574             }
33575         }
33576     };
33577     
33578     var getPad = function(){
33579         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33580     };
33581
33582     var show = function(o){
33583         if(disabled){
33584             return;
33585         }
33586         clearTimeout(dismissProc);
33587         ce = o;
33588         if(removeCls){ // in case manually hidden
33589             el.removeClass(removeCls);
33590             removeCls = null;
33591         }
33592         if(ce.cls){
33593             el.addClass(ce.cls);
33594             removeCls = ce.cls;
33595         }
33596         if(ce.title){
33597             tipTitle.update(ce.title);
33598             tipTitle.show();
33599         }else{
33600             tipTitle.update('');
33601             tipTitle.hide();
33602         }
33603         el.dom.style.width  = tm.maxWidth+'px';
33604         //tipBody.dom.style.width = '';
33605         tipBodyText.update(o.text);
33606         var p = getPad(), w = ce.width;
33607         if(!w){
33608             var td = tipBodyText.dom;
33609             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33610             if(aw > tm.maxWidth){
33611                 w = tm.maxWidth;
33612             }else if(aw < tm.minWidth){
33613                 w = tm.minWidth;
33614             }else{
33615                 w = aw;
33616             }
33617         }
33618         //tipBody.setWidth(w);
33619         el.setWidth(parseInt(w, 10) + p);
33620         if(ce.autoHide === false){
33621             close.setDisplayed(true);
33622             if(dd){
33623                 dd.unlock();
33624             }
33625         }else{
33626             close.setDisplayed(false);
33627             if(dd){
33628                 dd.lock();
33629             }
33630         }
33631         if(xy){
33632             el.avoidY = xy[1]-18;
33633             el.setXY(xy);
33634         }
33635         if(tm.animate){
33636             el.setOpacity(.1);
33637             el.setStyle("visibility", "visible");
33638             el.fadeIn({callback: afterShow});
33639         }else{
33640             afterShow();
33641         }
33642     };
33643     
33644     var afterShow = function(){
33645         if(ce){
33646             el.show();
33647             esc.enable();
33648             if(tm.autoDismiss && ce.autoHide !== false){
33649                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33650             }
33651         }
33652     };
33653     
33654     var hide = function(noanim){
33655         clearTimeout(dismissProc);
33656         clearTimeout(hideProc);
33657         ce = null;
33658         if(el.isVisible()){
33659             esc.disable();
33660             if(noanim !== true && tm.animate){
33661                 el.fadeOut({callback: afterHide});
33662             }else{
33663                 afterHide();
33664             } 
33665         }
33666     };
33667     
33668     var afterHide = function(){
33669         el.hide();
33670         if(removeCls){
33671             el.removeClass(removeCls);
33672             removeCls = null;
33673         }
33674     };
33675     
33676     return {
33677         /**
33678         * @cfg {Number} minWidth
33679         * The minimum width of the quick tip (defaults to 40)
33680         */
33681        minWidth : 40,
33682         /**
33683         * @cfg {Number} maxWidth
33684         * The maximum width of the quick tip (defaults to 300)
33685         */
33686        maxWidth : 300,
33687         /**
33688         * @cfg {Boolean} interceptTitles
33689         * True to automatically use the element's DOM title value if available (defaults to false)
33690         */
33691        interceptTitles : false,
33692         /**
33693         * @cfg {Boolean} trackMouse
33694         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33695         */
33696        trackMouse : false,
33697         /**
33698         * @cfg {Boolean} hideOnClick
33699         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33700         */
33701        hideOnClick : true,
33702         /**
33703         * @cfg {Number} showDelay
33704         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33705         */
33706        showDelay : 500,
33707         /**
33708         * @cfg {Number} hideDelay
33709         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33710         */
33711        hideDelay : 200,
33712         /**
33713         * @cfg {Boolean} autoHide
33714         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33715         * Used in conjunction with hideDelay.
33716         */
33717        autoHide : true,
33718         /**
33719         * @cfg {Boolean}
33720         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33721         * (defaults to true).  Used in conjunction with autoDismissDelay.
33722         */
33723        autoDismiss : true,
33724         /**
33725         * @cfg {Number}
33726         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33727         */
33728        autoDismissDelay : 5000,
33729        /**
33730         * @cfg {Boolean} animate
33731         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33732         */
33733        animate : false,
33734
33735        /**
33736         * @cfg {String} title
33737         * Title text to display (defaults to '').  This can be any valid HTML markup.
33738         */
33739         title: '',
33740        /**
33741         * @cfg {String} text
33742         * Body text to display (defaults to '').  This can be any valid HTML markup.
33743         */
33744         text : '',
33745        /**
33746         * @cfg {String} cls
33747         * A CSS class to apply to the base quick tip element (defaults to '').
33748         */
33749         cls : '',
33750        /**
33751         * @cfg {Number} width
33752         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33753         * minWidth or maxWidth.
33754         */
33755         width : null,
33756
33757     /**
33758      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33759      * or display QuickTips in a page.
33760      */
33761        init : function(){
33762           tm = Roo.QuickTips;
33763           cfg = tm.tagConfig;
33764           if(!inited){
33765               if(!Roo.isReady){ // allow calling of init() before onReady
33766                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33767                   return;
33768               }
33769               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33770               el.fxDefaults = {stopFx: true};
33771               // maximum custom styling
33772               //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>');
33773               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>');              
33774               tipTitle = el.child('h3');
33775               tipTitle.enableDisplayMode("block");
33776               tipBody = el.child('div.x-tip-bd');
33777               tipBodyText = el.child('div.x-tip-bd-inner');
33778               //bdLeft = el.child('div.x-tip-bd-left');
33779               //bdRight = el.child('div.x-tip-bd-right');
33780               close = el.child('div.x-tip-close');
33781               close.enableDisplayMode("block");
33782               close.on("click", hide);
33783               var d = Roo.get(document);
33784               d.on("mousedown", onDown);
33785               d.on("mouseover", onOver);
33786               d.on("mouseout", onOut);
33787               d.on("mousemove", onMove);
33788               esc = d.addKeyListener(27, hide);
33789               esc.disable();
33790               if(Roo.dd.DD){
33791                   dd = el.initDD("default", null, {
33792                       onDrag : function(){
33793                           el.sync();  
33794                       }
33795                   });
33796                   dd.setHandleElId(tipTitle.id);
33797                   dd.lock();
33798               }
33799               inited = true;
33800           }
33801           this.enable(); 
33802        },
33803
33804     /**
33805      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33806      * are supported:
33807      * <pre>
33808 Property    Type                   Description
33809 ----------  ---------------------  ------------------------------------------------------------------------
33810 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33811      * </ul>
33812      * @param {Object} config The config object
33813      */
33814        register : function(config){
33815            var cs = config instanceof Array ? config : arguments;
33816            for(var i = 0, len = cs.length; i < len; i++) {
33817                var c = cs[i];
33818                var target = c.target;
33819                if(target){
33820                    if(target instanceof Array){
33821                        for(var j = 0, jlen = target.length; j < jlen; j++){
33822                            tagEls[target[j]] = c;
33823                        }
33824                    }else{
33825                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33826                    }
33827                }
33828            }
33829        },
33830
33831     /**
33832      * Removes this quick tip from its element and destroys it.
33833      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33834      */
33835        unregister : function(el){
33836            delete tagEls[Roo.id(el)];
33837        },
33838
33839     /**
33840      * Enable this quick tip.
33841      */
33842        enable : function(){
33843            if(inited && disabled){
33844                locks.pop();
33845                if(locks.length < 1){
33846                    disabled = false;
33847                }
33848            }
33849        },
33850
33851     /**
33852      * Disable this quick tip.
33853      */
33854        disable : function(){
33855           disabled = true;
33856           clearTimeout(showProc);
33857           clearTimeout(hideProc);
33858           clearTimeout(dismissProc);
33859           if(ce){
33860               hide(true);
33861           }
33862           locks.push(1);
33863        },
33864
33865     /**
33866      * Returns true if the quick tip is enabled, else false.
33867      */
33868        isEnabled : function(){
33869             return !disabled;
33870        },
33871
33872         // private
33873        tagConfig : {
33874            namespace : "roo", // was ext?? this may break..
33875            alt_namespace : "ext",
33876            attribute : "qtip",
33877            width : "width",
33878            target : "target",
33879            title : "qtitle",
33880            hide : "hide",
33881            cls : "qclass"
33882        }
33883    };
33884 }();
33885
33886 // backwards compat
33887 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33888  * Based on:
33889  * Ext JS Library 1.1.1
33890  * Copyright(c) 2006-2007, Ext JS, LLC.
33891  *
33892  * Originally Released Under LGPL - original licence link has changed is not relivant.
33893  *
33894  * Fork - LGPL
33895  * <script type="text/javascript">
33896  */
33897  
33898
33899 /**
33900  * @class Roo.tree.TreePanel
33901  * @extends Roo.data.Tree
33902
33903  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33904  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33905  * @cfg {Boolean} enableDD true to enable drag and drop
33906  * @cfg {Boolean} enableDrag true to enable just drag
33907  * @cfg {Boolean} enableDrop true to enable just drop
33908  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33909  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33910  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33911  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33912  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33913  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33914  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33915  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33916  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33917  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33918  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33919  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33920  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33921  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33922  * @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>
33923  * @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>
33924  * 
33925  * @constructor
33926  * @param {String/HTMLElement/Element} el The container element
33927  * @param {Object} config
33928  */
33929 Roo.tree.TreePanel = function(el, config){
33930     var root = false;
33931     var loader = false;
33932     if (config.root) {
33933         root = config.root;
33934         delete config.root;
33935     }
33936     if (config.loader) {
33937         loader = config.loader;
33938         delete config.loader;
33939     }
33940     
33941     Roo.apply(this, config);
33942     Roo.tree.TreePanel.superclass.constructor.call(this);
33943     this.el = Roo.get(el);
33944     this.el.addClass('x-tree');
33945     //console.log(root);
33946     if (root) {
33947         this.setRootNode( Roo.factory(root, Roo.tree));
33948     }
33949     if (loader) {
33950         this.loader = Roo.factory(loader, Roo.tree);
33951     }
33952    /**
33953     * Read-only. The id of the container element becomes this TreePanel's id.
33954     */
33955     this.id = this.el.id;
33956     this.addEvents({
33957         /**
33958         * @event beforeload
33959         * Fires before a node is loaded, return false to cancel
33960         * @param {Node} node The node being loaded
33961         */
33962         "beforeload" : true,
33963         /**
33964         * @event load
33965         * Fires when a node is loaded
33966         * @param {Node} node The node that was loaded
33967         */
33968         "load" : true,
33969         /**
33970         * @event textchange
33971         * Fires when the text for a node is changed
33972         * @param {Node} node The node
33973         * @param {String} text The new text
33974         * @param {String} oldText The old text
33975         */
33976         "textchange" : true,
33977         /**
33978         * @event beforeexpand
33979         * Fires before a node is expanded, return false to cancel.
33980         * @param {Node} node The node
33981         * @param {Boolean} deep
33982         * @param {Boolean} anim
33983         */
33984         "beforeexpand" : true,
33985         /**
33986         * @event beforecollapse
33987         * Fires before a node is collapsed, return false to cancel.
33988         * @param {Node} node The node
33989         * @param {Boolean} deep
33990         * @param {Boolean} anim
33991         */
33992         "beforecollapse" : true,
33993         /**
33994         * @event expand
33995         * Fires when a node is expanded
33996         * @param {Node} node The node
33997         */
33998         "expand" : true,
33999         /**
34000         * @event disabledchange
34001         * Fires when the disabled status of a node changes
34002         * @param {Node} node The node
34003         * @param {Boolean} disabled
34004         */
34005         "disabledchange" : true,
34006         /**
34007         * @event collapse
34008         * Fires when a node is collapsed
34009         * @param {Node} node The node
34010         */
34011         "collapse" : true,
34012         /**
34013         * @event beforeclick
34014         * Fires before click processing on a node. Return false to cancel the default action.
34015         * @param {Node} node The node
34016         * @param {Roo.EventObject} e The event object
34017         */
34018         "beforeclick":true,
34019         /**
34020         * @event checkchange
34021         * Fires when a node with a checkbox's checked property changes
34022         * @param {Node} this This node
34023         * @param {Boolean} checked
34024         */
34025         "checkchange":true,
34026         /**
34027         * @event click
34028         * Fires when a node is clicked
34029         * @param {Node} node The node
34030         * @param {Roo.EventObject} e The event object
34031         */
34032         "click":true,
34033         /**
34034         * @event dblclick
34035         * Fires when a node is double clicked
34036         * @param {Node} node The node
34037         * @param {Roo.EventObject} e The event object
34038         */
34039         "dblclick":true,
34040         /**
34041         * @event contextmenu
34042         * Fires when a node is right clicked
34043         * @param {Node} node The node
34044         * @param {Roo.EventObject} e The event object
34045         */
34046         "contextmenu":true,
34047         /**
34048         * @event beforechildrenrendered
34049         * Fires right before the child nodes for a node are rendered
34050         * @param {Node} node The node
34051         */
34052         "beforechildrenrendered":true,
34053         /**
34054         * @event startdrag
34055         * Fires when a node starts being dragged
34056         * @param {Roo.tree.TreePanel} this
34057         * @param {Roo.tree.TreeNode} node
34058         * @param {event} e The raw browser event
34059         */ 
34060        "startdrag" : true,
34061        /**
34062         * @event enddrag
34063         * Fires when a drag operation is complete
34064         * @param {Roo.tree.TreePanel} this
34065         * @param {Roo.tree.TreeNode} node
34066         * @param {event} e The raw browser event
34067         */
34068        "enddrag" : true,
34069        /**
34070         * @event dragdrop
34071         * Fires when a dragged node is dropped on a valid DD target
34072         * @param {Roo.tree.TreePanel} this
34073         * @param {Roo.tree.TreeNode} node
34074         * @param {DD} dd The dd it was dropped on
34075         * @param {event} e The raw browser event
34076         */
34077        "dragdrop" : true,
34078        /**
34079         * @event beforenodedrop
34080         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34081         * passed to handlers has the following properties:<br />
34082         * <ul style="padding:5px;padding-left:16px;">
34083         * <li>tree - The TreePanel</li>
34084         * <li>target - The node being targeted for the drop</li>
34085         * <li>data - The drag data from the drag source</li>
34086         * <li>point - The point of the drop - append, above or below</li>
34087         * <li>source - The drag source</li>
34088         * <li>rawEvent - Raw mouse event</li>
34089         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34090         * to be inserted by setting them on this object.</li>
34091         * <li>cancel - Set this to true to cancel the drop.</li>
34092         * </ul>
34093         * @param {Object} dropEvent
34094         */
34095        "beforenodedrop" : true,
34096        /**
34097         * @event nodedrop
34098         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34099         * passed to handlers has the following properties:<br />
34100         * <ul style="padding:5px;padding-left:16px;">
34101         * <li>tree - The TreePanel</li>
34102         * <li>target - The node being targeted for the drop</li>
34103         * <li>data - The drag data from the drag source</li>
34104         * <li>point - The point of the drop - append, above or below</li>
34105         * <li>source - The drag source</li>
34106         * <li>rawEvent - Raw mouse event</li>
34107         * <li>dropNode - Dropped node(s).</li>
34108         * </ul>
34109         * @param {Object} dropEvent
34110         */
34111        "nodedrop" : true,
34112         /**
34113         * @event nodedragover
34114         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34115         * passed to handlers has the following properties:<br />
34116         * <ul style="padding:5px;padding-left:16px;">
34117         * <li>tree - The TreePanel</li>
34118         * <li>target - The node being targeted for the drop</li>
34119         * <li>data - The drag data from the drag source</li>
34120         * <li>point - The point of the drop - append, above or below</li>
34121         * <li>source - The drag source</li>
34122         * <li>rawEvent - Raw mouse event</li>
34123         * <li>dropNode - Drop node(s) provided by the source.</li>
34124         * <li>cancel - Set this to true to signal drop not allowed.</li>
34125         * </ul>
34126         * @param {Object} dragOverEvent
34127         */
34128        "nodedragover" : true
34129         
34130     });
34131     if(this.singleExpand){
34132        this.on("beforeexpand", this.restrictExpand, this);
34133     }
34134     if (this.editor) {
34135         this.editor.tree = this;
34136         this.editor = Roo.factory(this.editor, Roo.tree);
34137     }
34138     
34139     if (this.selModel) {
34140         this.selModel = Roo.factory(this.selModel, Roo.tree);
34141     }
34142    
34143 };
34144 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34145     rootVisible : true,
34146     animate: Roo.enableFx,
34147     lines : true,
34148     enableDD : false,
34149     hlDrop : Roo.enableFx,
34150   
34151     renderer: false,
34152     
34153     rendererTip: false,
34154     // private
34155     restrictExpand : function(node){
34156         var p = node.parentNode;
34157         if(p){
34158             if(p.expandedChild && p.expandedChild.parentNode == p){
34159                 p.expandedChild.collapse();
34160             }
34161             p.expandedChild = node;
34162         }
34163     },
34164
34165     // private override
34166     setRootNode : function(node){
34167         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34168         if(!this.rootVisible){
34169             node.ui = new Roo.tree.RootTreeNodeUI(node);
34170         }
34171         return node;
34172     },
34173
34174     /**
34175      * Returns the container element for this TreePanel
34176      */
34177     getEl : function(){
34178         return this.el;
34179     },
34180
34181     /**
34182      * Returns the default TreeLoader for this TreePanel
34183      */
34184     getLoader : function(){
34185         return this.loader;
34186     },
34187
34188     /**
34189      * Expand all nodes
34190      */
34191     expandAll : function(){
34192         this.root.expand(true);
34193     },
34194
34195     /**
34196      * Collapse all nodes
34197      */
34198     collapseAll : function(){
34199         this.root.collapse(true);
34200     },
34201
34202     /**
34203      * Returns the selection model used by this TreePanel
34204      */
34205     getSelectionModel : function(){
34206         if(!this.selModel){
34207             this.selModel = new Roo.tree.DefaultSelectionModel();
34208         }
34209         return this.selModel;
34210     },
34211
34212     /**
34213      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34214      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34215      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34216      * @return {Array}
34217      */
34218     getChecked : function(a, startNode){
34219         startNode = startNode || this.root;
34220         var r = [];
34221         var f = function(){
34222             if(this.attributes.checked){
34223                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34224             }
34225         }
34226         startNode.cascade(f);
34227         return r;
34228     },
34229
34230     /**
34231      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34232      * @param {String} path
34233      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34234      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34235      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34236      */
34237     expandPath : function(path, attr, callback){
34238         attr = attr || "id";
34239         var keys = path.split(this.pathSeparator);
34240         var curNode = this.root;
34241         if(curNode.attributes[attr] != keys[1]){ // invalid root
34242             if(callback){
34243                 callback(false, null);
34244             }
34245             return;
34246         }
34247         var index = 1;
34248         var f = function(){
34249             if(++index == keys.length){
34250                 if(callback){
34251                     callback(true, curNode);
34252                 }
34253                 return;
34254             }
34255             var c = curNode.findChild(attr, keys[index]);
34256             if(!c){
34257                 if(callback){
34258                     callback(false, curNode);
34259                 }
34260                 return;
34261             }
34262             curNode = c;
34263             c.expand(false, false, f);
34264         };
34265         curNode.expand(false, false, f);
34266     },
34267
34268     /**
34269      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34270      * @param {String} path
34271      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34272      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34273      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34274      */
34275     selectPath : function(path, attr, callback){
34276         attr = attr || "id";
34277         var keys = path.split(this.pathSeparator);
34278         var v = keys.pop();
34279         if(keys.length > 0){
34280             var f = function(success, node){
34281                 if(success && node){
34282                     var n = node.findChild(attr, v);
34283                     if(n){
34284                         n.select();
34285                         if(callback){
34286                             callback(true, n);
34287                         }
34288                     }else if(callback){
34289                         callback(false, n);
34290                     }
34291                 }else{
34292                     if(callback){
34293                         callback(false, n);
34294                     }
34295                 }
34296             };
34297             this.expandPath(keys.join(this.pathSeparator), attr, f);
34298         }else{
34299             this.root.select();
34300             if(callback){
34301                 callback(true, this.root);
34302             }
34303         }
34304     },
34305
34306     getTreeEl : function(){
34307         return this.el;
34308     },
34309
34310     /**
34311      * Trigger rendering of this TreePanel
34312      */
34313     render : function(){
34314         if (this.innerCt) {
34315             return this; // stop it rendering more than once!!
34316         }
34317         
34318         this.innerCt = this.el.createChild({tag:"ul",
34319                cls:"x-tree-root-ct " +
34320                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34321
34322         if(this.containerScroll){
34323             Roo.dd.ScrollManager.register(this.el);
34324         }
34325         if((this.enableDD || this.enableDrop) && !this.dropZone){
34326            /**
34327             * The dropZone used by this tree if drop is enabled
34328             * @type Roo.tree.TreeDropZone
34329             */
34330              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34331                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34332            });
34333         }
34334         if((this.enableDD || this.enableDrag) && !this.dragZone){
34335            /**
34336             * The dragZone used by this tree if drag is enabled
34337             * @type Roo.tree.TreeDragZone
34338             */
34339             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34340                ddGroup: this.ddGroup || "TreeDD",
34341                scroll: this.ddScroll
34342            });
34343         }
34344         this.getSelectionModel().init(this);
34345         if (!this.root) {
34346             Roo.log("ROOT not set in tree");
34347             return this;
34348         }
34349         this.root.render();
34350         if(!this.rootVisible){
34351             this.root.renderChildren();
34352         }
34353         return this;
34354     }
34355 });/*
34356  * Based on:
34357  * Ext JS Library 1.1.1
34358  * Copyright(c) 2006-2007, Ext JS, LLC.
34359  *
34360  * Originally Released Under LGPL - original licence link has changed is not relivant.
34361  *
34362  * Fork - LGPL
34363  * <script type="text/javascript">
34364  */
34365  
34366
34367 /**
34368  * @class Roo.tree.DefaultSelectionModel
34369  * @extends Roo.util.Observable
34370  * The default single selection for a TreePanel.
34371  * @param {Object} cfg Configuration
34372  */
34373 Roo.tree.DefaultSelectionModel = function(cfg){
34374    this.selNode = null;
34375    
34376    
34377    
34378    this.addEvents({
34379        /**
34380         * @event selectionchange
34381         * Fires when the selected node changes
34382         * @param {DefaultSelectionModel} this
34383         * @param {TreeNode} node the new selection
34384         */
34385        "selectionchange" : true,
34386
34387        /**
34388         * @event beforeselect
34389         * Fires before the selected node changes, return false to cancel the change
34390         * @param {DefaultSelectionModel} this
34391         * @param {TreeNode} node the new selection
34392         * @param {TreeNode} node the old selection
34393         */
34394        "beforeselect" : true
34395    });
34396    
34397     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34398 };
34399
34400 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34401     init : function(tree){
34402         this.tree = tree;
34403         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34404         tree.on("click", this.onNodeClick, this);
34405     },
34406     
34407     onNodeClick : function(node, e){
34408         if (e.ctrlKey && this.selNode == node)  {
34409             this.unselect(node);
34410             return;
34411         }
34412         this.select(node);
34413     },
34414     
34415     /**
34416      * Select a node.
34417      * @param {TreeNode} node The node to select
34418      * @return {TreeNode} The selected node
34419      */
34420     select : function(node){
34421         var last = this.selNode;
34422         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34423             if(last){
34424                 last.ui.onSelectedChange(false);
34425             }
34426             this.selNode = node;
34427             node.ui.onSelectedChange(true);
34428             this.fireEvent("selectionchange", this, node, last);
34429         }
34430         return node;
34431     },
34432     
34433     /**
34434      * Deselect a node.
34435      * @param {TreeNode} node The node to unselect
34436      */
34437     unselect : function(node){
34438         if(this.selNode == node){
34439             this.clearSelections();
34440         }    
34441     },
34442     
34443     /**
34444      * Clear all selections
34445      */
34446     clearSelections : function(){
34447         var n = this.selNode;
34448         if(n){
34449             n.ui.onSelectedChange(false);
34450             this.selNode = null;
34451             this.fireEvent("selectionchange", this, null);
34452         }
34453         return n;
34454     },
34455     
34456     /**
34457      * Get the selected node
34458      * @return {TreeNode} The selected node
34459      */
34460     getSelectedNode : function(){
34461         return this.selNode;    
34462     },
34463     
34464     /**
34465      * Returns true if the node is selected
34466      * @param {TreeNode} node The node to check
34467      * @return {Boolean}
34468      */
34469     isSelected : function(node){
34470         return this.selNode == node;  
34471     },
34472
34473     /**
34474      * Selects the node above the selected node in the tree, intelligently walking the nodes
34475      * @return TreeNode The new selection
34476      */
34477     selectPrevious : function(){
34478         var s = this.selNode || this.lastSelNode;
34479         if(!s){
34480             return null;
34481         }
34482         var ps = s.previousSibling;
34483         if(ps){
34484             if(!ps.isExpanded() || ps.childNodes.length < 1){
34485                 return this.select(ps);
34486             } else{
34487                 var lc = ps.lastChild;
34488                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34489                     lc = lc.lastChild;
34490                 }
34491                 return this.select(lc);
34492             }
34493         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34494             return this.select(s.parentNode);
34495         }
34496         return null;
34497     },
34498
34499     /**
34500      * Selects the node above the selected node in the tree, intelligently walking the nodes
34501      * @return TreeNode The new selection
34502      */
34503     selectNext : function(){
34504         var s = this.selNode || this.lastSelNode;
34505         if(!s){
34506             return null;
34507         }
34508         if(s.firstChild && s.isExpanded()){
34509              return this.select(s.firstChild);
34510          }else if(s.nextSibling){
34511              return this.select(s.nextSibling);
34512          }else if(s.parentNode){
34513             var newS = null;
34514             s.parentNode.bubble(function(){
34515                 if(this.nextSibling){
34516                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34517                     return false;
34518                 }
34519             });
34520             return newS;
34521          }
34522         return null;
34523     },
34524
34525     onKeyDown : function(e){
34526         var s = this.selNode || this.lastSelNode;
34527         // undesirable, but required
34528         var sm = this;
34529         if(!s){
34530             return;
34531         }
34532         var k = e.getKey();
34533         switch(k){
34534              case e.DOWN:
34535                  e.stopEvent();
34536                  this.selectNext();
34537              break;
34538              case e.UP:
34539                  e.stopEvent();
34540                  this.selectPrevious();
34541              break;
34542              case e.RIGHT:
34543                  e.preventDefault();
34544                  if(s.hasChildNodes()){
34545                      if(!s.isExpanded()){
34546                          s.expand();
34547                      }else if(s.firstChild){
34548                          this.select(s.firstChild, e);
34549                      }
34550                  }
34551              break;
34552              case e.LEFT:
34553                  e.preventDefault();
34554                  if(s.hasChildNodes() && s.isExpanded()){
34555                      s.collapse();
34556                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34557                      this.select(s.parentNode, e);
34558                  }
34559              break;
34560         };
34561     }
34562 });
34563
34564 /**
34565  * @class Roo.tree.MultiSelectionModel
34566  * @extends Roo.util.Observable
34567  * Multi selection for a TreePanel.
34568  * @param {Object} cfg Configuration
34569  */
34570 Roo.tree.MultiSelectionModel = function(){
34571    this.selNodes = [];
34572    this.selMap = {};
34573    this.addEvents({
34574        /**
34575         * @event selectionchange
34576         * Fires when the selected nodes change
34577         * @param {MultiSelectionModel} this
34578         * @param {Array} nodes Array of the selected nodes
34579         */
34580        "selectionchange" : true
34581    });
34582    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34583    
34584 };
34585
34586 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34587     init : function(tree){
34588         this.tree = tree;
34589         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34590         tree.on("click", this.onNodeClick, this);
34591     },
34592     
34593     onNodeClick : function(node, e){
34594         this.select(node, e, e.ctrlKey);
34595     },
34596     
34597     /**
34598      * Select a node.
34599      * @param {TreeNode} node The node to select
34600      * @param {EventObject} e (optional) An event associated with the selection
34601      * @param {Boolean} keepExisting True to retain existing selections
34602      * @return {TreeNode} The selected node
34603      */
34604     select : function(node, e, keepExisting){
34605         if(keepExisting !== true){
34606             this.clearSelections(true);
34607         }
34608         if(this.isSelected(node)){
34609             this.lastSelNode = node;
34610             return node;
34611         }
34612         this.selNodes.push(node);
34613         this.selMap[node.id] = node;
34614         this.lastSelNode = node;
34615         node.ui.onSelectedChange(true);
34616         this.fireEvent("selectionchange", this, this.selNodes);
34617         return node;
34618     },
34619     
34620     /**
34621      * Deselect a node.
34622      * @param {TreeNode} node The node to unselect
34623      */
34624     unselect : function(node){
34625         if(this.selMap[node.id]){
34626             node.ui.onSelectedChange(false);
34627             var sn = this.selNodes;
34628             var index = -1;
34629             if(sn.indexOf){
34630                 index = sn.indexOf(node);
34631             }else{
34632                 for(var i = 0, len = sn.length; i < len; i++){
34633                     if(sn[i] == node){
34634                         index = i;
34635                         break;
34636                     }
34637                 }
34638             }
34639             if(index != -1){
34640                 this.selNodes.splice(index, 1);
34641             }
34642             delete this.selMap[node.id];
34643             this.fireEvent("selectionchange", this, this.selNodes);
34644         }
34645     },
34646     
34647     /**
34648      * Clear all selections
34649      */
34650     clearSelections : function(suppressEvent){
34651         var sn = this.selNodes;
34652         if(sn.length > 0){
34653             for(var i = 0, len = sn.length; i < len; i++){
34654                 sn[i].ui.onSelectedChange(false);
34655             }
34656             this.selNodes = [];
34657             this.selMap = {};
34658             if(suppressEvent !== true){
34659                 this.fireEvent("selectionchange", this, this.selNodes);
34660             }
34661         }
34662     },
34663     
34664     /**
34665      * Returns true if the node is selected
34666      * @param {TreeNode} node The node to check
34667      * @return {Boolean}
34668      */
34669     isSelected : function(node){
34670         return this.selMap[node.id] ? true : false;  
34671     },
34672     
34673     /**
34674      * Returns an array of the selected nodes
34675      * @return {Array}
34676      */
34677     getSelectedNodes : function(){
34678         return this.selNodes;    
34679     },
34680
34681     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34682
34683     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34684
34685     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34686 });/*
34687  * Based on:
34688  * Ext JS Library 1.1.1
34689  * Copyright(c) 2006-2007, Ext JS, LLC.
34690  *
34691  * Originally Released Under LGPL - original licence link has changed is not relivant.
34692  *
34693  * Fork - LGPL
34694  * <script type="text/javascript">
34695  */
34696  
34697 /**
34698  * @class Roo.tree.TreeNode
34699  * @extends Roo.data.Node
34700  * @cfg {String} text The text for this node
34701  * @cfg {Boolean} expanded true to start the node expanded
34702  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34703  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34704  * @cfg {Boolean} disabled true to start the node disabled
34705  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34706  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34707  * @cfg {String} cls A css class to be added to the node
34708  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34709  * @cfg {String} href URL of the link used for the node (defaults to #)
34710  * @cfg {String} hrefTarget target frame for the link
34711  * @cfg {String} qtip An Ext QuickTip for the node
34712  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34713  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34714  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34715  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34716  * (defaults to undefined with no checkbox rendered)
34717  * @constructor
34718  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34719  */
34720 Roo.tree.TreeNode = function(attributes){
34721     attributes = attributes || {};
34722     if(typeof attributes == "string"){
34723         attributes = {text: attributes};
34724     }
34725     this.childrenRendered = false;
34726     this.rendered = false;
34727     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34728     this.expanded = attributes.expanded === true;
34729     this.isTarget = attributes.isTarget !== false;
34730     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34731     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34732
34733     /**
34734      * Read-only. The text for this node. To change it use setText().
34735      * @type String
34736      */
34737     this.text = attributes.text;
34738     /**
34739      * True if this node is disabled.
34740      * @type Boolean
34741      */
34742     this.disabled = attributes.disabled === true;
34743
34744     this.addEvents({
34745         /**
34746         * @event textchange
34747         * Fires when the text for this node is changed
34748         * @param {Node} this This node
34749         * @param {String} text The new text
34750         * @param {String} oldText The old text
34751         */
34752         "textchange" : true,
34753         /**
34754         * @event beforeexpand
34755         * Fires before this node is expanded, return false to cancel.
34756         * @param {Node} this This node
34757         * @param {Boolean} deep
34758         * @param {Boolean} anim
34759         */
34760         "beforeexpand" : true,
34761         /**
34762         * @event beforecollapse
34763         * Fires before this node is collapsed, return false to cancel.
34764         * @param {Node} this This node
34765         * @param {Boolean} deep
34766         * @param {Boolean} anim
34767         */
34768         "beforecollapse" : true,
34769         /**
34770         * @event expand
34771         * Fires when this node is expanded
34772         * @param {Node} this This node
34773         */
34774         "expand" : true,
34775         /**
34776         * @event disabledchange
34777         * Fires when the disabled status of this node changes
34778         * @param {Node} this This node
34779         * @param {Boolean} disabled
34780         */
34781         "disabledchange" : true,
34782         /**
34783         * @event collapse
34784         * Fires when this node is collapsed
34785         * @param {Node} this This node
34786         */
34787         "collapse" : true,
34788         /**
34789         * @event beforeclick
34790         * Fires before click processing. Return false to cancel the default action.
34791         * @param {Node} this This node
34792         * @param {Roo.EventObject} e The event object
34793         */
34794         "beforeclick":true,
34795         /**
34796         * @event checkchange
34797         * Fires when a node with a checkbox's checked property changes
34798         * @param {Node} this This node
34799         * @param {Boolean} checked
34800         */
34801         "checkchange":true,
34802         /**
34803         * @event click
34804         * Fires when this node is clicked
34805         * @param {Node} this This node
34806         * @param {Roo.EventObject} e The event object
34807         */
34808         "click":true,
34809         /**
34810         * @event dblclick
34811         * Fires when this node is double clicked
34812         * @param {Node} this This node
34813         * @param {Roo.EventObject} e The event object
34814         */
34815         "dblclick":true,
34816         /**
34817         * @event contextmenu
34818         * Fires when this node is right clicked
34819         * @param {Node} this This node
34820         * @param {Roo.EventObject} e The event object
34821         */
34822         "contextmenu":true,
34823         /**
34824         * @event beforechildrenrendered
34825         * Fires right before the child nodes for this node are rendered
34826         * @param {Node} this This node
34827         */
34828         "beforechildrenrendered":true
34829     });
34830
34831     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34832
34833     /**
34834      * Read-only. The UI for this node
34835      * @type TreeNodeUI
34836      */
34837     this.ui = new uiClass(this);
34838     
34839     // finally support items[]
34840     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34841         return;
34842     }
34843     
34844     
34845     Roo.each(this.attributes.items, function(c) {
34846         this.appendChild(Roo.factory(c,Roo.Tree));
34847     }, this);
34848     delete this.attributes.items;
34849     
34850     
34851     
34852 };
34853 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34854     preventHScroll: true,
34855     /**
34856      * Returns true if this node is expanded
34857      * @return {Boolean}
34858      */
34859     isExpanded : function(){
34860         return this.expanded;
34861     },
34862
34863     /**
34864      * Returns the UI object for this node
34865      * @return {TreeNodeUI}
34866      */
34867     getUI : function(){
34868         return this.ui;
34869     },
34870
34871     // private override
34872     setFirstChild : function(node){
34873         var of = this.firstChild;
34874         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34875         if(this.childrenRendered && of && node != of){
34876             of.renderIndent(true, true);
34877         }
34878         if(this.rendered){
34879             this.renderIndent(true, true);
34880         }
34881     },
34882
34883     // private override
34884     setLastChild : function(node){
34885         var ol = this.lastChild;
34886         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34887         if(this.childrenRendered && ol && node != ol){
34888             ol.renderIndent(true, true);
34889         }
34890         if(this.rendered){
34891             this.renderIndent(true, true);
34892         }
34893     },
34894
34895     // these methods are overridden to provide lazy rendering support
34896     // private override
34897     appendChild : function()
34898     {
34899         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34900         if(node && this.childrenRendered){
34901             node.render();
34902         }
34903         this.ui.updateExpandIcon();
34904         return node;
34905     },
34906
34907     // private override
34908     removeChild : function(node){
34909         this.ownerTree.getSelectionModel().unselect(node);
34910         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34911         // if it's been rendered remove dom node
34912         if(this.childrenRendered){
34913             node.ui.remove();
34914         }
34915         if(this.childNodes.length < 1){
34916             this.collapse(false, false);
34917         }else{
34918             this.ui.updateExpandIcon();
34919         }
34920         if(!this.firstChild) {
34921             this.childrenRendered = false;
34922         }
34923         return node;
34924     },
34925
34926     // private override
34927     insertBefore : function(node, refNode){
34928         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34929         if(newNode && refNode && this.childrenRendered){
34930             node.render();
34931         }
34932         this.ui.updateExpandIcon();
34933         return newNode;
34934     },
34935
34936     /**
34937      * Sets the text for this node
34938      * @param {String} text
34939      */
34940     setText : function(text){
34941         var oldText = this.text;
34942         this.text = text;
34943         this.attributes.text = text;
34944         if(this.rendered){ // event without subscribing
34945             this.ui.onTextChange(this, text, oldText);
34946         }
34947         this.fireEvent("textchange", this, text, oldText);
34948     },
34949
34950     /**
34951      * Triggers selection of this node
34952      */
34953     select : function(){
34954         this.getOwnerTree().getSelectionModel().select(this);
34955     },
34956
34957     /**
34958      * Triggers deselection of this node
34959      */
34960     unselect : function(){
34961         this.getOwnerTree().getSelectionModel().unselect(this);
34962     },
34963
34964     /**
34965      * Returns true if this node is selected
34966      * @return {Boolean}
34967      */
34968     isSelected : function(){
34969         return this.getOwnerTree().getSelectionModel().isSelected(this);
34970     },
34971
34972     /**
34973      * Expand this node.
34974      * @param {Boolean} deep (optional) True to expand all children as well
34975      * @param {Boolean} anim (optional) false to cancel the default animation
34976      * @param {Function} callback (optional) A callback to be called when
34977      * expanding this node completes (does not wait for deep expand to complete).
34978      * Called with 1 parameter, this node.
34979      */
34980     expand : function(deep, anim, callback){
34981         if(!this.expanded){
34982             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34983                 return;
34984             }
34985             if(!this.childrenRendered){
34986                 this.renderChildren();
34987             }
34988             this.expanded = true;
34989             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34990                 this.ui.animExpand(function(){
34991                     this.fireEvent("expand", this);
34992                     if(typeof callback == "function"){
34993                         callback(this);
34994                     }
34995                     if(deep === true){
34996                         this.expandChildNodes(true);
34997                     }
34998                 }.createDelegate(this));
34999                 return;
35000             }else{
35001                 this.ui.expand();
35002                 this.fireEvent("expand", this);
35003                 if(typeof callback == "function"){
35004                     callback(this);
35005                 }
35006             }
35007         }else{
35008            if(typeof callback == "function"){
35009                callback(this);
35010            }
35011         }
35012         if(deep === true){
35013             this.expandChildNodes(true);
35014         }
35015     },
35016
35017     isHiddenRoot : function(){
35018         return this.isRoot && !this.getOwnerTree().rootVisible;
35019     },
35020
35021     /**
35022      * Collapse this node.
35023      * @param {Boolean} deep (optional) True to collapse all children as well
35024      * @param {Boolean} anim (optional) false to cancel the default animation
35025      */
35026     collapse : function(deep, anim){
35027         if(this.expanded && !this.isHiddenRoot()){
35028             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35029                 return;
35030             }
35031             this.expanded = false;
35032             if((this.getOwnerTree().animate && anim !== false) || anim){
35033                 this.ui.animCollapse(function(){
35034                     this.fireEvent("collapse", this);
35035                     if(deep === true){
35036                         this.collapseChildNodes(true);
35037                     }
35038                 }.createDelegate(this));
35039                 return;
35040             }else{
35041                 this.ui.collapse();
35042                 this.fireEvent("collapse", this);
35043             }
35044         }
35045         if(deep === true){
35046             var cs = this.childNodes;
35047             for(var i = 0, len = cs.length; i < len; i++) {
35048                 cs[i].collapse(true, false);
35049             }
35050         }
35051     },
35052
35053     // private
35054     delayedExpand : function(delay){
35055         if(!this.expandProcId){
35056             this.expandProcId = this.expand.defer(delay, this);
35057         }
35058     },
35059
35060     // private
35061     cancelExpand : function(){
35062         if(this.expandProcId){
35063             clearTimeout(this.expandProcId);
35064         }
35065         this.expandProcId = false;
35066     },
35067
35068     /**
35069      * Toggles expanded/collapsed state of the node
35070      */
35071     toggle : function(){
35072         if(this.expanded){
35073             this.collapse();
35074         }else{
35075             this.expand();
35076         }
35077     },
35078
35079     /**
35080      * Ensures all parent nodes are expanded
35081      */
35082     ensureVisible : function(callback){
35083         var tree = this.getOwnerTree();
35084         tree.expandPath(this.parentNode.getPath(), false, function(){
35085             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35086             Roo.callback(callback);
35087         }.createDelegate(this));
35088     },
35089
35090     /**
35091      * Expand all child nodes
35092      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35093      */
35094     expandChildNodes : function(deep){
35095         var cs = this.childNodes;
35096         for(var i = 0, len = cs.length; i < len; i++) {
35097                 cs[i].expand(deep);
35098         }
35099     },
35100
35101     /**
35102      * Collapse all child nodes
35103      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35104      */
35105     collapseChildNodes : function(deep){
35106         var cs = this.childNodes;
35107         for(var i = 0, len = cs.length; i < len; i++) {
35108                 cs[i].collapse(deep);
35109         }
35110     },
35111
35112     /**
35113      * Disables this node
35114      */
35115     disable : function(){
35116         this.disabled = true;
35117         this.unselect();
35118         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35119             this.ui.onDisableChange(this, true);
35120         }
35121         this.fireEvent("disabledchange", this, true);
35122     },
35123
35124     /**
35125      * Enables this node
35126      */
35127     enable : function(){
35128         this.disabled = false;
35129         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35130             this.ui.onDisableChange(this, false);
35131         }
35132         this.fireEvent("disabledchange", this, false);
35133     },
35134
35135     // private
35136     renderChildren : function(suppressEvent){
35137         if(suppressEvent !== false){
35138             this.fireEvent("beforechildrenrendered", this);
35139         }
35140         var cs = this.childNodes;
35141         for(var i = 0, len = cs.length; i < len; i++){
35142             cs[i].render(true);
35143         }
35144         this.childrenRendered = true;
35145     },
35146
35147     // private
35148     sort : function(fn, scope){
35149         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35150         if(this.childrenRendered){
35151             var cs = this.childNodes;
35152             for(var i = 0, len = cs.length; i < len; i++){
35153                 cs[i].render(true);
35154             }
35155         }
35156     },
35157
35158     // private
35159     render : function(bulkRender){
35160         this.ui.render(bulkRender);
35161         if(!this.rendered){
35162             this.rendered = true;
35163             if(this.expanded){
35164                 this.expanded = false;
35165                 this.expand(false, false);
35166             }
35167         }
35168     },
35169
35170     // private
35171     renderIndent : function(deep, refresh){
35172         if(refresh){
35173             this.ui.childIndent = null;
35174         }
35175         this.ui.renderIndent();
35176         if(deep === true && this.childrenRendered){
35177             var cs = this.childNodes;
35178             for(var i = 0, len = cs.length; i < len; i++){
35179                 cs[i].renderIndent(true, refresh);
35180             }
35181         }
35182     }
35183 });/*
35184  * Based on:
35185  * Ext JS Library 1.1.1
35186  * Copyright(c) 2006-2007, Ext JS, LLC.
35187  *
35188  * Originally Released Under LGPL - original licence link has changed is not relivant.
35189  *
35190  * Fork - LGPL
35191  * <script type="text/javascript">
35192  */
35193  
35194 /**
35195  * @class Roo.tree.AsyncTreeNode
35196  * @extends Roo.tree.TreeNode
35197  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35198  * @constructor
35199  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35200  */
35201  Roo.tree.AsyncTreeNode = function(config){
35202     this.loaded = false;
35203     this.loading = false;
35204     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35205     /**
35206     * @event beforeload
35207     * Fires before this node is loaded, return false to cancel
35208     * @param {Node} this This node
35209     */
35210     this.addEvents({'beforeload':true, 'load': true});
35211     /**
35212     * @event load
35213     * Fires when this node is loaded
35214     * @param {Node} this This node
35215     */
35216     /**
35217      * The loader used by this node (defaults to using the tree's defined loader)
35218      * @type TreeLoader
35219      * @property loader
35220      */
35221 };
35222 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35223     expand : function(deep, anim, callback){
35224         if(this.loading){ // if an async load is already running, waiting til it's done
35225             var timer;
35226             var f = function(){
35227                 if(!this.loading){ // done loading
35228                     clearInterval(timer);
35229                     this.expand(deep, anim, callback);
35230                 }
35231             }.createDelegate(this);
35232             timer = setInterval(f, 200);
35233             return;
35234         }
35235         if(!this.loaded){
35236             if(this.fireEvent("beforeload", this) === false){
35237                 return;
35238             }
35239             this.loading = true;
35240             this.ui.beforeLoad(this);
35241             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35242             if(loader){
35243                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35244                 return;
35245             }
35246         }
35247         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35248     },
35249     
35250     /**
35251      * Returns true if this node is currently loading
35252      * @return {Boolean}
35253      */
35254     isLoading : function(){
35255         return this.loading;  
35256     },
35257     
35258     loadComplete : function(deep, anim, callback){
35259         this.loading = false;
35260         this.loaded = true;
35261         this.ui.afterLoad(this);
35262         this.fireEvent("load", this);
35263         this.expand(deep, anim, callback);
35264     },
35265     
35266     /**
35267      * Returns true if this node has been loaded
35268      * @return {Boolean}
35269      */
35270     isLoaded : function(){
35271         return this.loaded;
35272     },
35273     
35274     hasChildNodes : function(){
35275         if(!this.isLeaf() && !this.loaded){
35276             return true;
35277         }else{
35278             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35279         }
35280     },
35281
35282     /**
35283      * Trigger a reload for this node
35284      * @param {Function} callback
35285      */
35286     reload : function(callback){
35287         this.collapse(false, false);
35288         while(this.firstChild){
35289             this.removeChild(this.firstChild);
35290         }
35291         this.childrenRendered = false;
35292         this.loaded = false;
35293         if(this.isHiddenRoot()){
35294             this.expanded = false;
35295         }
35296         this.expand(false, false, callback);
35297     }
35298 });/*
35299  * Based on:
35300  * Ext JS Library 1.1.1
35301  * Copyright(c) 2006-2007, Ext JS, LLC.
35302  *
35303  * Originally Released Under LGPL - original licence link has changed is not relivant.
35304  *
35305  * Fork - LGPL
35306  * <script type="text/javascript">
35307  */
35308  
35309 /**
35310  * @class Roo.tree.TreeNodeUI
35311  * @constructor
35312  * @param {Object} node The node to render
35313  * The TreeNode UI implementation is separate from the
35314  * tree implementation. Unless you are customizing the tree UI,
35315  * you should never have to use this directly.
35316  */
35317 Roo.tree.TreeNodeUI = function(node){
35318     this.node = node;
35319     this.rendered = false;
35320     this.animating = false;
35321     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35322 };
35323
35324 Roo.tree.TreeNodeUI.prototype = {
35325     removeChild : function(node){
35326         if(this.rendered){
35327             this.ctNode.removeChild(node.ui.getEl());
35328         }
35329     },
35330
35331     beforeLoad : function(){
35332          this.addClass("x-tree-node-loading");
35333     },
35334
35335     afterLoad : function(){
35336          this.removeClass("x-tree-node-loading");
35337     },
35338
35339     onTextChange : function(node, text, oldText){
35340         if(this.rendered){
35341             this.textNode.innerHTML = text;
35342         }
35343     },
35344
35345     onDisableChange : function(node, state){
35346         this.disabled = state;
35347         if(state){
35348             this.addClass("x-tree-node-disabled");
35349         }else{
35350             this.removeClass("x-tree-node-disabled");
35351         }
35352     },
35353
35354     onSelectedChange : function(state){
35355         if(state){
35356             this.focus();
35357             this.addClass("x-tree-selected");
35358         }else{
35359             //this.blur();
35360             this.removeClass("x-tree-selected");
35361         }
35362     },
35363
35364     onMove : function(tree, node, oldParent, newParent, index, refNode){
35365         this.childIndent = null;
35366         if(this.rendered){
35367             var targetNode = newParent.ui.getContainer();
35368             if(!targetNode){//target not rendered
35369                 this.holder = document.createElement("div");
35370                 this.holder.appendChild(this.wrap);
35371                 return;
35372             }
35373             var insertBefore = refNode ? refNode.ui.getEl() : null;
35374             if(insertBefore){
35375                 targetNode.insertBefore(this.wrap, insertBefore);
35376             }else{
35377                 targetNode.appendChild(this.wrap);
35378             }
35379             this.node.renderIndent(true);
35380         }
35381     },
35382
35383     addClass : function(cls){
35384         if(this.elNode){
35385             Roo.fly(this.elNode).addClass(cls);
35386         }
35387     },
35388
35389     removeClass : function(cls){
35390         if(this.elNode){
35391             Roo.fly(this.elNode).removeClass(cls);
35392         }
35393     },
35394
35395     remove : function(){
35396         if(this.rendered){
35397             this.holder = document.createElement("div");
35398             this.holder.appendChild(this.wrap);
35399         }
35400     },
35401
35402     fireEvent : function(){
35403         return this.node.fireEvent.apply(this.node, arguments);
35404     },
35405
35406     initEvents : function(){
35407         this.node.on("move", this.onMove, this);
35408         var E = Roo.EventManager;
35409         var a = this.anchor;
35410
35411         var el = Roo.fly(a, '_treeui');
35412
35413         if(Roo.isOpera){ // opera render bug ignores the CSS
35414             el.setStyle("text-decoration", "none");
35415         }
35416
35417         el.on("click", this.onClick, this);
35418         el.on("dblclick", this.onDblClick, this);
35419
35420         if(this.checkbox){
35421             Roo.EventManager.on(this.checkbox,
35422                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35423         }
35424
35425         el.on("contextmenu", this.onContextMenu, this);
35426
35427         var icon = Roo.fly(this.iconNode);
35428         icon.on("click", this.onClick, this);
35429         icon.on("dblclick", this.onDblClick, this);
35430         icon.on("contextmenu", this.onContextMenu, this);
35431         E.on(this.ecNode, "click", this.ecClick, this, true);
35432
35433         if(this.node.disabled){
35434             this.addClass("x-tree-node-disabled");
35435         }
35436         if(this.node.hidden){
35437             this.addClass("x-tree-node-disabled");
35438         }
35439         var ot = this.node.getOwnerTree();
35440         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35441         if(dd && (!this.node.isRoot || ot.rootVisible)){
35442             Roo.dd.Registry.register(this.elNode, {
35443                 node: this.node,
35444                 handles: this.getDDHandles(),
35445                 isHandle: false
35446             });
35447         }
35448     },
35449
35450     getDDHandles : function(){
35451         return [this.iconNode, this.textNode];
35452     },
35453
35454     hide : function(){
35455         if(this.rendered){
35456             this.wrap.style.display = "none";
35457         }
35458     },
35459
35460     show : function(){
35461         if(this.rendered){
35462             this.wrap.style.display = "";
35463         }
35464     },
35465
35466     onContextMenu : function(e){
35467         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35468             e.preventDefault();
35469             this.focus();
35470             this.fireEvent("contextmenu", this.node, e);
35471         }
35472     },
35473
35474     onClick : function(e){
35475         if(this.dropping){
35476             e.stopEvent();
35477             return;
35478         }
35479         if(this.fireEvent("beforeclick", this.node, e) !== false){
35480             if(!this.disabled && this.node.attributes.href){
35481                 this.fireEvent("click", this.node, e);
35482                 return;
35483             }
35484             e.preventDefault();
35485             if(this.disabled){
35486                 return;
35487             }
35488
35489             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35490                 this.node.toggle();
35491             }
35492
35493             this.fireEvent("click", this.node, e);
35494         }else{
35495             e.stopEvent();
35496         }
35497     },
35498
35499     onDblClick : function(e){
35500         e.preventDefault();
35501         if(this.disabled){
35502             return;
35503         }
35504         if(this.checkbox){
35505             this.toggleCheck();
35506         }
35507         if(!this.animating && this.node.hasChildNodes()){
35508             this.node.toggle();
35509         }
35510         this.fireEvent("dblclick", this.node, e);
35511     },
35512
35513     onCheckChange : function(){
35514         var checked = this.checkbox.checked;
35515         this.node.attributes.checked = checked;
35516         this.fireEvent('checkchange', this.node, checked);
35517     },
35518
35519     ecClick : function(e){
35520         if(!this.animating && this.node.hasChildNodes()){
35521             this.node.toggle();
35522         }
35523     },
35524
35525     startDrop : function(){
35526         this.dropping = true;
35527     },
35528
35529     // delayed drop so the click event doesn't get fired on a drop
35530     endDrop : function(){
35531        setTimeout(function(){
35532            this.dropping = false;
35533        }.createDelegate(this), 50);
35534     },
35535
35536     expand : function(){
35537         this.updateExpandIcon();
35538         this.ctNode.style.display = "";
35539     },
35540
35541     focus : function(){
35542         if(!this.node.preventHScroll){
35543             try{this.anchor.focus();
35544             }catch(e){}
35545         }else if(!Roo.isIE){
35546             try{
35547                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35548                 var l = noscroll.scrollLeft;
35549                 this.anchor.focus();
35550                 noscroll.scrollLeft = l;
35551             }catch(e){}
35552         }
35553     },
35554
35555     toggleCheck : function(value){
35556         var cb = this.checkbox;
35557         if(cb){
35558             cb.checked = (value === undefined ? !cb.checked : value);
35559         }
35560     },
35561
35562     blur : function(){
35563         try{
35564             this.anchor.blur();
35565         }catch(e){}
35566     },
35567
35568     animExpand : function(callback){
35569         var ct = Roo.get(this.ctNode);
35570         ct.stopFx();
35571         if(!this.node.hasChildNodes()){
35572             this.updateExpandIcon();
35573             this.ctNode.style.display = "";
35574             Roo.callback(callback);
35575             return;
35576         }
35577         this.animating = true;
35578         this.updateExpandIcon();
35579
35580         ct.slideIn('t', {
35581            callback : function(){
35582                this.animating = false;
35583                Roo.callback(callback);
35584             },
35585             scope: this,
35586             duration: this.node.ownerTree.duration || .25
35587         });
35588     },
35589
35590     highlight : function(){
35591         var tree = this.node.getOwnerTree();
35592         Roo.fly(this.wrap).highlight(
35593             tree.hlColor || "C3DAF9",
35594             {endColor: tree.hlBaseColor}
35595         );
35596     },
35597
35598     collapse : function(){
35599         this.updateExpandIcon();
35600         this.ctNode.style.display = "none";
35601     },
35602
35603     animCollapse : function(callback){
35604         var ct = Roo.get(this.ctNode);
35605         ct.enableDisplayMode('block');
35606         ct.stopFx();
35607
35608         this.animating = true;
35609         this.updateExpandIcon();
35610
35611         ct.slideOut('t', {
35612             callback : function(){
35613                this.animating = false;
35614                Roo.callback(callback);
35615             },
35616             scope: this,
35617             duration: this.node.ownerTree.duration || .25
35618         });
35619     },
35620
35621     getContainer : function(){
35622         return this.ctNode;
35623     },
35624
35625     getEl : function(){
35626         return this.wrap;
35627     },
35628
35629     appendDDGhost : function(ghostNode){
35630         ghostNode.appendChild(this.elNode.cloneNode(true));
35631     },
35632
35633     getDDRepairXY : function(){
35634         return Roo.lib.Dom.getXY(this.iconNode);
35635     },
35636
35637     onRender : function(){
35638         this.render();
35639     },
35640
35641     render : function(bulkRender){
35642         var n = this.node, a = n.attributes;
35643         var targetNode = n.parentNode ?
35644               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35645
35646         if(!this.rendered){
35647             this.rendered = true;
35648
35649             this.renderElements(n, a, targetNode, bulkRender);
35650
35651             if(a.qtip){
35652                if(this.textNode.setAttributeNS){
35653                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35654                    if(a.qtipTitle){
35655                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35656                    }
35657                }else{
35658                    this.textNode.setAttribute("ext:qtip", a.qtip);
35659                    if(a.qtipTitle){
35660                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35661                    }
35662                }
35663             }else if(a.qtipCfg){
35664                 a.qtipCfg.target = Roo.id(this.textNode);
35665                 Roo.QuickTips.register(a.qtipCfg);
35666             }
35667             this.initEvents();
35668             if(!this.node.expanded){
35669                 this.updateExpandIcon();
35670             }
35671         }else{
35672             if(bulkRender === true) {
35673                 targetNode.appendChild(this.wrap);
35674             }
35675         }
35676     },
35677
35678     renderElements : function(n, a, targetNode, bulkRender)
35679     {
35680         // add some indent caching, this helps performance when rendering a large tree
35681         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35682         var t = n.getOwnerTree();
35683         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35684         if (typeof(n.attributes.html) != 'undefined') {
35685             txt = n.attributes.html;
35686         }
35687         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35688         var cb = typeof a.checked == 'boolean';
35689         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35690         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35691             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35692             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35693             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35694             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35695             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35696              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35697                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35698             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35699             "</li>"];
35700
35701         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35702             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35703                                 n.nextSibling.ui.getEl(), buf.join(""));
35704         }else{
35705             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35706         }
35707
35708         this.elNode = this.wrap.childNodes[0];
35709         this.ctNode = this.wrap.childNodes[1];
35710         var cs = this.elNode.childNodes;
35711         this.indentNode = cs[0];
35712         this.ecNode = cs[1];
35713         this.iconNode = cs[2];
35714         var index = 3;
35715         if(cb){
35716             this.checkbox = cs[3];
35717             index++;
35718         }
35719         this.anchor = cs[index];
35720         this.textNode = cs[index].firstChild;
35721     },
35722
35723     getAnchor : function(){
35724         return this.anchor;
35725     },
35726
35727     getTextEl : function(){
35728         return this.textNode;
35729     },
35730
35731     getIconEl : function(){
35732         return this.iconNode;
35733     },
35734
35735     isChecked : function(){
35736         return this.checkbox ? this.checkbox.checked : false;
35737     },
35738
35739     updateExpandIcon : function(){
35740         if(this.rendered){
35741             var n = this.node, c1, c2;
35742             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35743             var hasChild = n.hasChildNodes();
35744             if(hasChild){
35745                 if(n.expanded){
35746                     cls += "-minus";
35747                     c1 = "x-tree-node-collapsed";
35748                     c2 = "x-tree-node-expanded";
35749                 }else{
35750                     cls += "-plus";
35751                     c1 = "x-tree-node-expanded";
35752                     c2 = "x-tree-node-collapsed";
35753                 }
35754                 if(this.wasLeaf){
35755                     this.removeClass("x-tree-node-leaf");
35756                     this.wasLeaf = false;
35757                 }
35758                 if(this.c1 != c1 || this.c2 != c2){
35759                     Roo.fly(this.elNode).replaceClass(c1, c2);
35760                     this.c1 = c1; this.c2 = c2;
35761                 }
35762             }else{
35763                 // this changes non-leafs into leafs if they have no children.
35764                 // it's not very rational behaviour..
35765                 
35766                 if(!this.wasLeaf && this.node.leaf){
35767                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35768                     delete this.c1;
35769                     delete this.c2;
35770                     this.wasLeaf = true;
35771                 }
35772             }
35773             var ecc = "x-tree-ec-icon "+cls;
35774             if(this.ecc != ecc){
35775                 this.ecNode.className = ecc;
35776                 this.ecc = ecc;
35777             }
35778         }
35779     },
35780
35781     getChildIndent : function(){
35782         if(!this.childIndent){
35783             var buf = [];
35784             var p = this.node;
35785             while(p){
35786                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35787                     if(!p.isLast()) {
35788                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35789                     } else {
35790                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35791                     }
35792                 }
35793                 p = p.parentNode;
35794             }
35795             this.childIndent = buf.join("");
35796         }
35797         return this.childIndent;
35798     },
35799
35800     renderIndent : function(){
35801         if(this.rendered){
35802             var indent = "";
35803             var p = this.node.parentNode;
35804             if(p){
35805                 indent = p.ui.getChildIndent();
35806             }
35807             if(this.indentMarkup != indent){ // don't rerender if not required
35808                 this.indentNode.innerHTML = indent;
35809                 this.indentMarkup = indent;
35810             }
35811             this.updateExpandIcon();
35812         }
35813     }
35814 };
35815
35816 Roo.tree.RootTreeNodeUI = function(){
35817     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35818 };
35819 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35820     render : function(){
35821         if(!this.rendered){
35822             var targetNode = this.node.ownerTree.innerCt.dom;
35823             this.node.expanded = true;
35824             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35825             this.wrap = this.ctNode = targetNode.firstChild;
35826         }
35827     },
35828     collapse : function(){
35829     },
35830     expand : function(){
35831     }
35832 });/*
35833  * Based on:
35834  * Ext JS Library 1.1.1
35835  * Copyright(c) 2006-2007, Ext JS, LLC.
35836  *
35837  * Originally Released Under LGPL - original licence link has changed is not relivant.
35838  *
35839  * Fork - LGPL
35840  * <script type="text/javascript">
35841  */
35842 /**
35843  * @class Roo.tree.TreeLoader
35844  * @extends Roo.util.Observable
35845  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35846  * nodes from a specified URL. The response must be a javascript Array definition
35847  * who's elements are node definition objects. eg:
35848  * <pre><code>
35849 {  success : true,
35850    data :      [
35851    
35852     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35853     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35854     ]
35855 }
35856
35857
35858 </code></pre>
35859  * <br><br>
35860  * The old style respose with just an array is still supported, but not recommended.
35861  * <br><br>
35862  *
35863  * A server request is sent, and child nodes are loaded only when a node is expanded.
35864  * The loading node's id is passed to the server under the parameter name "node" to
35865  * enable the server to produce the correct child nodes.
35866  * <br><br>
35867  * To pass extra parameters, an event handler may be attached to the "beforeload"
35868  * event, and the parameters specified in the TreeLoader's baseParams property:
35869  * <pre><code>
35870     myTreeLoader.on("beforeload", function(treeLoader, node) {
35871         this.baseParams.category = node.attributes.category;
35872     }, this);
35873 </code></pre><
35874  * This would pass an HTTP parameter called "category" to the server containing
35875  * the value of the Node's "category" attribute.
35876  * @constructor
35877  * Creates a new Treeloader.
35878  * @param {Object} config A config object containing config properties.
35879  */
35880 Roo.tree.TreeLoader = function(config){
35881     this.baseParams = {};
35882     this.requestMethod = "POST";
35883     Roo.apply(this, config);
35884
35885     this.addEvents({
35886     
35887         /**
35888          * @event beforeload
35889          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35890          * @param {Object} This TreeLoader object.
35891          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35892          * @param {Object} callback The callback function specified in the {@link #load} call.
35893          */
35894         beforeload : true,
35895         /**
35896          * @event load
35897          * Fires when the node has been successfuly loaded.
35898          * @param {Object} This TreeLoader object.
35899          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35900          * @param {Object} response The response object containing the data from the server.
35901          */
35902         load : true,
35903         /**
35904          * @event loadexception
35905          * Fires if the network request failed.
35906          * @param {Object} This TreeLoader object.
35907          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35908          * @param {Object} response The response object containing the data from the server.
35909          */
35910         loadexception : true,
35911         /**
35912          * @event create
35913          * Fires before a node is created, enabling you to return custom Node types 
35914          * @param {Object} This TreeLoader object.
35915          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35916          */
35917         create : true
35918     });
35919
35920     Roo.tree.TreeLoader.superclass.constructor.call(this);
35921 };
35922
35923 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35924     /**
35925     * @cfg {String} dataUrl The URL from which to request a Json string which
35926     * specifies an array of node definition object representing the child nodes
35927     * to be loaded.
35928     */
35929     /**
35930     * @cfg {String} requestMethod either GET or POST
35931     * defaults to POST (due to BC)
35932     * to be loaded.
35933     */
35934     /**
35935     * @cfg {Object} baseParams (optional) An object containing properties which
35936     * specify HTTP parameters to be passed to each request for child nodes.
35937     */
35938     /**
35939     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35940     * created by this loader. If the attributes sent by the server have an attribute in this object,
35941     * they take priority.
35942     */
35943     /**
35944     * @cfg {Object} uiProviders (optional) An object containing properties which
35945     * 
35946     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35947     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35948     * <i>uiProvider</i> attribute of a returned child node is a string rather
35949     * than a reference to a TreeNodeUI implementation, this that string value
35950     * is used as a property name in the uiProviders object. You can define the provider named
35951     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35952     */
35953     uiProviders : {},
35954
35955     /**
35956     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35957     * child nodes before loading.
35958     */
35959     clearOnLoad : true,
35960
35961     /**
35962     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35963     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35964     * Grid query { data : [ .....] }
35965     */
35966     
35967     root : false,
35968      /**
35969     * @cfg {String} queryParam (optional) 
35970     * Name of the query as it will be passed on the querystring (defaults to 'node')
35971     * eg. the request will be ?node=[id]
35972     */
35973     
35974     
35975     queryParam: false,
35976     
35977     /**
35978      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35979      * This is called automatically when a node is expanded, but may be used to reload
35980      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35981      * @param {Roo.tree.TreeNode} node
35982      * @param {Function} callback
35983      */
35984     load : function(node, callback){
35985         if(this.clearOnLoad){
35986             while(node.firstChild){
35987                 node.removeChild(node.firstChild);
35988             }
35989         }
35990         if(node.attributes.children){ // preloaded json children
35991             var cs = node.attributes.children;
35992             for(var i = 0, len = cs.length; i < len; i++){
35993                 node.appendChild(this.createNode(cs[i]));
35994             }
35995             if(typeof callback == "function"){
35996                 callback();
35997             }
35998         }else if(this.dataUrl){
35999             this.requestData(node, callback);
36000         }
36001     },
36002
36003     getParams: function(node){
36004         var buf = [], bp = this.baseParams;
36005         for(var key in bp){
36006             if(typeof bp[key] != "function"){
36007                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36008             }
36009         }
36010         var n = this.queryParam === false ? 'node' : this.queryParam;
36011         buf.push(n + "=", encodeURIComponent(node.id));
36012         return buf.join("");
36013     },
36014
36015     requestData : function(node, callback){
36016         if(this.fireEvent("beforeload", this, node, callback) !== false){
36017             this.transId = Roo.Ajax.request({
36018                 method:this.requestMethod,
36019                 url: this.dataUrl||this.url,
36020                 success: this.handleResponse,
36021                 failure: this.handleFailure,
36022                 scope: this,
36023                 argument: {callback: callback, node: node},
36024                 params: this.getParams(node)
36025             });
36026         }else{
36027             // if the load is cancelled, make sure we notify
36028             // the node that we are done
36029             if(typeof callback == "function"){
36030                 callback();
36031             }
36032         }
36033     },
36034
36035     isLoading : function(){
36036         return this.transId ? true : false;
36037     },
36038
36039     abort : function(){
36040         if(this.isLoading()){
36041             Roo.Ajax.abort(this.transId);
36042         }
36043     },
36044
36045     // private
36046     createNode : function(attr)
36047     {
36048         // apply baseAttrs, nice idea Corey!
36049         if(this.baseAttrs){
36050             Roo.applyIf(attr, this.baseAttrs);
36051         }
36052         if(this.applyLoader !== false){
36053             attr.loader = this;
36054         }
36055         // uiProvider = depreciated..
36056         
36057         if(typeof(attr.uiProvider) == 'string'){
36058            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36059                 /**  eval:var:attr */ eval(attr.uiProvider);
36060         }
36061         if(typeof(this.uiProviders['default']) != 'undefined') {
36062             attr.uiProvider = this.uiProviders['default'];
36063         }
36064         
36065         this.fireEvent('create', this, attr);
36066         
36067         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36068         return(attr.leaf ?
36069                         new Roo.tree.TreeNode(attr) :
36070                         new Roo.tree.AsyncTreeNode(attr));
36071     },
36072
36073     processResponse : function(response, node, callback)
36074     {
36075         var json = response.responseText;
36076         try {
36077             
36078             var o = Roo.decode(json);
36079             
36080             if (this.root === false && typeof(o.success) != undefined) {
36081                 this.root = 'data'; // the default behaviour for list like data..
36082                 }
36083                 
36084             if (this.root !== false &&  !o.success) {
36085                 // it's a failure condition.
36086                 var a = response.argument;
36087                 this.fireEvent("loadexception", this, a.node, response);
36088                 Roo.log("Load failed - should have a handler really");
36089                 return;
36090             }
36091             
36092             
36093             
36094             if (this.root !== false) {
36095                  o = o[this.root];
36096             }
36097             
36098             for(var i = 0, len = o.length; i < len; i++){
36099                 var n = this.createNode(o[i]);
36100                 if(n){
36101                     node.appendChild(n);
36102                 }
36103             }
36104             if(typeof callback == "function"){
36105                 callback(this, node);
36106             }
36107         }catch(e){
36108             this.handleFailure(response);
36109         }
36110     },
36111
36112     handleResponse : function(response){
36113         this.transId = false;
36114         var a = response.argument;
36115         this.processResponse(response, a.node, a.callback);
36116         this.fireEvent("load", this, a.node, response);
36117     },
36118
36119     handleFailure : function(response)
36120     {
36121         // should handle failure better..
36122         this.transId = false;
36123         var a = response.argument;
36124         this.fireEvent("loadexception", this, a.node, response);
36125         if(typeof a.callback == "function"){
36126             a.callback(this, a.node);
36127         }
36128     }
36129 });/*
36130  * Based on:
36131  * Ext JS Library 1.1.1
36132  * Copyright(c) 2006-2007, Ext JS, LLC.
36133  *
36134  * Originally Released Under LGPL - original licence link has changed is not relivant.
36135  *
36136  * Fork - LGPL
36137  * <script type="text/javascript">
36138  */
36139
36140 /**
36141 * @class Roo.tree.TreeFilter
36142 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36143 * @param {TreePanel} tree
36144 * @param {Object} config (optional)
36145  */
36146 Roo.tree.TreeFilter = function(tree, config){
36147     this.tree = tree;
36148     this.filtered = {};
36149     Roo.apply(this, config);
36150 };
36151
36152 Roo.tree.TreeFilter.prototype = {
36153     clearBlank:false,
36154     reverse:false,
36155     autoClear:false,
36156     remove:false,
36157
36158      /**
36159      * Filter the data by a specific attribute.
36160      * @param {String/RegExp} value Either string that the attribute value
36161      * should start with or a RegExp to test against the attribute
36162      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36163      * @param {TreeNode} startNode (optional) The node to start the filter at.
36164      */
36165     filter : function(value, attr, startNode){
36166         attr = attr || "text";
36167         var f;
36168         if(typeof value == "string"){
36169             var vlen = value.length;
36170             // auto clear empty filter
36171             if(vlen == 0 && this.clearBlank){
36172                 this.clear();
36173                 return;
36174             }
36175             value = value.toLowerCase();
36176             f = function(n){
36177                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36178             };
36179         }else if(value.exec){ // regex?
36180             f = function(n){
36181                 return value.test(n.attributes[attr]);
36182             };
36183         }else{
36184             throw 'Illegal filter type, must be string or regex';
36185         }
36186         this.filterBy(f, null, startNode);
36187         },
36188
36189     /**
36190      * Filter by a function. The passed function will be called with each
36191      * node in the tree (or from the startNode). If the function returns true, the node is kept
36192      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36193      * @param {Function} fn The filter function
36194      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36195      */
36196     filterBy : function(fn, scope, startNode){
36197         startNode = startNode || this.tree.root;
36198         if(this.autoClear){
36199             this.clear();
36200         }
36201         var af = this.filtered, rv = this.reverse;
36202         var f = function(n){
36203             if(n == startNode){
36204                 return true;
36205             }
36206             if(af[n.id]){
36207                 return false;
36208             }
36209             var m = fn.call(scope || n, n);
36210             if(!m || rv){
36211                 af[n.id] = n;
36212                 n.ui.hide();
36213                 return false;
36214             }
36215             return true;
36216         };
36217         startNode.cascade(f);
36218         if(this.remove){
36219            for(var id in af){
36220                if(typeof id != "function"){
36221                    var n = af[id];
36222                    if(n && n.parentNode){
36223                        n.parentNode.removeChild(n);
36224                    }
36225                }
36226            }
36227         }
36228     },
36229
36230     /**
36231      * Clears the current filter. Note: with the "remove" option
36232      * set a filter cannot be cleared.
36233      */
36234     clear : function(){
36235         var t = this.tree;
36236         var af = this.filtered;
36237         for(var id in af){
36238             if(typeof id != "function"){
36239                 var n = af[id];
36240                 if(n){
36241                     n.ui.show();
36242                 }
36243             }
36244         }
36245         this.filtered = {};
36246     }
36247 };
36248 /*
36249  * Based on:
36250  * Ext JS Library 1.1.1
36251  * Copyright(c) 2006-2007, Ext JS, LLC.
36252  *
36253  * Originally Released Under LGPL - original licence link has changed is not relivant.
36254  *
36255  * Fork - LGPL
36256  * <script type="text/javascript">
36257  */
36258  
36259
36260 /**
36261  * @class Roo.tree.TreeSorter
36262  * Provides sorting of nodes in a TreePanel
36263  * 
36264  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36265  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36266  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36267  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36268  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36269  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36270  * @constructor
36271  * @param {TreePanel} tree
36272  * @param {Object} config
36273  */
36274 Roo.tree.TreeSorter = function(tree, config){
36275     Roo.apply(this, config);
36276     tree.on("beforechildrenrendered", this.doSort, this);
36277     tree.on("append", this.updateSort, this);
36278     tree.on("insert", this.updateSort, this);
36279     
36280     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36281     var p = this.property || "text";
36282     var sortType = this.sortType;
36283     var fs = this.folderSort;
36284     var cs = this.caseSensitive === true;
36285     var leafAttr = this.leafAttr || 'leaf';
36286
36287     this.sortFn = function(n1, n2){
36288         if(fs){
36289             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36290                 return 1;
36291             }
36292             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36293                 return -1;
36294             }
36295         }
36296         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36297         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36298         if(v1 < v2){
36299                         return dsc ? +1 : -1;
36300                 }else if(v1 > v2){
36301                         return dsc ? -1 : +1;
36302         }else{
36303                 return 0;
36304         }
36305     };
36306 };
36307
36308 Roo.tree.TreeSorter.prototype = {
36309     doSort : function(node){
36310         node.sort(this.sortFn);
36311     },
36312     
36313     compareNodes : function(n1, n2){
36314         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36315     },
36316     
36317     updateSort : function(tree, node){
36318         if(node.childrenRendered){
36319             this.doSort.defer(1, this, [node]);
36320         }
36321     }
36322 };/*
36323  * Based on:
36324  * Ext JS Library 1.1.1
36325  * Copyright(c) 2006-2007, Ext JS, LLC.
36326  *
36327  * Originally Released Under LGPL - original licence link has changed is not relivant.
36328  *
36329  * Fork - LGPL
36330  * <script type="text/javascript">
36331  */
36332
36333 if(Roo.dd.DropZone){
36334     
36335 Roo.tree.TreeDropZone = function(tree, config){
36336     this.allowParentInsert = false;
36337     this.allowContainerDrop = false;
36338     this.appendOnly = false;
36339     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36340     this.tree = tree;
36341     this.lastInsertClass = "x-tree-no-status";
36342     this.dragOverData = {};
36343 };
36344
36345 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36346     ddGroup : "TreeDD",
36347     scroll:  true,
36348     
36349     expandDelay : 1000,
36350     
36351     expandNode : function(node){
36352         if(node.hasChildNodes() && !node.isExpanded()){
36353             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36354         }
36355     },
36356     
36357     queueExpand : function(node){
36358         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36359     },
36360     
36361     cancelExpand : function(){
36362         if(this.expandProcId){
36363             clearTimeout(this.expandProcId);
36364             this.expandProcId = false;
36365         }
36366     },
36367     
36368     isValidDropPoint : function(n, pt, dd, e, data){
36369         if(!n || !data){ return false; }
36370         var targetNode = n.node;
36371         var dropNode = data.node;
36372         // default drop rules
36373         if(!(targetNode && targetNode.isTarget && pt)){
36374             return false;
36375         }
36376         if(pt == "append" && targetNode.allowChildren === false){
36377             return false;
36378         }
36379         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36380             return false;
36381         }
36382         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36383             return false;
36384         }
36385         // reuse the object
36386         var overEvent = this.dragOverData;
36387         overEvent.tree = this.tree;
36388         overEvent.target = targetNode;
36389         overEvent.data = data;
36390         overEvent.point = pt;
36391         overEvent.source = dd;
36392         overEvent.rawEvent = e;
36393         overEvent.dropNode = dropNode;
36394         overEvent.cancel = false;  
36395         var result = this.tree.fireEvent("nodedragover", overEvent);
36396         return overEvent.cancel === false && result !== false;
36397     },
36398     
36399     getDropPoint : function(e, n, dd)
36400     {
36401         var tn = n.node;
36402         if(tn.isRoot){
36403             return tn.allowChildren !== false ? "append" : false; // always append for root
36404         }
36405         var dragEl = n.ddel;
36406         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36407         var y = Roo.lib.Event.getPageY(e);
36408         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36409         
36410         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36411         var noAppend = tn.allowChildren === false;
36412         if(this.appendOnly || tn.parentNode.allowChildren === false){
36413             return noAppend ? false : "append";
36414         }
36415         var noBelow = false;
36416         if(!this.allowParentInsert){
36417             noBelow = tn.hasChildNodes() && tn.isExpanded();
36418         }
36419         var q = (b - t) / (noAppend ? 2 : 3);
36420         if(y >= t && y < (t + q)){
36421             return "above";
36422         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36423             return "below";
36424         }else{
36425             return "append";
36426         }
36427     },
36428     
36429     onNodeEnter : function(n, dd, e, data)
36430     {
36431         this.cancelExpand();
36432     },
36433     
36434     onNodeOver : function(n, dd, e, data)
36435     {
36436        
36437         var pt = this.getDropPoint(e, n, dd);
36438         var node = n.node;
36439         
36440         // auto node expand check
36441         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36442             this.queueExpand(node);
36443         }else if(pt != "append"){
36444             this.cancelExpand();
36445         }
36446         
36447         // set the insert point style on the target node
36448         var returnCls = this.dropNotAllowed;
36449         if(this.isValidDropPoint(n, pt, dd, e, data)){
36450            if(pt){
36451                var el = n.ddel;
36452                var cls;
36453                if(pt == "above"){
36454                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36455                    cls = "x-tree-drag-insert-above";
36456                }else if(pt == "below"){
36457                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36458                    cls = "x-tree-drag-insert-below";
36459                }else{
36460                    returnCls = "x-tree-drop-ok-append";
36461                    cls = "x-tree-drag-append";
36462                }
36463                if(this.lastInsertClass != cls){
36464                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36465                    this.lastInsertClass = cls;
36466                }
36467            }
36468        }
36469        return returnCls;
36470     },
36471     
36472     onNodeOut : function(n, dd, e, data){
36473         
36474         this.cancelExpand();
36475         this.removeDropIndicators(n);
36476     },
36477     
36478     onNodeDrop : function(n, dd, e, data){
36479         var point = this.getDropPoint(e, n, dd);
36480         var targetNode = n.node;
36481         targetNode.ui.startDrop();
36482         if(!this.isValidDropPoint(n, point, dd, e, data)){
36483             targetNode.ui.endDrop();
36484             return false;
36485         }
36486         // first try to find the drop node
36487         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36488         var dropEvent = {
36489             tree : this.tree,
36490             target: targetNode,
36491             data: data,
36492             point: point,
36493             source: dd,
36494             rawEvent: e,
36495             dropNode: dropNode,
36496             cancel: !dropNode   
36497         };
36498         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36499         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36500             targetNode.ui.endDrop();
36501             return false;
36502         }
36503         // allow target changing
36504         targetNode = dropEvent.target;
36505         if(point == "append" && !targetNode.isExpanded()){
36506             targetNode.expand(false, null, function(){
36507                 this.completeDrop(dropEvent);
36508             }.createDelegate(this));
36509         }else{
36510             this.completeDrop(dropEvent);
36511         }
36512         return true;
36513     },
36514     
36515     completeDrop : function(de){
36516         var ns = de.dropNode, p = de.point, t = de.target;
36517         if(!(ns instanceof Array)){
36518             ns = [ns];
36519         }
36520         var n;
36521         for(var i = 0, len = ns.length; i < len; i++){
36522             n = ns[i];
36523             if(p == "above"){
36524                 t.parentNode.insertBefore(n, t);
36525             }else if(p == "below"){
36526                 t.parentNode.insertBefore(n, t.nextSibling);
36527             }else{
36528                 t.appendChild(n);
36529             }
36530         }
36531         n.ui.focus();
36532         if(this.tree.hlDrop){
36533             n.ui.highlight();
36534         }
36535         t.ui.endDrop();
36536         this.tree.fireEvent("nodedrop", de);
36537     },
36538     
36539     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36540         if(this.tree.hlDrop){
36541             dropNode.ui.focus();
36542             dropNode.ui.highlight();
36543         }
36544         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36545     },
36546     
36547     getTree : function(){
36548         return this.tree;
36549     },
36550     
36551     removeDropIndicators : function(n){
36552         if(n && n.ddel){
36553             var el = n.ddel;
36554             Roo.fly(el).removeClass([
36555                     "x-tree-drag-insert-above",
36556                     "x-tree-drag-insert-below",
36557                     "x-tree-drag-append"]);
36558             this.lastInsertClass = "_noclass";
36559         }
36560     },
36561     
36562     beforeDragDrop : function(target, e, id){
36563         this.cancelExpand();
36564         return true;
36565     },
36566     
36567     afterRepair : function(data){
36568         if(data && Roo.enableFx){
36569             data.node.ui.highlight();
36570         }
36571         this.hideProxy();
36572     } 
36573     
36574 });
36575
36576 }
36577 /*
36578  * Based on:
36579  * Ext JS Library 1.1.1
36580  * Copyright(c) 2006-2007, Ext JS, LLC.
36581  *
36582  * Originally Released Under LGPL - original licence link has changed is not relivant.
36583  *
36584  * Fork - LGPL
36585  * <script type="text/javascript">
36586  */
36587  
36588
36589 if(Roo.dd.DragZone){
36590 Roo.tree.TreeDragZone = function(tree, config){
36591     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36592     this.tree = tree;
36593 };
36594
36595 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36596     ddGroup : "TreeDD",
36597    
36598     onBeforeDrag : function(data, e){
36599         var n = data.node;
36600         return n && n.draggable && !n.disabled;
36601     },
36602      
36603     
36604     onInitDrag : function(e){
36605         var data = this.dragData;
36606         this.tree.getSelectionModel().select(data.node);
36607         this.proxy.update("");
36608         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36609         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36610     },
36611     
36612     getRepairXY : function(e, data){
36613         return data.node.ui.getDDRepairXY();
36614     },
36615     
36616     onEndDrag : function(data, e){
36617         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36618         
36619         
36620     },
36621     
36622     onValidDrop : function(dd, e, id){
36623         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36624         this.hideProxy();
36625     },
36626     
36627     beforeInvalidDrop : function(e, id){
36628         // this scrolls the original position back into view
36629         var sm = this.tree.getSelectionModel();
36630         sm.clearSelections();
36631         sm.select(this.dragData.node);
36632     }
36633 });
36634 }/*
36635  * Based on:
36636  * Ext JS Library 1.1.1
36637  * Copyright(c) 2006-2007, Ext JS, LLC.
36638  *
36639  * Originally Released Under LGPL - original licence link has changed is not relivant.
36640  *
36641  * Fork - LGPL
36642  * <script type="text/javascript">
36643  */
36644 /**
36645  * @class Roo.tree.TreeEditor
36646  * @extends Roo.Editor
36647  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36648  * as the editor field.
36649  * @constructor
36650  * @param {Object} config (used to be the tree panel.)
36651  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36652  * 
36653  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36654  * @cfg {Roo.form.TextField|Object} field The field configuration
36655  *
36656  * 
36657  */
36658 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36659     var tree = config;
36660     var field;
36661     if (oldconfig) { // old style..
36662         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36663     } else {
36664         // new style..
36665         tree = config.tree;
36666         config.field = config.field  || {};
36667         config.field.xtype = 'TextField';
36668         field = Roo.factory(config.field, Roo.form);
36669     }
36670     config = config || {};
36671     
36672     
36673     this.addEvents({
36674         /**
36675          * @event beforenodeedit
36676          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36677          * false from the handler of this event.
36678          * @param {Editor} this
36679          * @param {Roo.tree.Node} node 
36680          */
36681         "beforenodeedit" : true
36682     });
36683     
36684     //Roo.log(config);
36685     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36686
36687     this.tree = tree;
36688
36689     tree.on('beforeclick', this.beforeNodeClick, this);
36690     tree.getTreeEl().on('mousedown', this.hide, this);
36691     this.on('complete', this.updateNode, this);
36692     this.on('beforestartedit', this.fitToTree, this);
36693     this.on('startedit', this.bindScroll, this, {delay:10});
36694     this.on('specialkey', this.onSpecialKey, this);
36695 };
36696
36697 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36698     /**
36699      * @cfg {String} alignment
36700      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36701      */
36702     alignment: "l-l",
36703     // inherit
36704     autoSize: false,
36705     /**
36706      * @cfg {Boolean} hideEl
36707      * True to hide the bound element while the editor is displayed (defaults to false)
36708      */
36709     hideEl : false,
36710     /**
36711      * @cfg {String} cls
36712      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36713      */
36714     cls: "x-small-editor x-tree-editor",
36715     /**
36716      * @cfg {Boolean} shim
36717      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36718      */
36719     shim:false,
36720     // inherit
36721     shadow:"frame",
36722     /**
36723      * @cfg {Number} maxWidth
36724      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36725      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36726      * scroll and client offsets into account prior to each edit.
36727      */
36728     maxWidth: 250,
36729
36730     editDelay : 350,
36731
36732     // private
36733     fitToTree : function(ed, el){
36734         var td = this.tree.getTreeEl().dom, nd = el.dom;
36735         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36736             td.scrollLeft = nd.offsetLeft;
36737         }
36738         var w = Math.min(
36739                 this.maxWidth,
36740                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36741         this.setSize(w, '');
36742         
36743         return this.fireEvent('beforenodeedit', this, this.editNode);
36744         
36745     },
36746
36747     // private
36748     triggerEdit : function(node){
36749         this.completeEdit();
36750         this.editNode = node;
36751         this.startEdit(node.ui.textNode, node.text);
36752     },
36753
36754     // private
36755     bindScroll : function(){
36756         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36757     },
36758
36759     // private
36760     beforeNodeClick : function(node, e){
36761         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36762         this.lastClick = new Date();
36763         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36764             e.stopEvent();
36765             this.triggerEdit(node);
36766             return false;
36767         }
36768         return true;
36769     },
36770
36771     // private
36772     updateNode : function(ed, value){
36773         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36774         this.editNode.setText(value);
36775     },
36776
36777     // private
36778     onHide : function(){
36779         Roo.tree.TreeEditor.superclass.onHide.call(this);
36780         if(this.editNode){
36781             this.editNode.ui.focus();
36782         }
36783     },
36784
36785     // private
36786     onSpecialKey : function(field, e){
36787         var k = e.getKey();
36788         if(k == e.ESC){
36789             e.stopEvent();
36790             this.cancelEdit();
36791         }else if(k == e.ENTER && !e.hasModifier()){
36792             e.stopEvent();
36793             this.completeEdit();
36794         }
36795     }
36796 });//<Script type="text/javascript">
36797 /*
36798  * Based on:
36799  * Ext JS Library 1.1.1
36800  * Copyright(c) 2006-2007, Ext JS, LLC.
36801  *
36802  * Originally Released Under LGPL - original licence link has changed is not relivant.
36803  *
36804  * Fork - LGPL
36805  * <script type="text/javascript">
36806  */
36807  
36808 /**
36809  * Not documented??? - probably should be...
36810  */
36811
36812 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36813     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36814     
36815     renderElements : function(n, a, targetNode, bulkRender){
36816         //consel.log("renderElements?");
36817         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36818
36819         var t = n.getOwnerTree();
36820         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36821         
36822         var cols = t.columns;
36823         var bw = t.borderWidth;
36824         var c = cols[0];
36825         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36826          var cb = typeof a.checked == "boolean";
36827         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36828         var colcls = 'x-t-' + tid + '-c0';
36829         var buf = [
36830             '<li class="x-tree-node">',
36831             
36832                 
36833                 '<div class="x-tree-node-el ', a.cls,'">',
36834                     // extran...
36835                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36836                 
36837                 
36838                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36839                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36840                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36841                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36842                            (a.iconCls ? ' '+a.iconCls : ''),
36843                            '" unselectable="on" />',
36844                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36845                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36846                              
36847                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36848                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36849                             '<span unselectable="on" qtip="' + tx + '">',
36850                              tx,
36851                              '</span></a>' ,
36852                     '</div>',
36853                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36854                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36855                  ];
36856         for(var i = 1, len = cols.length; i < len; i++){
36857             c = cols[i];
36858             colcls = 'x-t-' + tid + '-c' +i;
36859             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36860             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36861                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36862                       "</div>");
36863          }
36864          
36865          buf.push(
36866             '</a>',
36867             '<div class="x-clear"></div></div>',
36868             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36869             "</li>");
36870         
36871         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36872             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36873                                 n.nextSibling.ui.getEl(), buf.join(""));
36874         }else{
36875             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36876         }
36877         var el = this.wrap.firstChild;
36878         this.elRow = el;
36879         this.elNode = el.firstChild;
36880         this.ranchor = el.childNodes[1];
36881         this.ctNode = this.wrap.childNodes[1];
36882         var cs = el.firstChild.childNodes;
36883         this.indentNode = cs[0];
36884         this.ecNode = cs[1];
36885         this.iconNode = cs[2];
36886         var index = 3;
36887         if(cb){
36888             this.checkbox = cs[3];
36889             index++;
36890         }
36891         this.anchor = cs[index];
36892         
36893         this.textNode = cs[index].firstChild;
36894         
36895         //el.on("click", this.onClick, this);
36896         //el.on("dblclick", this.onDblClick, this);
36897         
36898         
36899        // console.log(this);
36900     },
36901     initEvents : function(){
36902         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36903         
36904             
36905         var a = this.ranchor;
36906
36907         var el = Roo.get(a);
36908
36909         if(Roo.isOpera){ // opera render bug ignores the CSS
36910             el.setStyle("text-decoration", "none");
36911         }
36912
36913         el.on("click", this.onClick, this);
36914         el.on("dblclick", this.onDblClick, this);
36915         el.on("contextmenu", this.onContextMenu, this);
36916         
36917     },
36918     
36919     /*onSelectedChange : function(state){
36920         if(state){
36921             this.focus();
36922             this.addClass("x-tree-selected");
36923         }else{
36924             //this.blur();
36925             this.removeClass("x-tree-selected");
36926         }
36927     },*/
36928     addClass : function(cls){
36929         if(this.elRow){
36930             Roo.fly(this.elRow).addClass(cls);
36931         }
36932         
36933     },
36934     
36935     
36936     removeClass : function(cls){
36937         if(this.elRow){
36938             Roo.fly(this.elRow).removeClass(cls);
36939         }
36940     }
36941
36942     
36943     
36944 });//<Script type="text/javascript">
36945
36946 /*
36947  * Based on:
36948  * Ext JS Library 1.1.1
36949  * Copyright(c) 2006-2007, Ext JS, LLC.
36950  *
36951  * Originally Released Under LGPL - original licence link has changed is not relivant.
36952  *
36953  * Fork - LGPL
36954  * <script type="text/javascript">
36955  */
36956  
36957
36958 /**
36959  * @class Roo.tree.ColumnTree
36960  * @extends Roo.data.TreePanel
36961  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36962  * @cfg {int} borderWidth  compined right/left border allowance
36963  * @constructor
36964  * @param {String/HTMLElement/Element} el The container element
36965  * @param {Object} config
36966  */
36967 Roo.tree.ColumnTree =  function(el, config)
36968 {
36969    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36970    this.addEvents({
36971         /**
36972         * @event resize
36973         * Fire this event on a container when it resizes
36974         * @param {int} w Width
36975         * @param {int} h Height
36976         */
36977        "resize" : true
36978     });
36979     this.on('resize', this.onResize, this);
36980 };
36981
36982 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36983     //lines:false,
36984     
36985     
36986     borderWidth: Roo.isBorderBox ? 0 : 2, 
36987     headEls : false,
36988     
36989     render : function(){
36990         // add the header.....
36991        
36992         Roo.tree.ColumnTree.superclass.render.apply(this);
36993         
36994         this.el.addClass('x-column-tree');
36995         
36996         this.headers = this.el.createChild(
36997             {cls:'x-tree-headers'},this.innerCt.dom);
36998    
36999         var cols = this.columns, c;
37000         var totalWidth = 0;
37001         this.headEls = [];
37002         var  len = cols.length;
37003         for(var i = 0; i < len; i++){
37004              c = cols[i];
37005              totalWidth += c.width;
37006             this.headEls.push(this.headers.createChild({
37007                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37008                  cn: {
37009                      cls:'x-tree-hd-text',
37010                      html: c.header
37011                  },
37012                  style:'width:'+(c.width-this.borderWidth)+'px;'
37013              }));
37014         }
37015         this.headers.createChild({cls:'x-clear'});
37016         // prevent floats from wrapping when clipped
37017         this.headers.setWidth(totalWidth);
37018         //this.innerCt.setWidth(totalWidth);
37019         this.innerCt.setStyle({ overflow: 'auto' });
37020         this.onResize(this.width, this.height);
37021              
37022         
37023     },
37024     onResize : function(w,h)
37025     {
37026         this.height = h;
37027         this.width = w;
37028         // resize cols..
37029         this.innerCt.setWidth(this.width);
37030         this.innerCt.setHeight(this.height-20);
37031         
37032         // headers...
37033         var cols = this.columns, c;
37034         var totalWidth = 0;
37035         var expEl = false;
37036         var len = cols.length;
37037         for(var i = 0; i < len; i++){
37038             c = cols[i];
37039             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37040                 // it's the expander..
37041                 expEl  = this.headEls[i];
37042                 continue;
37043             }
37044             totalWidth += c.width;
37045             
37046         }
37047         if (expEl) {
37048             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37049         }
37050         this.headers.setWidth(w-20);
37051
37052         
37053         
37054         
37055     }
37056 });
37057 /*
37058  * Based on:
37059  * Ext JS Library 1.1.1
37060  * Copyright(c) 2006-2007, Ext JS, LLC.
37061  *
37062  * Originally Released Under LGPL - original licence link has changed is not relivant.
37063  *
37064  * Fork - LGPL
37065  * <script type="text/javascript">
37066  */
37067  
37068 /**
37069  * @class Roo.menu.Menu
37070  * @extends Roo.util.Observable
37071  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37072  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37073  * @constructor
37074  * Creates a new Menu
37075  * @param {Object} config Configuration options
37076  */
37077 Roo.menu.Menu = function(config){
37078     Roo.apply(this, config);
37079     this.id = this.id || Roo.id();
37080     this.addEvents({
37081         /**
37082          * @event beforeshow
37083          * Fires before this menu is displayed
37084          * @param {Roo.menu.Menu} this
37085          */
37086         beforeshow : true,
37087         /**
37088          * @event beforehide
37089          * Fires before this menu is hidden
37090          * @param {Roo.menu.Menu} this
37091          */
37092         beforehide : true,
37093         /**
37094          * @event show
37095          * Fires after this menu is displayed
37096          * @param {Roo.menu.Menu} this
37097          */
37098         show : true,
37099         /**
37100          * @event hide
37101          * Fires after this menu is hidden
37102          * @param {Roo.menu.Menu} this
37103          */
37104         hide : true,
37105         /**
37106          * @event click
37107          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37108          * @param {Roo.menu.Menu} this
37109          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37110          * @param {Roo.EventObject} e
37111          */
37112         click : true,
37113         /**
37114          * @event mouseover
37115          * Fires when the mouse is hovering over this menu
37116          * @param {Roo.menu.Menu} this
37117          * @param {Roo.EventObject} e
37118          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37119          */
37120         mouseover : true,
37121         /**
37122          * @event mouseout
37123          * Fires when the mouse exits this menu
37124          * @param {Roo.menu.Menu} this
37125          * @param {Roo.EventObject} e
37126          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37127          */
37128         mouseout : true,
37129         /**
37130          * @event itemclick
37131          * Fires when a menu item contained in this menu is clicked
37132          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37133          * @param {Roo.EventObject} e
37134          */
37135         itemclick: true
37136     });
37137     if (this.registerMenu) {
37138         Roo.menu.MenuMgr.register(this);
37139     }
37140     
37141     var mis = this.items;
37142     this.items = new Roo.util.MixedCollection();
37143     if(mis){
37144         this.add.apply(this, mis);
37145     }
37146 };
37147
37148 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37149     /**
37150      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37151      */
37152     minWidth : 120,
37153     /**
37154      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37155      * for bottom-right shadow (defaults to "sides")
37156      */
37157     shadow : "sides",
37158     /**
37159      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37160      * this menu (defaults to "tl-tr?")
37161      */
37162     subMenuAlign : "tl-tr?",
37163     /**
37164      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37165      * relative to its element of origin (defaults to "tl-bl?")
37166      */
37167     defaultAlign : "tl-bl?",
37168     /**
37169      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37170      */
37171     allowOtherMenus : false,
37172     /**
37173      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37174      */
37175     registerMenu : true,
37176
37177     hidden:true,
37178
37179     // private
37180     render : function(){
37181         if(this.el){
37182             return;
37183         }
37184         var el = this.el = new Roo.Layer({
37185             cls: "x-menu",
37186             shadow:this.shadow,
37187             constrain: false,
37188             parentEl: this.parentEl || document.body,
37189             zindex:15000
37190         });
37191
37192         this.keyNav = new Roo.menu.MenuNav(this);
37193
37194         if(this.plain){
37195             el.addClass("x-menu-plain");
37196         }
37197         if(this.cls){
37198             el.addClass(this.cls);
37199         }
37200         // generic focus element
37201         this.focusEl = el.createChild({
37202             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37203         });
37204         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37205         //disabling touch- as it's causing issues ..
37206         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37207         ul.on('click'   , this.onClick, this);
37208         
37209         
37210         ul.on("mouseover", this.onMouseOver, this);
37211         ul.on("mouseout", this.onMouseOut, this);
37212         this.items.each(function(item){
37213             if (item.hidden) {
37214                 return;
37215             }
37216             
37217             var li = document.createElement("li");
37218             li.className = "x-menu-list-item";
37219             ul.dom.appendChild(li);
37220             item.render(li, this);
37221         }, this);
37222         this.ul = ul;
37223         this.autoWidth();
37224     },
37225
37226     // private
37227     autoWidth : function(){
37228         var el = this.el, ul = this.ul;
37229         if(!el){
37230             return;
37231         }
37232         var w = this.width;
37233         if(w){
37234             el.setWidth(w);
37235         }else if(Roo.isIE){
37236             el.setWidth(this.minWidth);
37237             var t = el.dom.offsetWidth; // force recalc
37238             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37239         }
37240     },
37241
37242     // private
37243     delayAutoWidth : function(){
37244         if(this.rendered){
37245             if(!this.awTask){
37246                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37247             }
37248             this.awTask.delay(20);
37249         }
37250     },
37251
37252     // private
37253     findTargetItem : function(e){
37254         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37255         if(t && t.menuItemId){
37256             return this.items.get(t.menuItemId);
37257         }
37258     },
37259
37260     // private
37261     onClick : function(e){
37262         Roo.log("menu.onClick");
37263         var t = this.findTargetItem(e);
37264         if(!t){
37265             return;
37266         }
37267         Roo.log(e);
37268         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37269             if(t == this.activeItem && t.shouldDeactivate(e)){
37270                 this.activeItem.deactivate();
37271                 delete this.activeItem;
37272                 return;
37273             }
37274             if(t.canActivate){
37275                 this.setActiveItem(t, true);
37276             }
37277             return;
37278             
37279             
37280         }
37281         
37282         t.onClick(e);
37283         this.fireEvent("click", this, t, e);
37284     },
37285
37286     // private
37287     setActiveItem : function(item, autoExpand){
37288         if(item != this.activeItem){
37289             if(this.activeItem){
37290                 this.activeItem.deactivate();
37291             }
37292             this.activeItem = item;
37293             item.activate(autoExpand);
37294         }else if(autoExpand){
37295             item.expandMenu();
37296         }
37297     },
37298
37299     // private
37300     tryActivate : function(start, step){
37301         var items = this.items;
37302         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37303             var item = items.get(i);
37304             if(!item.disabled && item.canActivate){
37305                 this.setActiveItem(item, false);
37306                 return item;
37307             }
37308         }
37309         return false;
37310     },
37311
37312     // private
37313     onMouseOver : function(e){
37314         var t;
37315         if(t = this.findTargetItem(e)){
37316             if(t.canActivate && !t.disabled){
37317                 this.setActiveItem(t, true);
37318             }
37319         }
37320         this.fireEvent("mouseover", this, e, t);
37321     },
37322
37323     // private
37324     onMouseOut : function(e){
37325         var t;
37326         if(t = this.findTargetItem(e)){
37327             if(t == this.activeItem && t.shouldDeactivate(e)){
37328                 this.activeItem.deactivate();
37329                 delete this.activeItem;
37330             }
37331         }
37332         this.fireEvent("mouseout", this, e, t);
37333     },
37334
37335     /**
37336      * Read-only.  Returns true if the menu is currently displayed, else false.
37337      * @type Boolean
37338      */
37339     isVisible : function(){
37340         return this.el && !this.hidden;
37341     },
37342
37343     /**
37344      * Displays this menu relative to another element
37345      * @param {String/HTMLElement/Roo.Element} element The element to align to
37346      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37347      * the element (defaults to this.defaultAlign)
37348      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37349      */
37350     show : function(el, pos, parentMenu){
37351         this.parentMenu = parentMenu;
37352         if(!this.el){
37353             this.render();
37354         }
37355         this.fireEvent("beforeshow", this);
37356         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37357     },
37358
37359     /**
37360      * Displays this menu at a specific xy position
37361      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37362      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37363      */
37364     showAt : function(xy, parentMenu, /* private: */_e){
37365         this.parentMenu = parentMenu;
37366         if(!this.el){
37367             this.render();
37368         }
37369         if(_e !== false){
37370             this.fireEvent("beforeshow", this);
37371             xy = this.el.adjustForConstraints(xy);
37372         }
37373         this.el.setXY(xy);
37374         this.el.show();
37375         this.hidden = false;
37376         this.focus();
37377         this.fireEvent("show", this);
37378     },
37379
37380     focus : function(){
37381         if(!this.hidden){
37382             this.doFocus.defer(50, this);
37383         }
37384     },
37385
37386     doFocus : function(){
37387         if(!this.hidden){
37388             this.focusEl.focus();
37389         }
37390     },
37391
37392     /**
37393      * Hides this menu and optionally all parent menus
37394      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37395      */
37396     hide : function(deep){
37397         if(this.el && this.isVisible()){
37398             this.fireEvent("beforehide", this);
37399             if(this.activeItem){
37400                 this.activeItem.deactivate();
37401                 this.activeItem = null;
37402             }
37403             this.el.hide();
37404             this.hidden = true;
37405             this.fireEvent("hide", this);
37406         }
37407         if(deep === true && this.parentMenu){
37408             this.parentMenu.hide(true);
37409         }
37410     },
37411
37412     /**
37413      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37414      * Any of the following are valid:
37415      * <ul>
37416      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37417      * <li>An HTMLElement object which will be converted to a menu item</li>
37418      * <li>A menu item config object that will be created as a new menu item</li>
37419      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37420      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37421      * </ul>
37422      * Usage:
37423      * <pre><code>
37424 // Create the menu
37425 var menu = new Roo.menu.Menu();
37426
37427 // Create a menu item to add by reference
37428 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37429
37430 // Add a bunch of items at once using different methods.
37431 // Only the last item added will be returned.
37432 var item = menu.add(
37433     menuItem,                // add existing item by ref
37434     'Dynamic Item',          // new TextItem
37435     '-',                     // new separator
37436     { text: 'Config Item' }  // new item by config
37437 );
37438 </code></pre>
37439      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37440      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37441      */
37442     add : function(){
37443         var a = arguments, l = a.length, item;
37444         for(var i = 0; i < l; i++){
37445             var el = a[i];
37446             if ((typeof(el) == "object") && el.xtype && el.xns) {
37447                 el = Roo.factory(el, Roo.menu);
37448             }
37449             
37450             if(el.render){ // some kind of Item
37451                 item = this.addItem(el);
37452             }else if(typeof el == "string"){ // string
37453                 if(el == "separator" || el == "-"){
37454                     item = this.addSeparator();
37455                 }else{
37456                     item = this.addText(el);
37457                 }
37458             }else if(el.tagName || el.el){ // element
37459                 item = this.addElement(el);
37460             }else if(typeof el == "object"){ // must be menu item config?
37461                 item = this.addMenuItem(el);
37462             }
37463         }
37464         return item;
37465     },
37466
37467     /**
37468      * Returns this menu's underlying {@link Roo.Element} object
37469      * @return {Roo.Element} The element
37470      */
37471     getEl : function(){
37472         if(!this.el){
37473             this.render();
37474         }
37475         return this.el;
37476     },
37477
37478     /**
37479      * Adds a separator bar to the menu
37480      * @return {Roo.menu.Item} The menu item that was added
37481      */
37482     addSeparator : function(){
37483         return this.addItem(new Roo.menu.Separator());
37484     },
37485
37486     /**
37487      * Adds an {@link Roo.Element} object to the menu
37488      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37489      * @return {Roo.menu.Item} The menu item that was added
37490      */
37491     addElement : function(el){
37492         return this.addItem(new Roo.menu.BaseItem(el));
37493     },
37494
37495     /**
37496      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37497      * @param {Roo.menu.Item} item The menu item to add
37498      * @return {Roo.menu.Item} The menu item that was added
37499      */
37500     addItem : function(item){
37501         this.items.add(item);
37502         if(this.ul){
37503             var li = document.createElement("li");
37504             li.className = "x-menu-list-item";
37505             this.ul.dom.appendChild(li);
37506             item.render(li, this);
37507             this.delayAutoWidth();
37508         }
37509         return item;
37510     },
37511
37512     /**
37513      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37514      * @param {Object} config A MenuItem config object
37515      * @return {Roo.menu.Item} The menu item that was added
37516      */
37517     addMenuItem : function(config){
37518         if(!(config instanceof Roo.menu.Item)){
37519             if(typeof config.checked == "boolean"){ // must be check menu item config?
37520                 config = new Roo.menu.CheckItem(config);
37521             }else{
37522                 config = new Roo.menu.Item(config);
37523             }
37524         }
37525         return this.addItem(config);
37526     },
37527
37528     /**
37529      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37530      * @param {String} text The text to display in the menu item
37531      * @return {Roo.menu.Item} The menu item that was added
37532      */
37533     addText : function(text){
37534         return this.addItem(new Roo.menu.TextItem({ text : text }));
37535     },
37536
37537     /**
37538      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37539      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37540      * @param {Roo.menu.Item} item The menu item to add
37541      * @return {Roo.menu.Item} The menu item that was added
37542      */
37543     insert : function(index, item){
37544         this.items.insert(index, item);
37545         if(this.ul){
37546             var li = document.createElement("li");
37547             li.className = "x-menu-list-item";
37548             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37549             item.render(li, this);
37550             this.delayAutoWidth();
37551         }
37552         return item;
37553     },
37554
37555     /**
37556      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37557      * @param {Roo.menu.Item} item The menu item to remove
37558      */
37559     remove : function(item){
37560         this.items.removeKey(item.id);
37561         item.destroy();
37562     },
37563
37564     /**
37565      * Removes and destroys all items in the menu
37566      */
37567     removeAll : function(){
37568         var f;
37569         while(f = this.items.first()){
37570             this.remove(f);
37571         }
37572     }
37573 });
37574
37575 // MenuNav is a private utility class used internally by the Menu
37576 Roo.menu.MenuNav = function(menu){
37577     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37578     this.scope = this.menu = menu;
37579 };
37580
37581 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37582     doRelay : function(e, h){
37583         var k = e.getKey();
37584         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37585             this.menu.tryActivate(0, 1);
37586             return false;
37587         }
37588         return h.call(this.scope || this, e, this.menu);
37589     },
37590
37591     up : function(e, m){
37592         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37593             m.tryActivate(m.items.length-1, -1);
37594         }
37595     },
37596
37597     down : function(e, m){
37598         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37599             m.tryActivate(0, 1);
37600         }
37601     },
37602
37603     right : function(e, m){
37604         if(m.activeItem){
37605             m.activeItem.expandMenu(true);
37606         }
37607     },
37608
37609     left : function(e, m){
37610         m.hide();
37611         if(m.parentMenu && m.parentMenu.activeItem){
37612             m.parentMenu.activeItem.activate();
37613         }
37614     },
37615
37616     enter : function(e, m){
37617         if(m.activeItem){
37618             e.stopPropagation();
37619             m.activeItem.onClick(e);
37620             m.fireEvent("click", this, m.activeItem);
37621             return true;
37622         }
37623     }
37624 });/*
37625  * Based on:
37626  * Ext JS Library 1.1.1
37627  * Copyright(c) 2006-2007, Ext JS, LLC.
37628  *
37629  * Originally Released Under LGPL - original licence link has changed is not relivant.
37630  *
37631  * Fork - LGPL
37632  * <script type="text/javascript">
37633  */
37634  
37635 /**
37636  * @class Roo.menu.MenuMgr
37637  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37638  * @singleton
37639  */
37640 Roo.menu.MenuMgr = function(){
37641    var menus, active, groups = {}, attached = false, lastShow = new Date();
37642
37643    // private - called when first menu is created
37644    function init(){
37645        menus = {};
37646        active = new Roo.util.MixedCollection();
37647        Roo.get(document).addKeyListener(27, function(){
37648            if(active.length > 0){
37649                hideAll();
37650            }
37651        });
37652    }
37653
37654    // private
37655    function hideAll(){
37656        if(active && active.length > 0){
37657            var c = active.clone();
37658            c.each(function(m){
37659                m.hide();
37660            });
37661        }
37662    }
37663
37664    // private
37665    function onHide(m){
37666        active.remove(m);
37667        if(active.length < 1){
37668            Roo.get(document).un("mousedown", onMouseDown);
37669            attached = false;
37670        }
37671    }
37672
37673    // private
37674    function onShow(m){
37675        var last = active.last();
37676        lastShow = new Date();
37677        active.add(m);
37678        if(!attached){
37679            Roo.get(document).on("mousedown", onMouseDown);
37680            attached = true;
37681        }
37682        if(m.parentMenu){
37683           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37684           m.parentMenu.activeChild = m;
37685        }else if(last && last.isVisible()){
37686           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37687        }
37688    }
37689
37690    // private
37691    function onBeforeHide(m){
37692        if(m.activeChild){
37693            m.activeChild.hide();
37694        }
37695        if(m.autoHideTimer){
37696            clearTimeout(m.autoHideTimer);
37697            delete m.autoHideTimer;
37698        }
37699    }
37700
37701    // private
37702    function onBeforeShow(m){
37703        var pm = m.parentMenu;
37704        if(!pm && !m.allowOtherMenus){
37705            hideAll();
37706        }else if(pm && pm.activeChild && active != m){
37707            pm.activeChild.hide();
37708        }
37709    }
37710
37711    // private
37712    function onMouseDown(e){
37713        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37714            hideAll();
37715        }
37716    }
37717
37718    // private
37719    function onBeforeCheck(mi, state){
37720        if(state){
37721            var g = groups[mi.group];
37722            for(var i = 0, l = g.length; i < l; i++){
37723                if(g[i] != mi){
37724                    g[i].setChecked(false);
37725                }
37726            }
37727        }
37728    }
37729
37730    return {
37731
37732        /**
37733         * Hides all menus that are currently visible
37734         */
37735        hideAll : function(){
37736             hideAll();  
37737        },
37738
37739        // private
37740        register : function(menu){
37741            if(!menus){
37742                init();
37743            }
37744            menus[menu.id] = menu;
37745            menu.on("beforehide", onBeforeHide);
37746            menu.on("hide", onHide);
37747            menu.on("beforeshow", onBeforeShow);
37748            menu.on("show", onShow);
37749            var g = menu.group;
37750            if(g && menu.events["checkchange"]){
37751                if(!groups[g]){
37752                    groups[g] = [];
37753                }
37754                groups[g].push(menu);
37755                menu.on("checkchange", onCheck);
37756            }
37757        },
37758
37759         /**
37760          * Returns a {@link Roo.menu.Menu} object
37761          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37762          * be used to generate and return a new Menu instance.
37763          */
37764        get : function(menu){
37765            if(typeof menu == "string"){ // menu id
37766                return menus[menu];
37767            }else if(menu.events){  // menu instance
37768                return menu;
37769            }else if(typeof menu.length == 'number'){ // array of menu items?
37770                return new Roo.menu.Menu({items:menu});
37771            }else{ // otherwise, must be a config
37772                return new Roo.menu.Menu(menu);
37773            }
37774        },
37775
37776        // private
37777        unregister : function(menu){
37778            delete menus[menu.id];
37779            menu.un("beforehide", onBeforeHide);
37780            menu.un("hide", onHide);
37781            menu.un("beforeshow", onBeforeShow);
37782            menu.un("show", onShow);
37783            var g = menu.group;
37784            if(g && menu.events["checkchange"]){
37785                groups[g].remove(menu);
37786                menu.un("checkchange", onCheck);
37787            }
37788        },
37789
37790        // private
37791        registerCheckable : function(menuItem){
37792            var g = menuItem.group;
37793            if(g){
37794                if(!groups[g]){
37795                    groups[g] = [];
37796                }
37797                groups[g].push(menuItem);
37798                menuItem.on("beforecheckchange", onBeforeCheck);
37799            }
37800        },
37801
37802        // private
37803        unregisterCheckable : function(menuItem){
37804            var g = menuItem.group;
37805            if(g){
37806                groups[g].remove(menuItem);
37807                menuItem.un("beforecheckchange", onBeforeCheck);
37808            }
37809        }
37810    };
37811 }();/*
37812  * Based on:
37813  * Ext JS Library 1.1.1
37814  * Copyright(c) 2006-2007, Ext JS, LLC.
37815  *
37816  * Originally Released Under LGPL - original licence link has changed is not relivant.
37817  *
37818  * Fork - LGPL
37819  * <script type="text/javascript">
37820  */
37821  
37822
37823 /**
37824  * @class Roo.menu.BaseItem
37825  * @extends Roo.Component
37826  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37827  * management and base configuration options shared by all menu components.
37828  * @constructor
37829  * Creates a new BaseItem
37830  * @param {Object} config Configuration options
37831  */
37832 Roo.menu.BaseItem = function(config){
37833     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37834
37835     this.addEvents({
37836         /**
37837          * @event click
37838          * Fires when this item is clicked
37839          * @param {Roo.menu.BaseItem} this
37840          * @param {Roo.EventObject} e
37841          */
37842         click: true,
37843         /**
37844          * @event activate
37845          * Fires when this item is activated
37846          * @param {Roo.menu.BaseItem} this
37847          */
37848         activate : true,
37849         /**
37850          * @event deactivate
37851          * Fires when this item is deactivated
37852          * @param {Roo.menu.BaseItem} this
37853          */
37854         deactivate : true
37855     });
37856
37857     if(this.handler){
37858         this.on("click", this.handler, this.scope, true);
37859     }
37860 };
37861
37862 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37863     /**
37864      * @cfg {Function} handler
37865      * A function that will handle the click event of this menu item (defaults to undefined)
37866      */
37867     /**
37868      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37869      */
37870     canActivate : false,
37871     
37872      /**
37873      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37874      */
37875     hidden: false,
37876     
37877     /**
37878      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37879      */
37880     activeClass : "x-menu-item-active",
37881     /**
37882      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37883      */
37884     hideOnClick : true,
37885     /**
37886      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37887      */
37888     hideDelay : 100,
37889
37890     // private
37891     ctype: "Roo.menu.BaseItem",
37892
37893     // private
37894     actionMode : "container",
37895
37896     // private
37897     render : function(container, parentMenu){
37898         this.parentMenu = parentMenu;
37899         Roo.menu.BaseItem.superclass.render.call(this, container);
37900         this.container.menuItemId = this.id;
37901     },
37902
37903     // private
37904     onRender : function(container, position){
37905         this.el = Roo.get(this.el);
37906         container.dom.appendChild(this.el.dom);
37907     },
37908
37909     // private
37910     onClick : function(e){
37911         if(!this.disabled && this.fireEvent("click", this, e) !== false
37912                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37913             this.handleClick(e);
37914         }else{
37915             e.stopEvent();
37916         }
37917     },
37918
37919     // private
37920     activate : function(){
37921         if(this.disabled){
37922             return false;
37923         }
37924         var li = this.container;
37925         li.addClass(this.activeClass);
37926         this.region = li.getRegion().adjust(2, 2, -2, -2);
37927         this.fireEvent("activate", this);
37928         return true;
37929     },
37930
37931     // private
37932     deactivate : function(){
37933         this.container.removeClass(this.activeClass);
37934         this.fireEvent("deactivate", this);
37935     },
37936
37937     // private
37938     shouldDeactivate : function(e){
37939         return !this.region || !this.region.contains(e.getPoint());
37940     },
37941
37942     // private
37943     handleClick : function(e){
37944         if(this.hideOnClick){
37945             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37946         }
37947     },
37948
37949     // private
37950     expandMenu : function(autoActivate){
37951         // do nothing
37952     },
37953
37954     // private
37955     hideMenu : function(){
37956         // do nothing
37957     }
37958 });/*
37959  * Based on:
37960  * Ext JS Library 1.1.1
37961  * Copyright(c) 2006-2007, Ext JS, LLC.
37962  *
37963  * Originally Released Under LGPL - original licence link has changed is not relivant.
37964  *
37965  * Fork - LGPL
37966  * <script type="text/javascript">
37967  */
37968  
37969 /**
37970  * @class Roo.menu.Adapter
37971  * @extends Roo.menu.BaseItem
37972  * 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.
37973  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37974  * @constructor
37975  * Creates a new Adapter
37976  * @param {Object} config Configuration options
37977  */
37978 Roo.menu.Adapter = function(component, config){
37979     Roo.menu.Adapter.superclass.constructor.call(this, config);
37980     this.component = component;
37981 };
37982 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37983     // private
37984     canActivate : true,
37985
37986     // private
37987     onRender : function(container, position){
37988         this.component.render(container);
37989         this.el = this.component.getEl();
37990     },
37991
37992     // private
37993     activate : function(){
37994         if(this.disabled){
37995             return false;
37996         }
37997         this.component.focus();
37998         this.fireEvent("activate", this);
37999         return true;
38000     },
38001
38002     // private
38003     deactivate : function(){
38004         this.fireEvent("deactivate", this);
38005     },
38006
38007     // private
38008     disable : function(){
38009         this.component.disable();
38010         Roo.menu.Adapter.superclass.disable.call(this);
38011     },
38012
38013     // private
38014     enable : function(){
38015         this.component.enable();
38016         Roo.menu.Adapter.superclass.enable.call(this);
38017     }
38018 });/*
38019  * Based on:
38020  * Ext JS Library 1.1.1
38021  * Copyright(c) 2006-2007, Ext JS, LLC.
38022  *
38023  * Originally Released Under LGPL - original licence link has changed is not relivant.
38024  *
38025  * Fork - LGPL
38026  * <script type="text/javascript">
38027  */
38028
38029 /**
38030  * @class Roo.menu.TextItem
38031  * @extends Roo.menu.BaseItem
38032  * Adds a static text string to a menu, usually used as either a heading or group separator.
38033  * Note: old style constructor with text is still supported.
38034  * 
38035  * @constructor
38036  * Creates a new TextItem
38037  * @param {Object} cfg Configuration
38038  */
38039 Roo.menu.TextItem = function(cfg){
38040     if (typeof(cfg) == 'string') {
38041         this.text = cfg;
38042     } else {
38043         Roo.apply(this,cfg);
38044     }
38045     
38046     Roo.menu.TextItem.superclass.constructor.call(this);
38047 };
38048
38049 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38050     /**
38051      * @cfg {Boolean} text Text to show on item.
38052      */
38053     text : '',
38054     
38055     /**
38056      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38057      */
38058     hideOnClick : false,
38059     /**
38060      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38061      */
38062     itemCls : "x-menu-text",
38063
38064     // private
38065     onRender : function(){
38066         var s = document.createElement("span");
38067         s.className = this.itemCls;
38068         s.innerHTML = this.text;
38069         this.el = s;
38070         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38071     }
38072 });/*
38073  * Based on:
38074  * Ext JS Library 1.1.1
38075  * Copyright(c) 2006-2007, Ext JS, LLC.
38076  *
38077  * Originally Released Under LGPL - original licence link has changed is not relivant.
38078  *
38079  * Fork - LGPL
38080  * <script type="text/javascript">
38081  */
38082
38083 /**
38084  * @class Roo.menu.Separator
38085  * @extends Roo.menu.BaseItem
38086  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38087  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38088  * @constructor
38089  * @param {Object} config Configuration options
38090  */
38091 Roo.menu.Separator = function(config){
38092     Roo.menu.Separator.superclass.constructor.call(this, config);
38093 };
38094
38095 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38096     /**
38097      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38098      */
38099     itemCls : "x-menu-sep",
38100     /**
38101      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38102      */
38103     hideOnClick : false,
38104
38105     // private
38106     onRender : function(li){
38107         var s = document.createElement("span");
38108         s.className = this.itemCls;
38109         s.innerHTML = "&#160;";
38110         this.el = s;
38111         li.addClass("x-menu-sep-li");
38112         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38113     }
38114 });/*
38115  * Based on:
38116  * Ext JS Library 1.1.1
38117  * Copyright(c) 2006-2007, Ext JS, LLC.
38118  *
38119  * Originally Released Under LGPL - original licence link has changed is not relivant.
38120  *
38121  * Fork - LGPL
38122  * <script type="text/javascript">
38123  */
38124 /**
38125  * @class Roo.menu.Item
38126  * @extends Roo.menu.BaseItem
38127  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38128  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38129  * activation and click handling.
38130  * @constructor
38131  * Creates a new Item
38132  * @param {Object} config Configuration options
38133  */
38134 Roo.menu.Item = function(config){
38135     Roo.menu.Item.superclass.constructor.call(this, config);
38136     if(this.menu){
38137         this.menu = Roo.menu.MenuMgr.get(this.menu);
38138     }
38139 };
38140 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38141     
38142     /**
38143      * @cfg {String} text
38144      * The text to show on the menu item.
38145      */
38146     text: '',
38147      /**
38148      * @cfg {String} HTML to render in menu
38149      * The text to show on the menu item (HTML version).
38150      */
38151     html: '',
38152     /**
38153      * @cfg {String} icon
38154      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38155      */
38156     icon: undefined,
38157     /**
38158      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38159      */
38160     itemCls : "x-menu-item",
38161     /**
38162      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38163      */
38164     canActivate : true,
38165     /**
38166      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38167      */
38168     showDelay: 200,
38169     // doc'd in BaseItem
38170     hideDelay: 200,
38171
38172     // private
38173     ctype: "Roo.menu.Item",
38174     
38175     // private
38176     onRender : function(container, position){
38177         var el = document.createElement("a");
38178         el.hideFocus = true;
38179         el.unselectable = "on";
38180         el.href = this.href || "#";
38181         if(this.hrefTarget){
38182             el.target = this.hrefTarget;
38183         }
38184         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38185         
38186         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38187         
38188         el.innerHTML = String.format(
38189                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38190                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38191         this.el = el;
38192         Roo.menu.Item.superclass.onRender.call(this, container, position);
38193     },
38194
38195     /**
38196      * Sets the text to display in this menu item
38197      * @param {String} text The text to display
38198      * @param {Boolean} isHTML true to indicate text is pure html.
38199      */
38200     setText : function(text, isHTML){
38201         if (isHTML) {
38202             this.html = text;
38203         } else {
38204             this.text = text;
38205             this.html = '';
38206         }
38207         if(this.rendered){
38208             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38209      
38210             this.el.update(String.format(
38211                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38212                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38213             this.parentMenu.autoWidth();
38214         }
38215     },
38216
38217     // private
38218     handleClick : function(e){
38219         if(!this.href){ // if no link defined, stop the event automatically
38220             e.stopEvent();
38221         }
38222         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38223     },
38224
38225     // private
38226     activate : function(autoExpand){
38227         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38228             this.focus();
38229             if(autoExpand){
38230                 this.expandMenu();
38231             }
38232         }
38233         return true;
38234     },
38235
38236     // private
38237     shouldDeactivate : function(e){
38238         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38239             if(this.menu && this.menu.isVisible()){
38240                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38241             }
38242             return true;
38243         }
38244         return false;
38245     },
38246
38247     // private
38248     deactivate : function(){
38249         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38250         this.hideMenu();
38251     },
38252
38253     // private
38254     expandMenu : function(autoActivate){
38255         if(!this.disabled && this.menu){
38256             clearTimeout(this.hideTimer);
38257             delete this.hideTimer;
38258             if(!this.menu.isVisible() && !this.showTimer){
38259                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38260             }else if (this.menu.isVisible() && autoActivate){
38261                 this.menu.tryActivate(0, 1);
38262             }
38263         }
38264     },
38265
38266     // private
38267     deferExpand : function(autoActivate){
38268         delete this.showTimer;
38269         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38270         if(autoActivate){
38271             this.menu.tryActivate(0, 1);
38272         }
38273     },
38274
38275     // private
38276     hideMenu : function(){
38277         clearTimeout(this.showTimer);
38278         delete this.showTimer;
38279         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38280             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38281         }
38282     },
38283
38284     // private
38285     deferHide : function(){
38286         delete this.hideTimer;
38287         this.menu.hide();
38288     }
38289 });/*
38290  * Based on:
38291  * Ext JS Library 1.1.1
38292  * Copyright(c) 2006-2007, Ext JS, LLC.
38293  *
38294  * Originally Released Under LGPL - original licence link has changed is not relivant.
38295  *
38296  * Fork - LGPL
38297  * <script type="text/javascript">
38298  */
38299  
38300 /**
38301  * @class Roo.menu.CheckItem
38302  * @extends Roo.menu.Item
38303  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38304  * @constructor
38305  * Creates a new CheckItem
38306  * @param {Object} config Configuration options
38307  */
38308 Roo.menu.CheckItem = function(config){
38309     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38310     this.addEvents({
38311         /**
38312          * @event beforecheckchange
38313          * Fires before the checked value is set, providing an opportunity to cancel if needed
38314          * @param {Roo.menu.CheckItem} this
38315          * @param {Boolean} checked The new checked value that will be set
38316          */
38317         "beforecheckchange" : true,
38318         /**
38319          * @event checkchange
38320          * Fires after the checked value has been set
38321          * @param {Roo.menu.CheckItem} this
38322          * @param {Boolean} checked The checked value that was set
38323          */
38324         "checkchange" : true
38325     });
38326     if(this.checkHandler){
38327         this.on('checkchange', this.checkHandler, this.scope);
38328     }
38329 };
38330 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38331     /**
38332      * @cfg {String} group
38333      * All check items with the same group name will automatically be grouped into a single-select
38334      * radio button group (defaults to '')
38335      */
38336     /**
38337      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38338      */
38339     itemCls : "x-menu-item x-menu-check-item",
38340     /**
38341      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38342      */
38343     groupClass : "x-menu-group-item",
38344
38345     /**
38346      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38347      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38348      * initialized with checked = true will be rendered as checked.
38349      */
38350     checked: false,
38351
38352     // private
38353     ctype: "Roo.menu.CheckItem",
38354
38355     // private
38356     onRender : function(c){
38357         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38358         if(this.group){
38359             this.el.addClass(this.groupClass);
38360         }
38361         Roo.menu.MenuMgr.registerCheckable(this);
38362         if(this.checked){
38363             this.checked = false;
38364             this.setChecked(true, true);
38365         }
38366     },
38367
38368     // private
38369     destroy : function(){
38370         if(this.rendered){
38371             Roo.menu.MenuMgr.unregisterCheckable(this);
38372         }
38373         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38374     },
38375
38376     /**
38377      * Set the checked state of this item
38378      * @param {Boolean} checked The new checked value
38379      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38380      */
38381     setChecked : function(state, suppressEvent){
38382         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38383             if(this.container){
38384                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38385             }
38386             this.checked = state;
38387             if(suppressEvent !== true){
38388                 this.fireEvent("checkchange", this, state);
38389             }
38390         }
38391     },
38392
38393     // private
38394     handleClick : function(e){
38395        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38396            this.setChecked(!this.checked);
38397        }
38398        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38399     }
38400 });/*
38401  * Based on:
38402  * Ext JS Library 1.1.1
38403  * Copyright(c) 2006-2007, Ext JS, LLC.
38404  *
38405  * Originally Released Under LGPL - original licence link has changed is not relivant.
38406  *
38407  * Fork - LGPL
38408  * <script type="text/javascript">
38409  */
38410  
38411 /**
38412  * @class Roo.menu.DateItem
38413  * @extends Roo.menu.Adapter
38414  * A menu item that wraps the {@link Roo.DatPicker} component.
38415  * @constructor
38416  * Creates a new DateItem
38417  * @param {Object} config Configuration options
38418  */
38419 Roo.menu.DateItem = function(config){
38420     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38421     /** The Roo.DatePicker object @type Roo.DatePicker */
38422     this.picker = this.component;
38423     this.addEvents({select: true});
38424     
38425     this.picker.on("render", function(picker){
38426         picker.getEl().swallowEvent("click");
38427         picker.container.addClass("x-menu-date-item");
38428     });
38429
38430     this.picker.on("select", this.onSelect, this);
38431 };
38432
38433 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38434     // private
38435     onSelect : function(picker, date){
38436         this.fireEvent("select", this, date, picker);
38437         Roo.menu.DateItem.superclass.handleClick.call(this);
38438     }
38439 });/*
38440  * Based on:
38441  * Ext JS Library 1.1.1
38442  * Copyright(c) 2006-2007, Ext JS, LLC.
38443  *
38444  * Originally Released Under LGPL - original licence link has changed is not relivant.
38445  *
38446  * Fork - LGPL
38447  * <script type="text/javascript">
38448  */
38449  
38450 /**
38451  * @class Roo.menu.ColorItem
38452  * @extends Roo.menu.Adapter
38453  * A menu item that wraps the {@link Roo.ColorPalette} component.
38454  * @constructor
38455  * Creates a new ColorItem
38456  * @param {Object} config Configuration options
38457  */
38458 Roo.menu.ColorItem = function(config){
38459     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38460     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38461     this.palette = this.component;
38462     this.relayEvents(this.palette, ["select"]);
38463     if(this.selectHandler){
38464         this.on('select', this.selectHandler, this.scope);
38465     }
38466 };
38467 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
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 /**
38480  * @class Roo.menu.DateMenu
38481  * @extends Roo.menu.Menu
38482  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38483  * @constructor
38484  * Creates a new DateMenu
38485  * @param {Object} config Configuration options
38486  */
38487 Roo.menu.DateMenu = function(config){
38488     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38489     this.plain = true;
38490     var di = new Roo.menu.DateItem(config);
38491     this.add(di);
38492     /**
38493      * The {@link Roo.DatePicker} instance for this DateMenu
38494      * @type DatePicker
38495      */
38496     this.picker = di.picker;
38497     /**
38498      * @event select
38499      * @param {DatePicker} picker
38500      * @param {Date} date
38501      */
38502     this.relayEvents(di, ["select"]);
38503     this.on('beforeshow', function(){
38504         if(this.picker){
38505             this.picker.hideMonthPicker(false);
38506         }
38507     }, this);
38508 };
38509 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38510     cls:'x-date-menu'
38511 });/*
38512  * Based on:
38513  * Ext JS Library 1.1.1
38514  * Copyright(c) 2006-2007, Ext JS, LLC.
38515  *
38516  * Originally Released Under LGPL - original licence link has changed is not relivant.
38517  *
38518  * Fork - LGPL
38519  * <script type="text/javascript">
38520  */
38521  
38522
38523 /**
38524  * @class Roo.menu.ColorMenu
38525  * @extends Roo.menu.Menu
38526  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38527  * @constructor
38528  * Creates a new ColorMenu
38529  * @param {Object} config Configuration options
38530  */
38531 Roo.menu.ColorMenu = function(config){
38532     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38533     this.plain = true;
38534     var ci = new Roo.menu.ColorItem(config);
38535     this.add(ci);
38536     /**
38537      * The {@link Roo.ColorPalette} instance for this ColorMenu
38538      * @type ColorPalette
38539      */
38540     this.palette = ci.palette;
38541     /**
38542      * @event select
38543      * @param {ColorPalette} palette
38544      * @param {String} color
38545      */
38546     this.relayEvents(ci, ["select"]);
38547 };
38548 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38549  * Based on:
38550  * Ext JS Library 1.1.1
38551  * Copyright(c) 2006-2007, Ext JS, LLC.
38552  *
38553  * Originally Released Under LGPL - original licence link has changed is not relivant.
38554  *
38555  * Fork - LGPL
38556  * <script type="text/javascript">
38557  */
38558  
38559 /**
38560  * @class Roo.form.Field
38561  * @extends Roo.BoxComponent
38562  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38563  * @constructor
38564  * Creates a new Field
38565  * @param {Object} config Configuration options
38566  */
38567 Roo.form.Field = function(config){
38568     Roo.form.Field.superclass.constructor.call(this, config);
38569 };
38570
38571 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38572     /**
38573      * @cfg {String} fieldLabel Label to use when rendering a form.
38574      */
38575        /**
38576      * @cfg {String} qtip Mouse over tip
38577      */
38578      
38579     /**
38580      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38581      */
38582     invalidClass : "x-form-invalid",
38583     /**
38584      * @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")
38585      */
38586     invalidText : "The value in this field is invalid",
38587     /**
38588      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38589      */
38590     focusClass : "x-form-focus",
38591     /**
38592      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38593       automatic validation (defaults to "keyup").
38594      */
38595     validationEvent : "keyup",
38596     /**
38597      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38598      */
38599     validateOnBlur : true,
38600     /**
38601      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38602      */
38603     validationDelay : 250,
38604     /**
38605      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38606      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38607      */
38608     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38609     /**
38610      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38611      */
38612     fieldClass : "x-form-field",
38613     /**
38614      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38615      *<pre>
38616 Value         Description
38617 -----------   ----------------------------------------------------------------------
38618 qtip          Display a quick tip when the user hovers over the field
38619 title         Display a default browser title attribute popup
38620 under         Add a block div beneath the field containing the error text
38621 side          Add an error icon to the right of the field with a popup on hover
38622 [element id]  Add the error text directly to the innerHTML of the specified element
38623 </pre>
38624      */
38625     msgTarget : 'qtip',
38626     /**
38627      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38628      */
38629     msgFx : 'normal',
38630
38631     /**
38632      * @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.
38633      */
38634     readOnly : false,
38635
38636     /**
38637      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38638      */
38639     disabled : false,
38640
38641     /**
38642      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38643      */
38644     inputType : undefined,
38645     
38646     /**
38647      * @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).
38648          */
38649         tabIndex : undefined,
38650         
38651     // private
38652     isFormField : true,
38653
38654     // private
38655     hasFocus : false,
38656     /**
38657      * @property {Roo.Element} fieldEl
38658      * Element Containing the rendered Field (with label etc.)
38659      */
38660     /**
38661      * @cfg {Mixed} value A value to initialize this field with.
38662      */
38663     value : undefined,
38664
38665     /**
38666      * @cfg {String} name The field's HTML name attribute.
38667      */
38668     /**
38669      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38670      */
38671     // private
38672     loadedValue : false,
38673      
38674      
38675         // private ??
38676         initComponent : function(){
38677         Roo.form.Field.superclass.initComponent.call(this);
38678         this.addEvents({
38679             /**
38680              * @event focus
38681              * Fires when this field receives input focus.
38682              * @param {Roo.form.Field} this
38683              */
38684             focus : true,
38685             /**
38686              * @event blur
38687              * Fires when this field loses input focus.
38688              * @param {Roo.form.Field} this
38689              */
38690             blur : true,
38691             /**
38692              * @event specialkey
38693              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38694              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38695              * @param {Roo.form.Field} this
38696              * @param {Roo.EventObject} e The event object
38697              */
38698             specialkey : true,
38699             /**
38700              * @event change
38701              * Fires just before the field blurs if the field value has changed.
38702              * @param {Roo.form.Field} this
38703              * @param {Mixed} newValue The new value
38704              * @param {Mixed} oldValue The original value
38705              */
38706             change : true,
38707             /**
38708              * @event invalid
38709              * Fires after the field has been marked as invalid.
38710              * @param {Roo.form.Field} this
38711              * @param {String} msg The validation message
38712              */
38713             invalid : true,
38714             /**
38715              * @event valid
38716              * Fires after the field has been validated with no errors.
38717              * @param {Roo.form.Field} this
38718              */
38719             valid : true,
38720              /**
38721              * @event keyup
38722              * Fires after the key up
38723              * @param {Roo.form.Field} this
38724              * @param {Roo.EventObject}  e The event Object
38725              */
38726             keyup : true
38727         });
38728     },
38729
38730     /**
38731      * Returns the name attribute of the field if available
38732      * @return {String} name The field name
38733      */
38734     getName: function(){
38735          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38736     },
38737
38738     // private
38739     onRender : function(ct, position){
38740         Roo.form.Field.superclass.onRender.call(this, ct, position);
38741         if(!this.el){
38742             var cfg = this.getAutoCreate();
38743             if(!cfg.name){
38744                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38745             }
38746             if (!cfg.name.length) {
38747                 delete cfg.name;
38748             }
38749             if(this.inputType){
38750                 cfg.type = this.inputType;
38751             }
38752             this.el = ct.createChild(cfg, position);
38753         }
38754         var type = this.el.dom.type;
38755         if(type){
38756             if(type == 'password'){
38757                 type = 'text';
38758             }
38759             this.el.addClass('x-form-'+type);
38760         }
38761         if(this.readOnly){
38762             this.el.dom.readOnly = true;
38763         }
38764         if(this.tabIndex !== undefined){
38765             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38766         }
38767
38768         this.el.addClass([this.fieldClass, this.cls]);
38769         this.initValue();
38770     },
38771
38772     /**
38773      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38774      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38775      * @return {Roo.form.Field} this
38776      */
38777     applyTo : function(target){
38778         this.allowDomMove = false;
38779         this.el = Roo.get(target);
38780         this.render(this.el.dom.parentNode);
38781         return this;
38782     },
38783
38784     // private
38785     initValue : function(){
38786         if(this.value !== undefined){
38787             this.setValue(this.value);
38788         }else if(this.el.dom.value.length > 0){
38789             this.setValue(this.el.dom.value);
38790         }
38791     },
38792
38793     /**
38794      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38795      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38796      */
38797     isDirty : function() {
38798         if(this.disabled) {
38799             return false;
38800         }
38801         return String(this.getValue()) !== String(this.originalValue);
38802     },
38803
38804     /**
38805      * stores the current value in loadedValue
38806      */
38807     resetHasChanged : function()
38808     {
38809         this.loadedValue = String(this.getValue());
38810     },
38811     /**
38812      * checks the current value against the 'loaded' value.
38813      * Note - will return false if 'resetHasChanged' has not been called first.
38814      */
38815     hasChanged : function()
38816     {
38817         if(this.disabled || this.readOnly) {
38818             return false;
38819         }
38820         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38821     },
38822     
38823     
38824     
38825     // private
38826     afterRender : function(){
38827         Roo.form.Field.superclass.afterRender.call(this);
38828         this.initEvents();
38829     },
38830
38831     // private
38832     fireKey : function(e){
38833         //Roo.log('field ' + e.getKey());
38834         if(e.isNavKeyPress()){
38835             this.fireEvent("specialkey", this, e);
38836         }
38837     },
38838
38839     /**
38840      * Resets the current field value to the originally loaded value and clears any validation messages
38841      */
38842     reset : function(){
38843         this.setValue(this.resetValue);
38844         this.clearInvalid();
38845     },
38846
38847     // private
38848     initEvents : function(){
38849         // safari killled keypress - so keydown is now used..
38850         this.el.on("keydown" , this.fireKey,  this);
38851         this.el.on("focus", this.onFocus,  this);
38852         this.el.on("blur", this.onBlur,  this);
38853         this.el.relayEvent('keyup', this);
38854
38855         // reference to original value for reset
38856         this.originalValue = this.getValue();
38857         this.resetValue =  this.getValue();
38858     },
38859
38860     // private
38861     onFocus : function(){
38862         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38863             this.el.addClass(this.focusClass);
38864         }
38865         if(!this.hasFocus){
38866             this.hasFocus = true;
38867             this.startValue = this.getValue();
38868             this.fireEvent("focus", this);
38869         }
38870     },
38871
38872     beforeBlur : Roo.emptyFn,
38873
38874     // private
38875     onBlur : function(){
38876         this.beforeBlur();
38877         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38878             this.el.removeClass(this.focusClass);
38879         }
38880         this.hasFocus = false;
38881         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38882             this.validate();
38883         }
38884         var v = this.getValue();
38885         if(String(v) !== String(this.startValue)){
38886             this.fireEvent('change', this, v, this.startValue);
38887         }
38888         this.fireEvent("blur", this);
38889     },
38890
38891     /**
38892      * Returns whether or not the field value is currently valid
38893      * @param {Boolean} preventMark True to disable marking the field invalid
38894      * @return {Boolean} True if the value is valid, else false
38895      */
38896     isValid : function(preventMark){
38897         if(this.disabled){
38898             return true;
38899         }
38900         var restore = this.preventMark;
38901         this.preventMark = preventMark === true;
38902         var v = this.validateValue(this.processValue(this.getRawValue()));
38903         this.preventMark = restore;
38904         return v;
38905     },
38906
38907     /**
38908      * Validates the field value
38909      * @return {Boolean} True if the value is valid, else false
38910      */
38911     validate : function(){
38912         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38913             this.clearInvalid();
38914             return true;
38915         }
38916         return false;
38917     },
38918
38919     processValue : function(value){
38920         return value;
38921     },
38922
38923     // private
38924     // Subclasses should provide the validation implementation by overriding this
38925     validateValue : function(value){
38926         return true;
38927     },
38928
38929     /**
38930      * Mark this field as invalid
38931      * @param {String} msg The validation message
38932      */
38933     markInvalid : function(msg){
38934         if(!this.rendered || this.preventMark){ // not rendered
38935             return;
38936         }
38937         
38938         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38939         
38940         obj.el.addClass(this.invalidClass);
38941         msg = msg || this.invalidText;
38942         switch(this.msgTarget){
38943             case 'qtip':
38944                 obj.el.dom.qtip = msg;
38945                 obj.el.dom.qclass = 'x-form-invalid-tip';
38946                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38947                     Roo.QuickTips.enable();
38948                 }
38949                 break;
38950             case 'title':
38951                 this.el.dom.title = msg;
38952                 break;
38953             case 'under':
38954                 if(!this.errorEl){
38955                     var elp = this.el.findParent('.x-form-element', 5, true);
38956                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38957                     this.errorEl.setWidth(elp.getWidth(true)-20);
38958                 }
38959                 this.errorEl.update(msg);
38960                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38961                 break;
38962             case 'side':
38963                 if(!this.errorIcon){
38964                     var elp = this.el.findParent('.x-form-element', 5, true);
38965                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38966                 }
38967                 this.alignErrorIcon();
38968                 this.errorIcon.dom.qtip = msg;
38969                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38970                 this.errorIcon.show();
38971                 this.on('resize', this.alignErrorIcon, this);
38972                 break;
38973             default:
38974                 var t = Roo.getDom(this.msgTarget);
38975                 t.innerHTML = msg;
38976                 t.style.display = this.msgDisplay;
38977                 break;
38978         }
38979         this.fireEvent('invalid', this, msg);
38980     },
38981
38982     // private
38983     alignErrorIcon : function(){
38984         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38985     },
38986
38987     /**
38988      * Clear any invalid styles/messages for this field
38989      */
38990     clearInvalid : function(){
38991         if(!this.rendered || this.preventMark){ // not rendered
38992             return;
38993         }
38994         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38995         
38996         obj.el.removeClass(this.invalidClass);
38997         switch(this.msgTarget){
38998             case 'qtip':
38999                 obj.el.dom.qtip = '';
39000                 break;
39001             case 'title':
39002                 this.el.dom.title = '';
39003                 break;
39004             case 'under':
39005                 if(this.errorEl){
39006                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39007                 }
39008                 break;
39009             case 'side':
39010                 if(this.errorIcon){
39011                     this.errorIcon.dom.qtip = '';
39012                     this.errorIcon.hide();
39013                     this.un('resize', this.alignErrorIcon, this);
39014                 }
39015                 break;
39016             default:
39017                 var t = Roo.getDom(this.msgTarget);
39018                 t.innerHTML = '';
39019                 t.style.display = 'none';
39020                 break;
39021         }
39022         this.fireEvent('valid', this);
39023     },
39024
39025     /**
39026      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39027      * @return {Mixed} value The field value
39028      */
39029     getRawValue : function(){
39030         var v = this.el.getValue();
39031         
39032         return v;
39033     },
39034
39035     /**
39036      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39037      * @return {Mixed} value The field value
39038      */
39039     getValue : function(){
39040         var v = this.el.getValue();
39041          
39042         return v;
39043     },
39044
39045     /**
39046      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39047      * @param {Mixed} value The value to set
39048      */
39049     setRawValue : function(v){
39050         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39051     },
39052
39053     /**
39054      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39055      * @param {Mixed} value The value to set
39056      */
39057     setValue : function(v){
39058         this.value = v;
39059         if(this.rendered){
39060             this.el.dom.value = (v === null || v === undefined ? '' : v);
39061              this.validate();
39062         }
39063     },
39064
39065     adjustSize : function(w, h){
39066         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39067         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39068         return s;
39069     },
39070
39071     adjustWidth : function(tag, w){
39072         tag = tag.toLowerCase();
39073         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39074             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39075                 if(tag == 'input'){
39076                     return w + 2;
39077                 }
39078                 if(tag == 'textarea'){
39079                     return w-2;
39080                 }
39081             }else if(Roo.isOpera){
39082                 if(tag == 'input'){
39083                     return w + 2;
39084                 }
39085                 if(tag == 'textarea'){
39086                     return w-2;
39087                 }
39088             }
39089         }
39090         return w;
39091     }
39092 });
39093
39094
39095 // anything other than normal should be considered experimental
39096 Roo.form.Field.msgFx = {
39097     normal : {
39098         show: function(msgEl, f){
39099             msgEl.setDisplayed('block');
39100         },
39101
39102         hide : function(msgEl, f){
39103             msgEl.setDisplayed(false).update('');
39104         }
39105     },
39106
39107     slide : {
39108         show: function(msgEl, f){
39109             msgEl.slideIn('t', {stopFx:true});
39110         },
39111
39112         hide : function(msgEl, f){
39113             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39114         }
39115     },
39116
39117     slideRight : {
39118         show: function(msgEl, f){
39119             msgEl.fixDisplay();
39120             msgEl.alignTo(f.el, 'tl-tr');
39121             msgEl.slideIn('l', {stopFx:true});
39122         },
39123
39124         hide : function(msgEl, f){
39125             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39126         }
39127     }
39128 };/*
39129  * Based on:
39130  * Ext JS Library 1.1.1
39131  * Copyright(c) 2006-2007, Ext JS, LLC.
39132  *
39133  * Originally Released Under LGPL - original licence link has changed is not relivant.
39134  *
39135  * Fork - LGPL
39136  * <script type="text/javascript">
39137  */
39138  
39139
39140 /**
39141  * @class Roo.form.TextField
39142  * @extends Roo.form.Field
39143  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39144  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39145  * @constructor
39146  * Creates a new TextField
39147  * @param {Object} config Configuration options
39148  */
39149 Roo.form.TextField = function(config){
39150     Roo.form.TextField.superclass.constructor.call(this, config);
39151     this.addEvents({
39152         /**
39153          * @event autosize
39154          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39155          * according to the default logic, but this event provides a hook for the developer to apply additional
39156          * logic at runtime to resize the field if needed.
39157              * @param {Roo.form.Field} this This text field
39158              * @param {Number} width The new field width
39159              */
39160         autosize : true
39161     });
39162 };
39163
39164 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39165     /**
39166      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39167      */
39168     grow : false,
39169     /**
39170      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39171      */
39172     growMin : 30,
39173     /**
39174      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39175      */
39176     growMax : 800,
39177     /**
39178      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39179      */
39180     vtype : null,
39181     /**
39182      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39183      */
39184     maskRe : null,
39185     /**
39186      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39187      */
39188     disableKeyFilter : false,
39189     /**
39190      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39191      */
39192     allowBlank : true,
39193     /**
39194      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39195      */
39196     minLength : 0,
39197     /**
39198      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39199      */
39200     maxLength : Number.MAX_VALUE,
39201     /**
39202      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39203      */
39204     minLengthText : "The minimum length for this field is {0}",
39205     /**
39206      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39207      */
39208     maxLengthText : "The maximum length for this field is {0}",
39209     /**
39210      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39211      */
39212     selectOnFocus : false,
39213     /**
39214      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39215      */
39216     blankText : "This field is required",
39217     /**
39218      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39219      * If available, this function will be called only after the basic validators all return true, and will be passed the
39220      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39221      */
39222     validator : null,
39223     /**
39224      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39225      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39226      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39227      */
39228     regex : null,
39229     /**
39230      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39231      */
39232     regexText : "",
39233     /**
39234      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39235      */
39236     emptyText : null,
39237    
39238
39239     // private
39240     initEvents : function()
39241     {
39242         if (this.emptyText) {
39243             this.el.attr('placeholder', this.emptyText);
39244         }
39245         
39246         Roo.form.TextField.superclass.initEvents.call(this);
39247         if(this.validationEvent == 'keyup'){
39248             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39249             this.el.on('keyup', this.filterValidation, this);
39250         }
39251         else if(this.validationEvent !== false){
39252             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39253         }
39254         
39255         if(this.selectOnFocus){
39256             this.on("focus", this.preFocus, this);
39257             
39258         }
39259         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39260             this.el.on("keypress", this.filterKeys, this);
39261         }
39262         if(this.grow){
39263             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39264             this.el.on("click", this.autoSize,  this);
39265         }
39266         if(this.el.is('input[type=password]') && Roo.isSafari){
39267             this.el.on('keydown', this.SafariOnKeyDown, this);
39268         }
39269     },
39270
39271     processValue : function(value){
39272         if(this.stripCharsRe){
39273             var newValue = value.replace(this.stripCharsRe, '');
39274             if(newValue !== value){
39275                 this.setRawValue(newValue);
39276                 return newValue;
39277             }
39278         }
39279         return value;
39280     },
39281
39282     filterValidation : function(e){
39283         if(!e.isNavKeyPress()){
39284             this.validationTask.delay(this.validationDelay);
39285         }
39286     },
39287
39288     // private
39289     onKeyUp : function(e){
39290         if(!e.isNavKeyPress()){
39291             this.autoSize();
39292         }
39293     },
39294
39295     /**
39296      * Resets the current field value to the originally-loaded value and clears any validation messages.
39297      *  
39298      */
39299     reset : function(){
39300         Roo.form.TextField.superclass.reset.call(this);
39301        
39302     },
39303
39304     
39305     // private
39306     preFocus : function(){
39307         
39308         if(this.selectOnFocus){
39309             this.el.dom.select();
39310         }
39311     },
39312
39313     
39314     // private
39315     filterKeys : function(e){
39316         var k = e.getKey();
39317         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39318             return;
39319         }
39320         var c = e.getCharCode(), cc = String.fromCharCode(c);
39321         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39322             return;
39323         }
39324         if(!this.maskRe.test(cc)){
39325             e.stopEvent();
39326         }
39327     },
39328
39329     setValue : function(v){
39330         
39331         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39332         
39333         this.autoSize();
39334     },
39335
39336     /**
39337      * Validates a value according to the field's validation rules and marks the field as invalid
39338      * if the validation fails
39339      * @param {Mixed} value The value to validate
39340      * @return {Boolean} True if the value is valid, else false
39341      */
39342     validateValue : function(value){
39343         if(value.length < 1)  { // if it's blank
39344              if(this.allowBlank){
39345                 this.clearInvalid();
39346                 return true;
39347              }else{
39348                 this.markInvalid(this.blankText);
39349                 return false;
39350              }
39351         }
39352         if(value.length < this.minLength){
39353             this.markInvalid(String.format(this.minLengthText, this.minLength));
39354             return false;
39355         }
39356         if(value.length > this.maxLength){
39357             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39358             return false;
39359         }
39360         if(this.vtype){
39361             var vt = Roo.form.VTypes;
39362             if(!vt[this.vtype](value, this)){
39363                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39364                 return false;
39365             }
39366         }
39367         if(typeof this.validator == "function"){
39368             var msg = this.validator(value);
39369             if(msg !== true){
39370                 this.markInvalid(msg);
39371                 return false;
39372             }
39373         }
39374         if(this.regex && !this.regex.test(value)){
39375             this.markInvalid(this.regexText);
39376             return false;
39377         }
39378         return true;
39379     },
39380
39381     /**
39382      * Selects text in this field
39383      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39384      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39385      */
39386     selectText : function(start, end){
39387         var v = this.getRawValue();
39388         if(v.length > 0){
39389             start = start === undefined ? 0 : start;
39390             end = end === undefined ? v.length : end;
39391             var d = this.el.dom;
39392             if(d.setSelectionRange){
39393                 d.setSelectionRange(start, end);
39394             }else if(d.createTextRange){
39395                 var range = d.createTextRange();
39396                 range.moveStart("character", start);
39397                 range.moveEnd("character", v.length-end);
39398                 range.select();
39399             }
39400         }
39401     },
39402
39403     /**
39404      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39405      * This only takes effect if grow = true, and fires the autosize event.
39406      */
39407     autoSize : function(){
39408         if(!this.grow || !this.rendered){
39409             return;
39410         }
39411         if(!this.metrics){
39412             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39413         }
39414         var el = this.el;
39415         var v = el.dom.value;
39416         var d = document.createElement('div');
39417         d.appendChild(document.createTextNode(v));
39418         v = d.innerHTML;
39419         d = null;
39420         v += "&#160;";
39421         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39422         this.el.setWidth(w);
39423         this.fireEvent("autosize", this, w);
39424     },
39425     
39426     // private
39427     SafariOnKeyDown : function(event)
39428     {
39429         // this is a workaround for a password hang bug on chrome/ webkit.
39430         
39431         var isSelectAll = false;
39432         
39433         if(this.el.dom.selectionEnd > 0){
39434             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39435         }
39436         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39437             event.preventDefault();
39438             this.setValue('');
39439             return;
39440         }
39441         
39442         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39443             
39444             event.preventDefault();
39445             // this is very hacky as keydown always get's upper case.
39446             
39447             var cc = String.fromCharCode(event.getCharCode());
39448             
39449             
39450             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39451             
39452         }
39453         
39454         
39455     }
39456 });/*
39457  * Based on:
39458  * Ext JS Library 1.1.1
39459  * Copyright(c) 2006-2007, Ext JS, LLC.
39460  *
39461  * Originally Released Under LGPL - original licence link has changed is not relivant.
39462  *
39463  * Fork - LGPL
39464  * <script type="text/javascript">
39465  */
39466  
39467 /**
39468  * @class Roo.form.Hidden
39469  * @extends Roo.form.TextField
39470  * Simple Hidden element used on forms 
39471  * 
39472  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39473  * 
39474  * @constructor
39475  * Creates a new Hidden form element.
39476  * @param {Object} config Configuration options
39477  */
39478
39479
39480
39481 // easy hidden field...
39482 Roo.form.Hidden = function(config){
39483     Roo.form.Hidden.superclass.constructor.call(this, config);
39484 };
39485   
39486 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39487     fieldLabel:      '',
39488     inputType:      'hidden',
39489     width:          50,
39490     allowBlank:     true,
39491     labelSeparator: '',
39492     hidden:         true,
39493     itemCls :       'x-form-item-display-none'
39494
39495
39496 });
39497
39498
39499 /*
39500  * Based on:
39501  * Ext JS Library 1.1.1
39502  * Copyright(c) 2006-2007, Ext JS, LLC.
39503  *
39504  * Originally Released Under LGPL - original licence link has changed is not relivant.
39505  *
39506  * Fork - LGPL
39507  * <script type="text/javascript">
39508  */
39509  
39510 /**
39511  * @class Roo.form.TriggerField
39512  * @extends Roo.form.TextField
39513  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39514  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39515  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39516  * for which you can provide a custom implementation.  For example:
39517  * <pre><code>
39518 var trigger = new Roo.form.TriggerField();
39519 trigger.onTriggerClick = myTriggerFn;
39520 trigger.applyTo('my-field');
39521 </code></pre>
39522  *
39523  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39524  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39525  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39526  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39527  * @constructor
39528  * Create a new TriggerField.
39529  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39530  * to the base TextField)
39531  */
39532 Roo.form.TriggerField = function(config){
39533     this.mimicing = false;
39534     Roo.form.TriggerField.superclass.constructor.call(this, config);
39535 };
39536
39537 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39538     /**
39539      * @cfg {String} triggerClass A CSS class to apply to the trigger
39540      */
39541     /**
39542      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39543      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39544      */
39545     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39546     /**
39547      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39548      */
39549     hideTrigger:false,
39550
39551     /** @cfg {Boolean} grow @hide */
39552     /** @cfg {Number} growMin @hide */
39553     /** @cfg {Number} growMax @hide */
39554
39555     /**
39556      * @hide 
39557      * @method
39558      */
39559     autoSize: Roo.emptyFn,
39560     // private
39561     monitorTab : true,
39562     // private
39563     deferHeight : true,
39564
39565     
39566     actionMode : 'wrap',
39567     // private
39568     onResize : function(w, h){
39569         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39570         if(typeof w == 'number'){
39571             var x = w - this.trigger.getWidth();
39572             this.el.setWidth(this.adjustWidth('input', x));
39573             this.trigger.setStyle('left', x+'px');
39574         }
39575     },
39576
39577     // private
39578     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39579
39580     // private
39581     getResizeEl : function(){
39582         return this.wrap;
39583     },
39584
39585     // private
39586     getPositionEl : function(){
39587         return this.wrap;
39588     },
39589
39590     // private
39591     alignErrorIcon : function(){
39592         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39593     },
39594
39595     // private
39596     onRender : function(ct, position){
39597         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39598         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39599         this.trigger = this.wrap.createChild(this.triggerConfig ||
39600                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39601         if(this.hideTrigger){
39602             this.trigger.setDisplayed(false);
39603         }
39604         this.initTrigger();
39605         if(!this.width){
39606             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39607         }
39608     },
39609
39610     // private
39611     initTrigger : function(){
39612         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39613         this.trigger.addClassOnOver('x-form-trigger-over');
39614         this.trigger.addClassOnClick('x-form-trigger-click');
39615     },
39616
39617     // private
39618     onDestroy : function(){
39619         if(this.trigger){
39620             this.trigger.removeAllListeners();
39621             this.trigger.remove();
39622         }
39623         if(this.wrap){
39624             this.wrap.remove();
39625         }
39626         Roo.form.TriggerField.superclass.onDestroy.call(this);
39627     },
39628
39629     // private
39630     onFocus : function(){
39631         Roo.form.TriggerField.superclass.onFocus.call(this);
39632         if(!this.mimicing){
39633             this.wrap.addClass('x-trigger-wrap-focus');
39634             this.mimicing = true;
39635             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39636             if(this.monitorTab){
39637                 this.el.on("keydown", this.checkTab, this);
39638             }
39639         }
39640     },
39641
39642     // private
39643     checkTab : function(e){
39644         if(e.getKey() == e.TAB){
39645             this.triggerBlur();
39646         }
39647     },
39648
39649     // private
39650     onBlur : function(){
39651         // do nothing
39652     },
39653
39654     // private
39655     mimicBlur : function(e, t){
39656         if(!this.wrap.contains(t) && this.validateBlur()){
39657             this.triggerBlur();
39658         }
39659     },
39660
39661     // private
39662     triggerBlur : function(){
39663         this.mimicing = false;
39664         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39665         if(this.monitorTab){
39666             this.el.un("keydown", this.checkTab, this);
39667         }
39668         this.wrap.removeClass('x-trigger-wrap-focus');
39669         Roo.form.TriggerField.superclass.onBlur.call(this);
39670     },
39671
39672     // private
39673     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39674     validateBlur : function(e, t){
39675         return true;
39676     },
39677
39678     // private
39679     onDisable : function(){
39680         Roo.form.TriggerField.superclass.onDisable.call(this);
39681         if(this.wrap){
39682             this.wrap.addClass('x-item-disabled');
39683         }
39684     },
39685
39686     // private
39687     onEnable : function(){
39688         Roo.form.TriggerField.superclass.onEnable.call(this);
39689         if(this.wrap){
39690             this.wrap.removeClass('x-item-disabled');
39691         }
39692     },
39693
39694     // private
39695     onShow : function(){
39696         var ae = this.getActionEl();
39697         
39698         if(ae){
39699             ae.dom.style.display = '';
39700             ae.dom.style.visibility = 'visible';
39701         }
39702     },
39703
39704     // private
39705     
39706     onHide : function(){
39707         var ae = this.getActionEl();
39708         ae.dom.style.display = 'none';
39709     },
39710
39711     /**
39712      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39713      * by an implementing function.
39714      * @method
39715      * @param {EventObject} e
39716      */
39717     onTriggerClick : Roo.emptyFn
39718 });
39719
39720 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39721 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39722 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39723 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39724     initComponent : function(){
39725         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39726
39727         this.triggerConfig = {
39728             tag:'span', cls:'x-form-twin-triggers', cn:[
39729             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39730             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39731         ]};
39732     },
39733
39734     getTrigger : function(index){
39735         return this.triggers[index];
39736     },
39737
39738     initTrigger : function(){
39739         var ts = this.trigger.select('.x-form-trigger', true);
39740         this.wrap.setStyle('overflow', 'hidden');
39741         var triggerField = this;
39742         ts.each(function(t, all, index){
39743             t.hide = function(){
39744                 var w = triggerField.wrap.getWidth();
39745                 this.dom.style.display = 'none';
39746                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39747             };
39748             t.show = function(){
39749                 var w = triggerField.wrap.getWidth();
39750                 this.dom.style.display = '';
39751                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39752             };
39753             var triggerIndex = 'Trigger'+(index+1);
39754
39755             if(this['hide'+triggerIndex]){
39756                 t.dom.style.display = 'none';
39757             }
39758             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39759             t.addClassOnOver('x-form-trigger-over');
39760             t.addClassOnClick('x-form-trigger-click');
39761         }, this);
39762         this.triggers = ts.elements;
39763     },
39764
39765     onTrigger1Click : Roo.emptyFn,
39766     onTrigger2Click : Roo.emptyFn
39767 });/*
39768  * Based on:
39769  * Ext JS Library 1.1.1
39770  * Copyright(c) 2006-2007, Ext JS, LLC.
39771  *
39772  * Originally Released Under LGPL - original licence link has changed is not relivant.
39773  *
39774  * Fork - LGPL
39775  * <script type="text/javascript">
39776  */
39777  
39778 /**
39779  * @class Roo.form.TextArea
39780  * @extends Roo.form.TextField
39781  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39782  * support for auto-sizing.
39783  * @constructor
39784  * Creates a new TextArea
39785  * @param {Object} config Configuration options
39786  */
39787 Roo.form.TextArea = function(config){
39788     Roo.form.TextArea.superclass.constructor.call(this, config);
39789     // these are provided exchanges for backwards compat
39790     // minHeight/maxHeight were replaced by growMin/growMax to be
39791     // compatible with TextField growing config values
39792     if(this.minHeight !== undefined){
39793         this.growMin = this.minHeight;
39794     }
39795     if(this.maxHeight !== undefined){
39796         this.growMax = this.maxHeight;
39797     }
39798 };
39799
39800 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39801     /**
39802      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39803      */
39804     growMin : 60,
39805     /**
39806      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39807      */
39808     growMax: 1000,
39809     /**
39810      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39811      * in the field (equivalent to setting overflow: hidden, defaults to false)
39812      */
39813     preventScrollbars: false,
39814     /**
39815      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39816      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39817      */
39818
39819     // private
39820     onRender : function(ct, position){
39821         if(!this.el){
39822             this.defaultAutoCreate = {
39823                 tag: "textarea",
39824                 style:"width:300px;height:60px;",
39825                 autocomplete: "new-password"
39826             };
39827         }
39828         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39829         if(this.grow){
39830             this.textSizeEl = Roo.DomHelper.append(document.body, {
39831                 tag: "pre", cls: "x-form-grow-sizer"
39832             });
39833             if(this.preventScrollbars){
39834                 this.el.setStyle("overflow", "hidden");
39835             }
39836             this.el.setHeight(this.growMin);
39837         }
39838     },
39839
39840     onDestroy : function(){
39841         if(this.textSizeEl){
39842             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39843         }
39844         Roo.form.TextArea.superclass.onDestroy.call(this);
39845     },
39846
39847     // private
39848     onKeyUp : function(e){
39849         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39850             this.autoSize();
39851         }
39852     },
39853
39854     /**
39855      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39856      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39857      */
39858     autoSize : function(){
39859         if(!this.grow || !this.textSizeEl){
39860             return;
39861         }
39862         var el = this.el;
39863         var v = el.dom.value;
39864         var ts = this.textSizeEl;
39865
39866         ts.innerHTML = '';
39867         ts.appendChild(document.createTextNode(v));
39868         v = ts.innerHTML;
39869
39870         Roo.fly(ts).setWidth(this.el.getWidth());
39871         if(v.length < 1){
39872             v = "&#160;&#160;";
39873         }else{
39874             if(Roo.isIE){
39875                 v = v.replace(/\n/g, '<p>&#160;</p>');
39876             }
39877             v += "&#160;\n&#160;";
39878         }
39879         ts.innerHTML = v;
39880         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39881         if(h != this.lastHeight){
39882             this.lastHeight = h;
39883             this.el.setHeight(h);
39884             this.fireEvent("autosize", this, h);
39885         }
39886     }
39887 });/*
39888  * Based on:
39889  * Ext JS Library 1.1.1
39890  * Copyright(c) 2006-2007, Ext JS, LLC.
39891  *
39892  * Originally Released Under LGPL - original licence link has changed is not relivant.
39893  *
39894  * Fork - LGPL
39895  * <script type="text/javascript">
39896  */
39897  
39898
39899 /**
39900  * @class Roo.form.NumberField
39901  * @extends Roo.form.TextField
39902  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39903  * @constructor
39904  * Creates a new NumberField
39905  * @param {Object} config Configuration options
39906  */
39907 Roo.form.NumberField = function(config){
39908     Roo.form.NumberField.superclass.constructor.call(this, config);
39909 };
39910
39911 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39912     /**
39913      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39914      */
39915     fieldClass: "x-form-field x-form-num-field",
39916     /**
39917      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39918      */
39919     allowDecimals : true,
39920     /**
39921      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39922      */
39923     decimalSeparator : ".",
39924     /**
39925      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39926      */
39927     decimalPrecision : 2,
39928     /**
39929      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39930      */
39931     allowNegative : true,
39932     /**
39933      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39934      */
39935     minValue : Number.NEGATIVE_INFINITY,
39936     /**
39937      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39938      */
39939     maxValue : Number.MAX_VALUE,
39940     /**
39941      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39942      */
39943     minText : "The minimum value for this field is {0}",
39944     /**
39945      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39946      */
39947     maxText : "The maximum value for this field is {0}",
39948     /**
39949      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39950      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39951      */
39952     nanText : "{0} is not a valid number",
39953
39954     // private
39955     initEvents : function(){
39956         Roo.form.NumberField.superclass.initEvents.call(this);
39957         var allowed = "0123456789";
39958         if(this.allowDecimals){
39959             allowed += this.decimalSeparator;
39960         }
39961         if(this.allowNegative){
39962             allowed += "-";
39963         }
39964         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39965         var keyPress = function(e){
39966             var k = e.getKey();
39967             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39968                 return;
39969             }
39970             var c = e.getCharCode();
39971             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39972                 e.stopEvent();
39973             }
39974         };
39975         this.el.on("keypress", keyPress, this);
39976     },
39977
39978     // private
39979     validateValue : function(value){
39980         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39981             return false;
39982         }
39983         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39984              return true;
39985         }
39986         var num = this.parseValue(value);
39987         if(isNaN(num)){
39988             this.markInvalid(String.format(this.nanText, value));
39989             return false;
39990         }
39991         if(num < this.minValue){
39992             this.markInvalid(String.format(this.minText, this.minValue));
39993             return false;
39994         }
39995         if(num > this.maxValue){
39996             this.markInvalid(String.format(this.maxText, this.maxValue));
39997             return false;
39998         }
39999         return true;
40000     },
40001
40002     getValue : function(){
40003         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40004     },
40005
40006     // private
40007     parseValue : function(value){
40008         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40009         return isNaN(value) ? '' : value;
40010     },
40011
40012     // private
40013     fixPrecision : function(value){
40014         var nan = isNaN(value);
40015         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40016             return nan ? '' : value;
40017         }
40018         return parseFloat(value).toFixed(this.decimalPrecision);
40019     },
40020
40021     setValue : function(v){
40022         v = this.fixPrecision(v);
40023         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40024     },
40025
40026     // private
40027     decimalPrecisionFcn : function(v){
40028         return Math.floor(v);
40029     },
40030
40031     beforeBlur : function(){
40032         var v = this.parseValue(this.getRawValue());
40033         if(v){
40034             this.setValue(v);
40035         }
40036     }
40037 });/*
40038  * Based on:
40039  * Ext JS Library 1.1.1
40040  * Copyright(c) 2006-2007, Ext JS, LLC.
40041  *
40042  * Originally Released Under LGPL - original licence link has changed is not relivant.
40043  *
40044  * Fork - LGPL
40045  * <script type="text/javascript">
40046  */
40047  
40048 /**
40049  * @class Roo.form.DateField
40050  * @extends Roo.form.TriggerField
40051  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40052 * @constructor
40053 * Create a new DateField
40054 * @param {Object} config
40055  */
40056 Roo.form.DateField = function(config){
40057     Roo.form.DateField.superclass.constructor.call(this, config);
40058     
40059       this.addEvents({
40060          
40061         /**
40062          * @event select
40063          * Fires when a date is selected
40064              * @param {Roo.form.DateField} combo This combo box
40065              * @param {Date} date The date selected
40066              */
40067         'select' : true
40068          
40069     });
40070     
40071     
40072     if(typeof this.minValue == "string") {
40073         this.minValue = this.parseDate(this.minValue);
40074     }
40075     if(typeof this.maxValue == "string") {
40076         this.maxValue = this.parseDate(this.maxValue);
40077     }
40078     this.ddMatch = null;
40079     if(this.disabledDates){
40080         var dd = this.disabledDates;
40081         var re = "(?:";
40082         for(var i = 0; i < dd.length; i++){
40083             re += dd[i];
40084             if(i != dd.length-1) {
40085                 re += "|";
40086             }
40087         }
40088         this.ddMatch = new RegExp(re + ")");
40089     }
40090 };
40091
40092 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40093     /**
40094      * @cfg {String} format
40095      * The default date format string which can be overriden for localization support.  The format must be
40096      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40097      */
40098     format : "m/d/y",
40099     /**
40100      * @cfg {String} altFormats
40101      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40102      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40103      */
40104     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40105     /**
40106      * @cfg {Array} disabledDays
40107      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40108      */
40109     disabledDays : null,
40110     /**
40111      * @cfg {String} disabledDaysText
40112      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40113      */
40114     disabledDaysText : "Disabled",
40115     /**
40116      * @cfg {Array} disabledDates
40117      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40118      * expression so they are very powerful. Some examples:
40119      * <ul>
40120      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40121      * <li>["03/08", "09/16"] would disable those days for every year</li>
40122      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40123      * <li>["03/../2006"] would disable every day in March 2006</li>
40124      * <li>["^03"] would disable every day in every March</li>
40125      * </ul>
40126      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40127      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40128      */
40129     disabledDates : null,
40130     /**
40131      * @cfg {String} disabledDatesText
40132      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40133      */
40134     disabledDatesText : "Disabled",
40135     /**
40136      * @cfg {Date/String} minValue
40137      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40138      * valid format (defaults to null).
40139      */
40140     minValue : null,
40141     /**
40142      * @cfg {Date/String} maxValue
40143      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40144      * valid format (defaults to null).
40145      */
40146     maxValue : null,
40147     /**
40148      * @cfg {String} minText
40149      * The error text to display when the date in the cell is before minValue (defaults to
40150      * 'The date in this field must be after {minValue}').
40151      */
40152     minText : "The date in this field must be equal to or after {0}",
40153     /**
40154      * @cfg {String} maxText
40155      * The error text to display when the date in the cell is after maxValue (defaults to
40156      * 'The date in this field must be before {maxValue}').
40157      */
40158     maxText : "The date in this field must be equal to or before {0}",
40159     /**
40160      * @cfg {String} invalidText
40161      * The error text to display when the date in the field is invalid (defaults to
40162      * '{value} is not a valid date - it must be in the format {format}').
40163      */
40164     invalidText : "{0} is not a valid date - it must be in the format {1}",
40165     /**
40166      * @cfg {String} triggerClass
40167      * An additional CSS class used to style the trigger button.  The trigger will always get the
40168      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40169      * which displays a calendar icon).
40170      */
40171     triggerClass : 'x-form-date-trigger',
40172     
40173
40174     /**
40175      * @cfg {Boolean} useIso
40176      * if enabled, then the date field will use a hidden field to store the 
40177      * real value as iso formated date. default (false)
40178      */ 
40179     useIso : false,
40180     /**
40181      * @cfg {String/Object} autoCreate
40182      * A DomHelper element spec, or true for a default element spec (defaults to
40183      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40184      */ 
40185     // private
40186     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40187     
40188     // private
40189     hiddenField: false,
40190     
40191     onRender : function(ct, position)
40192     {
40193         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40194         if (this.useIso) {
40195             //this.el.dom.removeAttribute('name'); 
40196             Roo.log("Changing name?");
40197             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40198             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40199                     'before', true);
40200             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40201             // prevent input submission
40202             this.hiddenName = this.name;
40203         }
40204             
40205             
40206     },
40207     
40208     // private
40209     validateValue : function(value)
40210     {
40211         value = this.formatDate(value);
40212         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40213             Roo.log('super failed');
40214             return false;
40215         }
40216         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40217              return true;
40218         }
40219         var svalue = value;
40220         value = this.parseDate(value);
40221         if(!value){
40222             Roo.log('parse date failed' + svalue);
40223             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40224             return false;
40225         }
40226         var time = value.getTime();
40227         if(this.minValue && time < this.minValue.getTime()){
40228             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40229             return false;
40230         }
40231         if(this.maxValue && time > this.maxValue.getTime()){
40232             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40233             return false;
40234         }
40235         if(this.disabledDays){
40236             var day = value.getDay();
40237             for(var i = 0; i < this.disabledDays.length; i++) {
40238                 if(day === this.disabledDays[i]){
40239                     this.markInvalid(this.disabledDaysText);
40240                     return false;
40241                 }
40242             }
40243         }
40244         var fvalue = this.formatDate(value);
40245         if(this.ddMatch && this.ddMatch.test(fvalue)){
40246             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40247             return false;
40248         }
40249         return true;
40250     },
40251
40252     // private
40253     // Provides logic to override the default TriggerField.validateBlur which just returns true
40254     validateBlur : function(){
40255         return !this.menu || !this.menu.isVisible();
40256     },
40257     
40258     getName: function()
40259     {
40260         // returns hidden if it's set..
40261         if (!this.rendered) {return ''};
40262         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40263         
40264     },
40265
40266     /**
40267      * Returns the current date value of the date field.
40268      * @return {Date} The date value
40269      */
40270     getValue : function(){
40271         
40272         return  this.hiddenField ?
40273                 this.hiddenField.value :
40274                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40275     },
40276
40277     /**
40278      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40279      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40280      * (the default format used is "m/d/y").
40281      * <br />Usage:
40282      * <pre><code>
40283 //All of these calls set the same date value (May 4, 2006)
40284
40285 //Pass a date object:
40286 var dt = new Date('5/4/06');
40287 dateField.setValue(dt);
40288
40289 //Pass a date string (default format):
40290 dateField.setValue('5/4/06');
40291
40292 //Pass a date string (custom format):
40293 dateField.format = 'Y-m-d';
40294 dateField.setValue('2006-5-4');
40295 </code></pre>
40296      * @param {String/Date} date The date or valid date string
40297      */
40298     setValue : function(date){
40299         if (this.hiddenField) {
40300             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40301         }
40302         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40303         // make sure the value field is always stored as a date..
40304         this.value = this.parseDate(date);
40305         
40306         
40307     },
40308
40309     // private
40310     parseDate : function(value){
40311         if(!value || value instanceof Date){
40312             return value;
40313         }
40314         var v = Date.parseDate(value, this.format);
40315          if (!v && this.useIso) {
40316             v = Date.parseDate(value, 'Y-m-d');
40317         }
40318         if(!v && this.altFormats){
40319             if(!this.altFormatsArray){
40320                 this.altFormatsArray = this.altFormats.split("|");
40321             }
40322             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40323                 v = Date.parseDate(value, this.altFormatsArray[i]);
40324             }
40325         }
40326         return v;
40327     },
40328
40329     // private
40330     formatDate : function(date, fmt){
40331         return (!date || !(date instanceof Date)) ?
40332                date : date.dateFormat(fmt || this.format);
40333     },
40334
40335     // private
40336     menuListeners : {
40337         select: function(m, d){
40338             
40339             this.setValue(d);
40340             this.fireEvent('select', this, d);
40341         },
40342         show : function(){ // retain focus styling
40343             this.onFocus();
40344         },
40345         hide : function(){
40346             this.focus.defer(10, this);
40347             var ml = this.menuListeners;
40348             this.menu.un("select", ml.select,  this);
40349             this.menu.un("show", ml.show,  this);
40350             this.menu.un("hide", ml.hide,  this);
40351         }
40352     },
40353
40354     // private
40355     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40356     onTriggerClick : function(){
40357         if(this.disabled){
40358             return;
40359         }
40360         if(this.menu == null){
40361             this.menu = new Roo.menu.DateMenu();
40362         }
40363         Roo.apply(this.menu.picker,  {
40364             showClear: this.allowBlank,
40365             minDate : this.minValue,
40366             maxDate : this.maxValue,
40367             disabledDatesRE : this.ddMatch,
40368             disabledDatesText : this.disabledDatesText,
40369             disabledDays : this.disabledDays,
40370             disabledDaysText : this.disabledDaysText,
40371             format : this.useIso ? 'Y-m-d' : this.format,
40372             minText : String.format(this.minText, this.formatDate(this.minValue)),
40373             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40374         });
40375         this.menu.on(Roo.apply({}, this.menuListeners, {
40376             scope:this
40377         }));
40378         this.menu.picker.setValue(this.getValue() || new Date());
40379         this.menu.show(this.el, "tl-bl?");
40380     },
40381
40382     beforeBlur : function(){
40383         var v = this.parseDate(this.getRawValue());
40384         if(v){
40385             this.setValue(v);
40386         }
40387     },
40388
40389     /*@
40390      * overide
40391      * 
40392      */
40393     isDirty : function() {
40394         if(this.disabled) {
40395             return false;
40396         }
40397         
40398         if(typeof(this.startValue) === 'undefined'){
40399             return false;
40400         }
40401         
40402         return String(this.getValue()) !== String(this.startValue);
40403         
40404     }
40405 });/*
40406  * Based on:
40407  * Ext JS Library 1.1.1
40408  * Copyright(c) 2006-2007, Ext JS, LLC.
40409  *
40410  * Originally Released Under LGPL - original licence link has changed is not relivant.
40411  *
40412  * Fork - LGPL
40413  * <script type="text/javascript">
40414  */
40415  
40416 /**
40417  * @class Roo.form.MonthField
40418  * @extends Roo.form.TriggerField
40419  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40420 * @constructor
40421 * Create a new MonthField
40422 * @param {Object} config
40423  */
40424 Roo.form.MonthField = function(config){
40425     
40426     Roo.form.MonthField.superclass.constructor.call(this, config);
40427     
40428       this.addEvents({
40429          
40430         /**
40431          * @event select
40432          * Fires when a date is selected
40433              * @param {Roo.form.MonthFieeld} combo This combo box
40434              * @param {Date} date The date selected
40435              */
40436         'select' : true
40437          
40438     });
40439     
40440     
40441     if(typeof this.minValue == "string") {
40442         this.minValue = this.parseDate(this.minValue);
40443     }
40444     if(typeof this.maxValue == "string") {
40445         this.maxValue = this.parseDate(this.maxValue);
40446     }
40447     this.ddMatch = null;
40448     if(this.disabledDates){
40449         var dd = this.disabledDates;
40450         var re = "(?:";
40451         for(var i = 0; i < dd.length; i++){
40452             re += dd[i];
40453             if(i != dd.length-1) {
40454                 re += "|";
40455             }
40456         }
40457         this.ddMatch = new RegExp(re + ")");
40458     }
40459 };
40460
40461 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40462     /**
40463      * @cfg {String} format
40464      * The default date format string which can be overriden for localization support.  The format must be
40465      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40466      */
40467     format : "M Y",
40468     /**
40469      * @cfg {String} altFormats
40470      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40471      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40472      */
40473     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40474     /**
40475      * @cfg {Array} disabledDays
40476      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40477      */
40478     disabledDays : [0,1,2,3,4,5,6],
40479     /**
40480      * @cfg {String} disabledDaysText
40481      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40482      */
40483     disabledDaysText : "Disabled",
40484     /**
40485      * @cfg {Array} disabledDates
40486      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40487      * expression so they are very powerful. Some examples:
40488      * <ul>
40489      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40490      * <li>["03/08", "09/16"] would disable those days for every year</li>
40491      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40492      * <li>["03/../2006"] would disable every day in March 2006</li>
40493      * <li>["^03"] would disable every day in every March</li>
40494      * </ul>
40495      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40496      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40497      */
40498     disabledDates : null,
40499     /**
40500      * @cfg {String} disabledDatesText
40501      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40502      */
40503     disabledDatesText : "Disabled",
40504     /**
40505      * @cfg {Date/String} minValue
40506      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40507      * valid format (defaults to null).
40508      */
40509     minValue : null,
40510     /**
40511      * @cfg {Date/String} maxValue
40512      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40513      * valid format (defaults to null).
40514      */
40515     maxValue : null,
40516     /**
40517      * @cfg {String} minText
40518      * The error text to display when the date in the cell is before minValue (defaults to
40519      * 'The date in this field must be after {minValue}').
40520      */
40521     minText : "The date in this field must be equal to or after {0}",
40522     /**
40523      * @cfg {String} maxTextf
40524      * The error text to display when the date in the cell is after maxValue (defaults to
40525      * 'The date in this field must be before {maxValue}').
40526      */
40527     maxText : "The date in this field must be equal to or before {0}",
40528     /**
40529      * @cfg {String} invalidText
40530      * The error text to display when the date in the field is invalid (defaults to
40531      * '{value} is not a valid date - it must be in the format {format}').
40532      */
40533     invalidText : "{0} is not a valid date - it must be in the format {1}",
40534     /**
40535      * @cfg {String} triggerClass
40536      * An additional CSS class used to style the trigger button.  The trigger will always get the
40537      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40538      * which displays a calendar icon).
40539      */
40540     triggerClass : 'x-form-date-trigger',
40541     
40542
40543     /**
40544      * @cfg {Boolean} useIso
40545      * if enabled, then the date field will use a hidden field to store the 
40546      * real value as iso formated date. default (true)
40547      */ 
40548     useIso : true,
40549     /**
40550      * @cfg {String/Object} autoCreate
40551      * A DomHelper element spec, or true for a default element spec (defaults to
40552      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40553      */ 
40554     // private
40555     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40556     
40557     // private
40558     hiddenField: false,
40559     
40560     hideMonthPicker : false,
40561     
40562     onRender : function(ct, position)
40563     {
40564         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40565         if (this.useIso) {
40566             this.el.dom.removeAttribute('name'); 
40567             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40568                     'before', true);
40569             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40570             // prevent input submission
40571             this.hiddenName = this.name;
40572         }
40573             
40574             
40575     },
40576     
40577     // private
40578     validateValue : function(value)
40579     {
40580         value = this.formatDate(value);
40581         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40582             return false;
40583         }
40584         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40585              return true;
40586         }
40587         var svalue = value;
40588         value = this.parseDate(value);
40589         if(!value){
40590             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40591             return false;
40592         }
40593         var time = value.getTime();
40594         if(this.minValue && time < this.minValue.getTime()){
40595             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40596             return false;
40597         }
40598         if(this.maxValue && time > this.maxValue.getTime()){
40599             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40600             return false;
40601         }
40602         /*if(this.disabledDays){
40603             var day = value.getDay();
40604             for(var i = 0; i < this.disabledDays.length; i++) {
40605                 if(day === this.disabledDays[i]){
40606                     this.markInvalid(this.disabledDaysText);
40607                     return false;
40608                 }
40609             }
40610         }
40611         */
40612         var fvalue = this.formatDate(value);
40613         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40614             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40615             return false;
40616         }
40617         */
40618         return true;
40619     },
40620
40621     // private
40622     // Provides logic to override the default TriggerField.validateBlur which just returns true
40623     validateBlur : function(){
40624         return !this.menu || !this.menu.isVisible();
40625     },
40626
40627     /**
40628      * Returns the current date value of the date field.
40629      * @return {Date} The date value
40630      */
40631     getValue : function(){
40632         
40633         
40634         
40635         return  this.hiddenField ?
40636                 this.hiddenField.value :
40637                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40638     },
40639
40640     /**
40641      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40642      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40643      * (the default format used is "m/d/y").
40644      * <br />Usage:
40645      * <pre><code>
40646 //All of these calls set the same date value (May 4, 2006)
40647
40648 //Pass a date object:
40649 var dt = new Date('5/4/06');
40650 monthField.setValue(dt);
40651
40652 //Pass a date string (default format):
40653 monthField.setValue('5/4/06');
40654
40655 //Pass a date string (custom format):
40656 monthField.format = 'Y-m-d';
40657 monthField.setValue('2006-5-4');
40658 </code></pre>
40659      * @param {String/Date} date The date or valid date string
40660      */
40661     setValue : function(date){
40662         Roo.log('month setValue' + date);
40663         // can only be first of month..
40664         
40665         var val = this.parseDate(date);
40666         
40667         if (this.hiddenField) {
40668             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40669         }
40670         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40671         this.value = this.parseDate(date);
40672     },
40673
40674     // private
40675     parseDate : function(value){
40676         if(!value || value instanceof Date){
40677             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40678             return value;
40679         }
40680         var v = Date.parseDate(value, this.format);
40681         if (!v && this.useIso) {
40682             v = Date.parseDate(value, 'Y-m-d');
40683         }
40684         if (v) {
40685             // 
40686             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40687         }
40688         
40689         
40690         if(!v && this.altFormats){
40691             if(!this.altFormatsArray){
40692                 this.altFormatsArray = this.altFormats.split("|");
40693             }
40694             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40695                 v = Date.parseDate(value, this.altFormatsArray[i]);
40696             }
40697         }
40698         return v;
40699     },
40700
40701     // private
40702     formatDate : function(date, fmt){
40703         return (!date || !(date instanceof Date)) ?
40704                date : date.dateFormat(fmt || this.format);
40705     },
40706
40707     // private
40708     menuListeners : {
40709         select: function(m, d){
40710             this.setValue(d);
40711             this.fireEvent('select', this, d);
40712         },
40713         show : function(){ // retain focus styling
40714             this.onFocus();
40715         },
40716         hide : function(){
40717             this.focus.defer(10, this);
40718             var ml = this.menuListeners;
40719             this.menu.un("select", ml.select,  this);
40720             this.menu.un("show", ml.show,  this);
40721             this.menu.un("hide", ml.hide,  this);
40722         }
40723     },
40724     // private
40725     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40726     onTriggerClick : function(){
40727         if(this.disabled){
40728             return;
40729         }
40730         if(this.menu == null){
40731             this.menu = new Roo.menu.DateMenu();
40732            
40733         }
40734         
40735         Roo.apply(this.menu.picker,  {
40736             
40737             showClear: this.allowBlank,
40738             minDate : this.minValue,
40739             maxDate : this.maxValue,
40740             disabledDatesRE : this.ddMatch,
40741             disabledDatesText : this.disabledDatesText,
40742             
40743             format : this.useIso ? 'Y-m-d' : this.format,
40744             minText : String.format(this.minText, this.formatDate(this.minValue)),
40745             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40746             
40747         });
40748          this.menu.on(Roo.apply({}, this.menuListeners, {
40749             scope:this
40750         }));
40751        
40752         
40753         var m = this.menu;
40754         var p = m.picker;
40755         
40756         // hide month picker get's called when we called by 'before hide';
40757         
40758         var ignorehide = true;
40759         p.hideMonthPicker  = function(disableAnim){
40760             if (ignorehide) {
40761                 return;
40762             }
40763              if(this.monthPicker){
40764                 Roo.log("hideMonthPicker called");
40765                 if(disableAnim === true){
40766                     this.monthPicker.hide();
40767                 }else{
40768                     this.monthPicker.slideOut('t', {duration:.2});
40769                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40770                     p.fireEvent("select", this, this.value);
40771                     m.hide();
40772                 }
40773             }
40774         }
40775         
40776         Roo.log('picker set value');
40777         Roo.log(this.getValue());
40778         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40779         m.show(this.el, 'tl-bl?');
40780         ignorehide  = false;
40781         // this will trigger hideMonthPicker..
40782         
40783         
40784         // hidden the day picker
40785         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40786         
40787         
40788         
40789       
40790         
40791         p.showMonthPicker.defer(100, p);
40792     
40793         
40794        
40795     },
40796
40797     beforeBlur : function(){
40798         var v = this.parseDate(this.getRawValue());
40799         if(v){
40800             this.setValue(v);
40801         }
40802     }
40803
40804     /** @cfg {Boolean} grow @hide */
40805     /** @cfg {Number} growMin @hide */
40806     /** @cfg {Number} growMax @hide */
40807     /**
40808      * @hide
40809      * @method autoSize
40810      */
40811 });/*
40812  * Based on:
40813  * Ext JS Library 1.1.1
40814  * Copyright(c) 2006-2007, Ext JS, LLC.
40815  *
40816  * Originally Released Under LGPL - original licence link has changed is not relivant.
40817  *
40818  * Fork - LGPL
40819  * <script type="text/javascript">
40820  */
40821  
40822
40823 /**
40824  * @class Roo.form.ComboBox
40825  * @extends Roo.form.TriggerField
40826  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40827  * @constructor
40828  * Create a new ComboBox.
40829  * @param {Object} config Configuration options
40830  */
40831 Roo.form.ComboBox = function(config){
40832     Roo.form.ComboBox.superclass.constructor.call(this, config);
40833     this.addEvents({
40834         /**
40835          * @event expand
40836          * Fires when the dropdown list is expanded
40837              * @param {Roo.form.ComboBox} combo This combo box
40838              */
40839         'expand' : true,
40840         /**
40841          * @event collapse
40842          * Fires when the dropdown list is collapsed
40843              * @param {Roo.form.ComboBox} combo This combo box
40844              */
40845         'collapse' : true,
40846         /**
40847          * @event beforeselect
40848          * Fires before a list item is selected. Return false to cancel the selection.
40849              * @param {Roo.form.ComboBox} combo This combo box
40850              * @param {Roo.data.Record} record The data record returned from the underlying store
40851              * @param {Number} index The index of the selected item in the dropdown list
40852              */
40853         'beforeselect' : true,
40854         /**
40855          * @event select
40856          * Fires when a list item is selected
40857              * @param {Roo.form.ComboBox} combo This combo box
40858              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40859              * @param {Number} index The index of the selected item in the dropdown list
40860              */
40861         'select' : true,
40862         /**
40863          * @event beforequery
40864          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40865          * The event object passed has these properties:
40866              * @param {Roo.form.ComboBox} combo This combo box
40867              * @param {String} query The query
40868              * @param {Boolean} forceAll true to force "all" query
40869              * @param {Boolean} cancel true to cancel the query
40870              * @param {Object} e The query event object
40871              */
40872         'beforequery': true,
40873          /**
40874          * @event add
40875          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40876              * @param {Roo.form.ComboBox} combo This combo box
40877              */
40878         'add' : true,
40879         /**
40880          * @event edit
40881          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40882              * @param {Roo.form.ComboBox} combo This combo box
40883              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40884              */
40885         'edit' : true
40886         
40887         
40888     });
40889     if(this.transform){
40890         this.allowDomMove = false;
40891         var s = Roo.getDom(this.transform);
40892         if(!this.hiddenName){
40893             this.hiddenName = s.name;
40894         }
40895         if(!this.store){
40896             this.mode = 'local';
40897             var d = [], opts = s.options;
40898             for(var i = 0, len = opts.length;i < len; i++){
40899                 var o = opts[i];
40900                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40901                 if(o.selected) {
40902                     this.value = value;
40903                 }
40904                 d.push([value, o.text]);
40905             }
40906             this.store = new Roo.data.SimpleStore({
40907                 'id': 0,
40908                 fields: ['value', 'text'],
40909                 data : d
40910             });
40911             this.valueField = 'value';
40912             this.displayField = 'text';
40913         }
40914         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40915         if(!this.lazyRender){
40916             this.target = true;
40917             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40918             s.parentNode.removeChild(s); // remove it
40919             this.render(this.el.parentNode);
40920         }else{
40921             s.parentNode.removeChild(s); // remove it
40922         }
40923
40924     }
40925     if (this.store) {
40926         this.store = Roo.factory(this.store, Roo.data);
40927     }
40928     
40929     this.selectedIndex = -1;
40930     if(this.mode == 'local'){
40931         if(config.queryDelay === undefined){
40932             this.queryDelay = 10;
40933         }
40934         if(config.minChars === undefined){
40935             this.minChars = 0;
40936         }
40937     }
40938 };
40939
40940 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40941     /**
40942      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40943      */
40944     /**
40945      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40946      * rendering into an Roo.Editor, defaults to false)
40947      */
40948     /**
40949      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40950      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40951      */
40952     /**
40953      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40954      */
40955     /**
40956      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40957      * the dropdown list (defaults to undefined, with no header element)
40958      */
40959
40960      /**
40961      * @cfg {String/Roo.Template} tpl The template to use to render the output
40962      */
40963      
40964     // private
40965     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40966     /**
40967      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40968      */
40969     listWidth: undefined,
40970     /**
40971      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40972      * mode = 'remote' or 'text' if mode = 'local')
40973      */
40974     displayField: undefined,
40975     /**
40976      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40977      * mode = 'remote' or 'value' if mode = 'local'). 
40978      * Note: use of a valueField requires the user make a selection
40979      * in order for a value to be mapped.
40980      */
40981     valueField: undefined,
40982     
40983     
40984     /**
40985      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40986      * field's data value (defaults to the underlying DOM element's name)
40987      */
40988     hiddenName: undefined,
40989     /**
40990      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40991      */
40992     listClass: '',
40993     /**
40994      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40995      */
40996     selectedClass: 'x-combo-selected',
40997     /**
40998      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40999      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41000      * which displays a downward arrow icon).
41001      */
41002     triggerClass : 'x-form-arrow-trigger',
41003     /**
41004      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41005      */
41006     shadow:'sides',
41007     /**
41008      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41009      * anchor positions (defaults to 'tl-bl')
41010      */
41011     listAlign: 'tl-bl?',
41012     /**
41013      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41014      */
41015     maxHeight: 300,
41016     /**
41017      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41018      * query specified by the allQuery config option (defaults to 'query')
41019      */
41020     triggerAction: 'query',
41021     /**
41022      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41023      * (defaults to 4, does not apply if editable = false)
41024      */
41025     minChars : 4,
41026     /**
41027      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41028      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41029      */
41030     typeAhead: false,
41031     /**
41032      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41033      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41034      */
41035     queryDelay: 500,
41036     /**
41037      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41038      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41039      */
41040     pageSize: 0,
41041     /**
41042      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41043      * when editable = true (defaults to false)
41044      */
41045     selectOnFocus:false,
41046     /**
41047      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41048      */
41049     queryParam: 'query',
41050     /**
41051      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41052      * when mode = 'remote' (defaults to 'Loading...')
41053      */
41054     loadingText: 'Loading...',
41055     /**
41056      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41057      */
41058     resizable: false,
41059     /**
41060      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41061      */
41062     handleHeight : 8,
41063     /**
41064      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41065      * traditional select (defaults to true)
41066      */
41067     editable: true,
41068     /**
41069      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41070      */
41071     allQuery: '',
41072     /**
41073      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41074      */
41075     mode: 'remote',
41076     /**
41077      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41078      * listWidth has a higher value)
41079      */
41080     minListWidth : 70,
41081     /**
41082      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41083      * allow the user to set arbitrary text into the field (defaults to false)
41084      */
41085     forceSelection:false,
41086     /**
41087      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41088      * if typeAhead = true (defaults to 250)
41089      */
41090     typeAheadDelay : 250,
41091     /**
41092      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41093      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41094      */
41095     valueNotFoundText : undefined,
41096     /**
41097      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41098      */
41099     blockFocus : false,
41100     
41101     /**
41102      * @cfg {Boolean} disableClear Disable showing of clear button.
41103      */
41104     disableClear : false,
41105     /**
41106      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41107      */
41108     alwaysQuery : false,
41109     
41110     //private
41111     addicon : false,
41112     editicon: false,
41113     
41114     // element that contains real text value.. (when hidden is used..)
41115      
41116     // private
41117     onRender : function(ct, position){
41118         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41119         if(this.hiddenName){
41120             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41121                     'before', true);
41122             this.hiddenField.value =
41123                 this.hiddenValue !== undefined ? this.hiddenValue :
41124                 this.value !== undefined ? this.value : '';
41125
41126             // prevent input submission
41127             this.el.dom.removeAttribute('name');
41128              
41129              
41130         }
41131         if(Roo.isGecko){
41132             this.el.dom.setAttribute('autocomplete', 'off');
41133         }
41134
41135         var cls = 'x-combo-list';
41136
41137         this.list = new Roo.Layer({
41138             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41139         });
41140
41141         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41142         this.list.setWidth(lw);
41143         this.list.swallowEvent('mousewheel');
41144         this.assetHeight = 0;
41145
41146         if(this.title){
41147             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41148             this.assetHeight += this.header.getHeight();
41149         }
41150
41151         this.innerList = this.list.createChild({cls:cls+'-inner'});
41152         this.innerList.on('mouseover', this.onViewOver, this);
41153         this.innerList.on('mousemove', this.onViewMove, this);
41154         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41155         
41156         if(this.allowBlank && !this.pageSize && !this.disableClear){
41157             this.footer = this.list.createChild({cls:cls+'-ft'});
41158             this.pageTb = new Roo.Toolbar(this.footer);
41159            
41160         }
41161         if(this.pageSize){
41162             this.footer = this.list.createChild({cls:cls+'-ft'});
41163             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41164                     {pageSize: this.pageSize});
41165             
41166         }
41167         
41168         if (this.pageTb && this.allowBlank && !this.disableClear) {
41169             var _this = this;
41170             this.pageTb.add(new Roo.Toolbar.Fill(), {
41171                 cls: 'x-btn-icon x-btn-clear',
41172                 text: '&#160;',
41173                 handler: function()
41174                 {
41175                     _this.collapse();
41176                     _this.clearValue();
41177                     _this.onSelect(false, -1);
41178                 }
41179             });
41180         }
41181         if (this.footer) {
41182             this.assetHeight += this.footer.getHeight();
41183         }
41184         
41185
41186         if(!this.tpl){
41187             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41188         }
41189
41190         this.view = new Roo.View(this.innerList, this.tpl, {
41191             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41192         });
41193
41194         this.view.on('click', this.onViewClick, this);
41195
41196         this.store.on('beforeload', this.onBeforeLoad, this);
41197         this.store.on('load', this.onLoad, this);
41198         this.store.on('loadexception', this.onLoadException, this);
41199
41200         if(this.resizable){
41201             this.resizer = new Roo.Resizable(this.list,  {
41202                pinned:true, handles:'se'
41203             });
41204             this.resizer.on('resize', function(r, w, h){
41205                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41206                 this.listWidth = w;
41207                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41208                 this.restrictHeight();
41209             }, this);
41210             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41211         }
41212         if(!this.editable){
41213             this.editable = true;
41214             this.setEditable(false);
41215         }  
41216         
41217         
41218         if (typeof(this.events.add.listeners) != 'undefined') {
41219             
41220             this.addicon = this.wrap.createChild(
41221                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41222        
41223             this.addicon.on('click', function(e) {
41224                 this.fireEvent('add', this);
41225             }, this);
41226         }
41227         if (typeof(this.events.edit.listeners) != 'undefined') {
41228             
41229             this.editicon = this.wrap.createChild(
41230                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41231             if (this.addicon) {
41232                 this.editicon.setStyle('margin-left', '40px');
41233             }
41234             this.editicon.on('click', function(e) {
41235                 
41236                 // we fire even  if inothing is selected..
41237                 this.fireEvent('edit', this, this.lastData );
41238                 
41239             }, this);
41240         }
41241         
41242         
41243         
41244     },
41245
41246     // private
41247     initEvents : function(){
41248         Roo.form.ComboBox.superclass.initEvents.call(this);
41249
41250         this.keyNav = new Roo.KeyNav(this.el, {
41251             "up" : function(e){
41252                 this.inKeyMode = true;
41253                 this.selectPrev();
41254             },
41255
41256             "down" : function(e){
41257                 if(!this.isExpanded()){
41258                     this.onTriggerClick();
41259                 }else{
41260                     this.inKeyMode = true;
41261                     this.selectNext();
41262                 }
41263             },
41264
41265             "enter" : function(e){
41266                 this.onViewClick();
41267                 //return true;
41268             },
41269
41270             "esc" : function(e){
41271                 this.collapse();
41272             },
41273
41274             "tab" : function(e){
41275                 this.onViewClick(false);
41276                 this.fireEvent("specialkey", this, e);
41277                 return true;
41278             },
41279
41280             scope : this,
41281
41282             doRelay : function(foo, bar, hname){
41283                 if(hname == 'down' || this.scope.isExpanded()){
41284                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41285                 }
41286                 return true;
41287             },
41288
41289             forceKeyDown: true
41290         });
41291         this.queryDelay = Math.max(this.queryDelay || 10,
41292                 this.mode == 'local' ? 10 : 250);
41293         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41294         if(this.typeAhead){
41295             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41296         }
41297         if(this.editable !== false){
41298             this.el.on("keyup", this.onKeyUp, this);
41299         }
41300         if(this.forceSelection){
41301             this.on('blur', this.doForce, this);
41302         }
41303     },
41304
41305     onDestroy : function(){
41306         if(this.view){
41307             this.view.setStore(null);
41308             this.view.el.removeAllListeners();
41309             this.view.el.remove();
41310             this.view.purgeListeners();
41311         }
41312         if(this.list){
41313             this.list.destroy();
41314         }
41315         if(this.store){
41316             this.store.un('beforeload', this.onBeforeLoad, this);
41317             this.store.un('load', this.onLoad, this);
41318             this.store.un('loadexception', this.onLoadException, this);
41319         }
41320         Roo.form.ComboBox.superclass.onDestroy.call(this);
41321     },
41322
41323     // private
41324     fireKey : function(e){
41325         if(e.isNavKeyPress() && !this.list.isVisible()){
41326             this.fireEvent("specialkey", this, e);
41327         }
41328     },
41329
41330     // private
41331     onResize: function(w, h){
41332         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41333         
41334         if(typeof w != 'number'){
41335             // we do not handle it!?!?
41336             return;
41337         }
41338         var tw = this.trigger.getWidth();
41339         tw += this.addicon ? this.addicon.getWidth() : 0;
41340         tw += this.editicon ? this.editicon.getWidth() : 0;
41341         var x = w - tw;
41342         this.el.setWidth( this.adjustWidth('input', x));
41343             
41344         this.trigger.setStyle('left', x+'px');
41345         
41346         if(this.list && this.listWidth === undefined){
41347             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41348             this.list.setWidth(lw);
41349             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41350         }
41351         
41352     
41353         
41354     },
41355
41356     /**
41357      * Allow or prevent the user from directly editing the field text.  If false is passed,
41358      * the user will only be able to select from the items defined in the dropdown list.  This method
41359      * is the runtime equivalent of setting the 'editable' config option at config time.
41360      * @param {Boolean} value True to allow the user to directly edit the field text
41361      */
41362     setEditable : function(value){
41363         if(value == this.editable){
41364             return;
41365         }
41366         this.editable = value;
41367         if(!value){
41368             this.el.dom.setAttribute('readOnly', true);
41369             this.el.on('mousedown', this.onTriggerClick,  this);
41370             this.el.addClass('x-combo-noedit');
41371         }else{
41372             this.el.dom.setAttribute('readOnly', false);
41373             this.el.un('mousedown', this.onTriggerClick,  this);
41374             this.el.removeClass('x-combo-noedit');
41375         }
41376     },
41377
41378     // private
41379     onBeforeLoad : function(){
41380         if(!this.hasFocus){
41381             return;
41382         }
41383         this.innerList.update(this.loadingText ?
41384                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41385         this.restrictHeight();
41386         this.selectedIndex = -1;
41387     },
41388
41389     // private
41390     onLoad : function(){
41391         if(!this.hasFocus){
41392             return;
41393         }
41394         if(this.store.getCount() > 0){
41395             this.expand();
41396             this.restrictHeight();
41397             if(this.lastQuery == this.allQuery){
41398                 if(this.editable){
41399                     this.el.dom.select();
41400                 }
41401                 if(!this.selectByValue(this.value, true)){
41402                     this.select(0, true);
41403                 }
41404             }else{
41405                 this.selectNext();
41406                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41407                     this.taTask.delay(this.typeAheadDelay);
41408                 }
41409             }
41410         }else{
41411             this.onEmptyResults();
41412         }
41413         //this.el.focus();
41414     },
41415     // private
41416     onLoadException : function()
41417     {
41418         this.collapse();
41419         Roo.log(this.store.reader.jsonData);
41420         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41421             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41422         }
41423         
41424         
41425     },
41426     // private
41427     onTypeAhead : function(){
41428         if(this.store.getCount() > 0){
41429             var r = this.store.getAt(0);
41430             var newValue = r.data[this.displayField];
41431             var len = newValue.length;
41432             var selStart = this.getRawValue().length;
41433             if(selStart != len){
41434                 this.setRawValue(newValue);
41435                 this.selectText(selStart, newValue.length);
41436             }
41437         }
41438     },
41439
41440     // private
41441     onSelect : function(record, index){
41442         if(this.fireEvent('beforeselect', this, record, index) !== false){
41443             this.setFromData(index > -1 ? record.data : false);
41444             this.collapse();
41445             this.fireEvent('select', this, record, index);
41446         }
41447     },
41448
41449     /**
41450      * Returns the currently selected field value or empty string if no value is set.
41451      * @return {String} value The selected value
41452      */
41453     getValue : function(){
41454         if(this.valueField){
41455             return typeof this.value != 'undefined' ? this.value : '';
41456         }
41457         return Roo.form.ComboBox.superclass.getValue.call(this);
41458     },
41459
41460     /**
41461      * Clears any text/value currently set in the field
41462      */
41463     clearValue : function(){
41464         if(this.hiddenField){
41465             this.hiddenField.value = '';
41466         }
41467         this.value = '';
41468         this.setRawValue('');
41469         this.lastSelectionText = '';
41470         
41471     },
41472
41473     /**
41474      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41475      * will be displayed in the field.  If the value does not match the data value of an existing item,
41476      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41477      * Otherwise the field will be blank (although the value will still be set).
41478      * @param {String} value The value to match
41479      */
41480     setValue : function(v){
41481         var text = v;
41482         if(this.valueField){
41483             var r = this.findRecord(this.valueField, v);
41484             if(r){
41485                 text = r.data[this.displayField];
41486             }else if(this.valueNotFoundText !== undefined){
41487                 text = this.valueNotFoundText;
41488             }
41489         }
41490         this.lastSelectionText = text;
41491         if(this.hiddenField){
41492             this.hiddenField.value = v;
41493         }
41494         Roo.form.ComboBox.superclass.setValue.call(this, text);
41495         this.value = v;
41496     },
41497     /**
41498      * @property {Object} the last set data for the element
41499      */
41500     
41501     lastData : false,
41502     /**
41503      * Sets the value of the field based on a object which is related to the record format for the store.
41504      * @param {Object} value the value to set as. or false on reset?
41505      */
41506     setFromData : function(o){
41507         var dv = ''; // display value
41508         var vv = ''; // value value..
41509         this.lastData = o;
41510         if (this.displayField) {
41511             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41512         } else {
41513             // this is an error condition!!!
41514             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41515         }
41516         
41517         if(this.valueField){
41518             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41519         }
41520         if(this.hiddenField){
41521             this.hiddenField.value = vv;
41522             
41523             this.lastSelectionText = dv;
41524             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41525             this.value = vv;
41526             return;
41527         }
41528         // no hidden field.. - we store the value in 'value', but still display
41529         // display field!!!!
41530         this.lastSelectionText = dv;
41531         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41532         this.value = vv;
41533         
41534         
41535     },
41536     // private
41537     reset : function(){
41538         // overridden so that last data is reset..
41539         this.setValue(this.resetValue);
41540         this.clearInvalid();
41541         this.lastData = false;
41542         if (this.view) {
41543             this.view.clearSelections();
41544         }
41545     },
41546     // private
41547     findRecord : function(prop, value){
41548         var record;
41549         if(this.store.getCount() > 0){
41550             this.store.each(function(r){
41551                 if(r.data[prop] == value){
41552                     record = r;
41553                     return false;
41554                 }
41555                 return true;
41556             });
41557         }
41558         return record;
41559     },
41560     
41561     getName: function()
41562     {
41563         // returns hidden if it's set..
41564         if (!this.rendered) {return ''};
41565         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41566         
41567     },
41568     // private
41569     onViewMove : function(e, t){
41570         this.inKeyMode = false;
41571     },
41572
41573     // private
41574     onViewOver : function(e, t){
41575         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41576             return;
41577         }
41578         var item = this.view.findItemFromChild(t);
41579         if(item){
41580             var index = this.view.indexOf(item);
41581             this.select(index, false);
41582         }
41583     },
41584
41585     // private
41586     onViewClick : function(doFocus)
41587     {
41588         var index = this.view.getSelectedIndexes()[0];
41589         var r = this.store.getAt(index);
41590         if(r){
41591             this.onSelect(r, index);
41592         }
41593         if(doFocus !== false && !this.blockFocus){
41594             this.el.focus();
41595         }
41596     },
41597
41598     // private
41599     restrictHeight : function(){
41600         this.innerList.dom.style.height = '';
41601         var inner = this.innerList.dom;
41602         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41603         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41604         this.list.beginUpdate();
41605         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41606         this.list.alignTo(this.el, this.listAlign);
41607         this.list.endUpdate();
41608     },
41609
41610     // private
41611     onEmptyResults : function(){
41612         this.collapse();
41613     },
41614
41615     /**
41616      * Returns true if the dropdown list is expanded, else false.
41617      */
41618     isExpanded : function(){
41619         return this.list.isVisible();
41620     },
41621
41622     /**
41623      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41624      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41625      * @param {String} value The data value of the item to select
41626      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41627      * selected item if it is not currently in view (defaults to true)
41628      * @return {Boolean} True if the value matched an item in the list, else false
41629      */
41630     selectByValue : function(v, scrollIntoView){
41631         if(v !== undefined && v !== null){
41632             var r = this.findRecord(this.valueField || this.displayField, v);
41633             if(r){
41634                 this.select(this.store.indexOf(r), scrollIntoView);
41635                 return true;
41636             }
41637         }
41638         return false;
41639     },
41640
41641     /**
41642      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41643      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41644      * @param {Number} index The zero-based index of the list item to select
41645      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41646      * selected item if it is not currently in view (defaults to true)
41647      */
41648     select : function(index, scrollIntoView){
41649         this.selectedIndex = index;
41650         this.view.select(index);
41651         if(scrollIntoView !== false){
41652             var el = this.view.getNode(index);
41653             if(el){
41654                 this.innerList.scrollChildIntoView(el, false);
41655             }
41656         }
41657     },
41658
41659     // private
41660     selectNext : function(){
41661         var ct = this.store.getCount();
41662         if(ct > 0){
41663             if(this.selectedIndex == -1){
41664                 this.select(0);
41665             }else if(this.selectedIndex < ct-1){
41666                 this.select(this.selectedIndex+1);
41667             }
41668         }
41669     },
41670
41671     // private
41672     selectPrev : function(){
41673         var ct = this.store.getCount();
41674         if(ct > 0){
41675             if(this.selectedIndex == -1){
41676                 this.select(0);
41677             }else if(this.selectedIndex != 0){
41678                 this.select(this.selectedIndex-1);
41679             }
41680         }
41681     },
41682
41683     // private
41684     onKeyUp : function(e){
41685         if(this.editable !== false && !e.isSpecialKey()){
41686             this.lastKey = e.getKey();
41687             this.dqTask.delay(this.queryDelay);
41688         }
41689     },
41690
41691     // private
41692     validateBlur : function(){
41693         return !this.list || !this.list.isVisible();   
41694     },
41695
41696     // private
41697     initQuery : function(){
41698         this.doQuery(this.getRawValue());
41699     },
41700
41701     // private
41702     doForce : function(){
41703         if(this.el.dom.value.length > 0){
41704             this.el.dom.value =
41705                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41706              
41707         }
41708     },
41709
41710     /**
41711      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41712      * query allowing the query action to be canceled if needed.
41713      * @param {String} query The SQL query to execute
41714      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41715      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41716      * saved in the current store (defaults to false)
41717      */
41718     doQuery : function(q, forceAll){
41719         if(q === undefined || q === null){
41720             q = '';
41721         }
41722         var qe = {
41723             query: q,
41724             forceAll: forceAll,
41725             combo: this,
41726             cancel:false
41727         };
41728         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41729             return false;
41730         }
41731         q = qe.query;
41732         forceAll = qe.forceAll;
41733         if(forceAll === true || (q.length >= this.minChars)){
41734             if(this.lastQuery != q || this.alwaysQuery){
41735                 this.lastQuery = q;
41736                 if(this.mode == 'local'){
41737                     this.selectedIndex = -1;
41738                     if(forceAll){
41739                         this.store.clearFilter();
41740                     }else{
41741                         this.store.filter(this.displayField, q);
41742                     }
41743                     this.onLoad();
41744                 }else{
41745                     this.store.baseParams[this.queryParam] = q;
41746                     this.store.load({
41747                         params: this.getParams(q)
41748                     });
41749                     this.expand();
41750                 }
41751             }else{
41752                 this.selectedIndex = -1;
41753                 this.onLoad();   
41754             }
41755         }
41756     },
41757
41758     // private
41759     getParams : function(q){
41760         var p = {};
41761         //p[this.queryParam] = q;
41762         if(this.pageSize){
41763             p.start = 0;
41764             p.limit = this.pageSize;
41765         }
41766         return p;
41767     },
41768
41769     /**
41770      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41771      */
41772     collapse : function(){
41773         if(!this.isExpanded()){
41774             return;
41775         }
41776         this.list.hide();
41777         Roo.get(document).un('mousedown', this.collapseIf, this);
41778         Roo.get(document).un('mousewheel', this.collapseIf, this);
41779         if (!this.editable) {
41780             Roo.get(document).un('keydown', this.listKeyPress, this);
41781         }
41782         this.fireEvent('collapse', this);
41783     },
41784
41785     // private
41786     collapseIf : function(e){
41787         if(!e.within(this.wrap) && !e.within(this.list)){
41788             this.collapse();
41789         }
41790     },
41791
41792     /**
41793      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41794      */
41795     expand : function(){
41796         if(this.isExpanded() || !this.hasFocus){
41797             return;
41798         }
41799         this.list.alignTo(this.el, this.listAlign);
41800         this.list.show();
41801         Roo.get(document).on('mousedown', this.collapseIf, this);
41802         Roo.get(document).on('mousewheel', this.collapseIf, this);
41803         if (!this.editable) {
41804             Roo.get(document).on('keydown', this.listKeyPress, this);
41805         }
41806         
41807         this.fireEvent('expand', this);
41808     },
41809
41810     // private
41811     // Implements the default empty TriggerField.onTriggerClick function
41812     onTriggerClick : function(){
41813         if(this.disabled){
41814             return;
41815         }
41816         if(this.isExpanded()){
41817             this.collapse();
41818             if (!this.blockFocus) {
41819                 this.el.focus();
41820             }
41821             
41822         }else {
41823             this.hasFocus = true;
41824             if(this.triggerAction == 'all') {
41825                 this.doQuery(this.allQuery, true);
41826             } else {
41827                 this.doQuery(this.getRawValue());
41828             }
41829             if (!this.blockFocus) {
41830                 this.el.focus();
41831             }
41832         }
41833     },
41834     listKeyPress : function(e)
41835     {
41836         //Roo.log('listkeypress');
41837         // scroll to first matching element based on key pres..
41838         if (e.isSpecialKey()) {
41839             return false;
41840         }
41841         var k = String.fromCharCode(e.getKey()).toUpperCase();
41842         //Roo.log(k);
41843         var match  = false;
41844         var csel = this.view.getSelectedNodes();
41845         var cselitem = false;
41846         if (csel.length) {
41847             var ix = this.view.indexOf(csel[0]);
41848             cselitem  = this.store.getAt(ix);
41849             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41850                 cselitem = false;
41851             }
41852             
41853         }
41854         
41855         this.store.each(function(v) { 
41856             if (cselitem) {
41857                 // start at existing selection.
41858                 if (cselitem.id == v.id) {
41859                     cselitem = false;
41860                 }
41861                 return;
41862             }
41863                 
41864             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41865                 match = this.store.indexOf(v);
41866                 return false;
41867             }
41868         }, this);
41869         
41870         if (match === false) {
41871             return true; // no more action?
41872         }
41873         // scroll to?
41874         this.view.select(match);
41875         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41876         sn.scrollIntoView(sn.dom.parentNode, false);
41877     }
41878
41879     /** 
41880     * @cfg {Boolean} grow 
41881     * @hide 
41882     */
41883     /** 
41884     * @cfg {Number} growMin 
41885     * @hide 
41886     */
41887     /** 
41888     * @cfg {Number} growMax 
41889     * @hide 
41890     */
41891     /**
41892      * @hide
41893      * @method autoSize
41894      */
41895 });/*
41896  * Copyright(c) 2010-2012, Roo J Solutions Limited
41897  *
41898  * Licence LGPL
41899  *
41900  */
41901
41902 /**
41903  * @class Roo.form.ComboBoxArray
41904  * @extends Roo.form.TextField
41905  * A facebook style adder... for lists of email / people / countries  etc...
41906  * pick multiple items from a combo box, and shows each one.
41907  *
41908  *  Fred [x]  Brian [x]  [Pick another |v]
41909  *
41910  *
41911  *  For this to work: it needs various extra information
41912  *    - normal combo problay has
41913  *      name, hiddenName
41914  *    + displayField, valueField
41915  *
41916  *    For our purpose...
41917  *
41918  *
41919  *   If we change from 'extends' to wrapping...
41920  *   
41921  *  
41922  *
41923  
41924  
41925  * @constructor
41926  * Create a new ComboBoxArray.
41927  * @param {Object} config Configuration options
41928  */
41929  
41930
41931 Roo.form.ComboBoxArray = function(config)
41932 {
41933     this.addEvents({
41934         /**
41935          * @event beforeremove
41936          * Fires before remove the value from the list
41937              * @param {Roo.form.ComboBoxArray} _self This combo box array
41938              * @param {Roo.form.ComboBoxArray.Item} item removed item
41939              */
41940         'beforeremove' : true,
41941         /**
41942          * @event remove
41943          * Fires when remove the value from the list
41944              * @param {Roo.form.ComboBoxArray} _self This combo box array
41945              * @param {Roo.form.ComboBoxArray.Item} item removed item
41946              */
41947         'remove' : true
41948         
41949         
41950     });
41951     
41952     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41953     
41954     this.items = new Roo.util.MixedCollection(false);
41955     
41956     // construct the child combo...
41957     
41958     
41959     
41960     
41961    
41962     
41963 }
41964
41965  
41966 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41967
41968     /**
41969      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41970      */
41971     
41972     lastData : false,
41973     
41974     // behavies liek a hiddne field
41975     inputType:      'hidden',
41976     /**
41977      * @cfg {Number} width The width of the box that displays the selected element
41978      */ 
41979     width:          300,
41980
41981     
41982     
41983     /**
41984      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41985      */
41986     name : false,
41987     /**
41988      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41989      */
41990     hiddenName : false,
41991     
41992     
41993     // private the array of items that are displayed..
41994     items  : false,
41995     // private - the hidden field el.
41996     hiddenEl : false,
41997     // private - the filed el..
41998     el : false,
41999     
42000     //validateValue : function() { return true; }, // all values are ok!
42001     //onAddClick: function() { },
42002     
42003     onRender : function(ct, position) 
42004     {
42005         
42006         // create the standard hidden element
42007         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42008         
42009         
42010         // give fake names to child combo;
42011         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42012         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42013         
42014         this.combo = Roo.factory(this.combo, Roo.form);
42015         this.combo.onRender(ct, position);
42016         if (typeof(this.combo.width) != 'undefined') {
42017             this.combo.onResize(this.combo.width,0);
42018         }
42019         
42020         this.combo.initEvents();
42021         
42022         // assigned so form know we need to do this..
42023         this.store          = this.combo.store;
42024         this.valueField     = this.combo.valueField;
42025         this.displayField   = this.combo.displayField ;
42026         
42027         
42028         this.combo.wrap.addClass('x-cbarray-grp');
42029         
42030         var cbwrap = this.combo.wrap.createChild(
42031             {tag: 'div', cls: 'x-cbarray-cb'},
42032             this.combo.el.dom
42033         );
42034         
42035              
42036         this.hiddenEl = this.combo.wrap.createChild({
42037             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42038         });
42039         this.el = this.combo.wrap.createChild({
42040             tag: 'input',  type:'hidden' , name: this.name, value : ''
42041         });
42042          //   this.el.dom.removeAttribute("name");
42043         
42044         
42045         this.outerWrap = this.combo.wrap;
42046         this.wrap = cbwrap;
42047         
42048         this.outerWrap.setWidth(this.width);
42049         this.outerWrap.dom.removeChild(this.el.dom);
42050         
42051         this.wrap.dom.appendChild(this.el.dom);
42052         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42053         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42054         
42055         this.combo.trigger.setStyle('position','relative');
42056         this.combo.trigger.setStyle('left', '0px');
42057         this.combo.trigger.setStyle('top', '2px');
42058         
42059         this.combo.el.setStyle('vertical-align', 'text-bottom');
42060         
42061         //this.trigger.setStyle('vertical-align', 'top');
42062         
42063         // this should use the code from combo really... on('add' ....)
42064         if (this.adder) {
42065             
42066         
42067             this.adder = this.outerWrap.createChild(
42068                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42069             var _t = this;
42070             this.adder.on('click', function(e) {
42071                 _t.fireEvent('adderclick', this, e);
42072             }, _t);
42073         }
42074         //var _t = this;
42075         //this.adder.on('click', this.onAddClick, _t);
42076         
42077         
42078         this.combo.on('select', function(cb, rec, ix) {
42079             this.addItem(rec.data);
42080             
42081             cb.setValue('');
42082             cb.el.dom.value = '';
42083             //cb.lastData = rec.data;
42084             // add to list
42085             
42086         }, this);
42087         
42088         
42089     },
42090     
42091     
42092     getName: function()
42093     {
42094         // returns hidden if it's set..
42095         if (!this.rendered) {return ''};
42096         return  this.hiddenName ? this.hiddenName : this.name;
42097         
42098     },
42099     
42100     
42101     onResize: function(w, h){
42102         
42103         return;
42104         // not sure if this is needed..
42105         //this.combo.onResize(w,h);
42106         
42107         if(typeof w != 'number'){
42108             // we do not handle it!?!?
42109             return;
42110         }
42111         var tw = this.combo.trigger.getWidth();
42112         tw += this.addicon ? this.addicon.getWidth() : 0;
42113         tw += this.editicon ? this.editicon.getWidth() : 0;
42114         var x = w - tw;
42115         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42116             
42117         this.combo.trigger.setStyle('left', '0px');
42118         
42119         if(this.list && this.listWidth === undefined){
42120             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42121             this.list.setWidth(lw);
42122             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42123         }
42124         
42125     
42126         
42127     },
42128     
42129     addItem: function(rec)
42130     {
42131         var valueField = this.combo.valueField;
42132         var displayField = this.combo.displayField;
42133         if (this.items.indexOfKey(rec[valueField]) > -1) {
42134             //console.log("GOT " + rec.data.id);
42135             return;
42136         }
42137         
42138         var x = new Roo.form.ComboBoxArray.Item({
42139             //id : rec[this.idField],
42140             data : rec,
42141             displayField : displayField ,
42142             tipField : displayField ,
42143             cb : this
42144         });
42145         // use the 
42146         this.items.add(rec[valueField],x);
42147         // add it before the element..
42148         this.updateHiddenEl();
42149         x.render(this.outerWrap, this.wrap.dom);
42150         // add the image handler..
42151     },
42152     
42153     updateHiddenEl : function()
42154     {
42155         this.validate();
42156         if (!this.hiddenEl) {
42157             return;
42158         }
42159         var ar = [];
42160         var idField = this.combo.valueField;
42161         
42162         this.items.each(function(f) {
42163             ar.push(f.data[idField]);
42164            
42165         });
42166         this.hiddenEl.dom.value = ar.join(',');
42167         this.validate();
42168     },
42169     
42170     reset : function()
42171     {
42172         this.items.clear();
42173         
42174         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42175            el.remove();
42176         });
42177         
42178         this.el.dom.value = '';
42179         if (this.hiddenEl) {
42180             this.hiddenEl.dom.value = '';
42181         }
42182         
42183     },
42184     getValue: function()
42185     {
42186         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42187     },
42188     setValue: function(v) // not a valid action - must use addItems..
42189     {
42190          
42191         this.reset();
42192         
42193         
42194         
42195         if (this.store.isLocal && (typeof(v) == 'string')) {
42196             // then we can use the store to find the values..
42197             // comma seperated at present.. this needs to allow JSON based encoding..
42198             this.hiddenEl.value  = v;
42199             var v_ar = [];
42200             Roo.each(v.split(','), function(k) {
42201                 Roo.log("CHECK " + this.valueField + ',' + k);
42202                 var li = this.store.query(this.valueField, k);
42203                 if (!li.length) {
42204                     return;
42205                 }
42206                 var add = {};
42207                 add[this.valueField] = k;
42208                 add[this.displayField] = li.item(0).data[this.displayField];
42209                 
42210                 this.addItem(add);
42211             }, this) 
42212              
42213         }
42214         if (typeof(v) == 'object' ) {
42215             // then let's assume it's an array of objects..
42216             Roo.each(v, function(l) {
42217                 this.addItem(l);
42218             }, this);
42219              
42220         }
42221         
42222         
42223     },
42224     setFromData: function(v)
42225     {
42226         // this recieves an object, if setValues is called.
42227         this.reset();
42228         this.el.dom.value = v[this.displayField];
42229         this.hiddenEl.dom.value = v[this.valueField];
42230         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42231             return;
42232         }
42233         var kv = v[this.valueField];
42234         var dv = v[this.displayField];
42235         kv = typeof(kv) != 'string' ? '' : kv;
42236         dv = typeof(dv) != 'string' ? '' : dv;
42237         
42238         
42239         var keys = kv.split(',');
42240         var display = dv.split(',');
42241         for (var i = 0 ; i < keys.length; i++) {
42242             
42243             add = {};
42244             add[this.valueField] = keys[i];
42245             add[this.displayField] = display[i];
42246             this.addItem(add);
42247         }
42248       
42249         
42250     },
42251     
42252     /**
42253      * Validates the combox array value
42254      * @return {Boolean} True if the value is valid, else false
42255      */
42256     validate : function(){
42257         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42258             this.clearInvalid();
42259             return true;
42260         }
42261         return false;
42262     },
42263     
42264     validateValue : function(value){
42265         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42266         
42267     },
42268     
42269     /*@
42270      * overide
42271      * 
42272      */
42273     isDirty : function() {
42274         if(this.disabled) {
42275             return false;
42276         }
42277         
42278         try {
42279             var d = Roo.decode(String(this.originalValue));
42280         } catch (e) {
42281             return String(this.getValue()) !== String(this.originalValue);
42282         }
42283         
42284         var originalValue = [];
42285         
42286         for (var i = 0; i < d.length; i++){
42287             originalValue.push(d[i][this.valueField]);
42288         }
42289         
42290         return String(this.getValue()) !== String(originalValue.join(','));
42291         
42292     }
42293     
42294 });
42295
42296
42297
42298 /**
42299  * @class Roo.form.ComboBoxArray.Item
42300  * @extends Roo.BoxComponent
42301  * A selected item in the list
42302  *  Fred [x]  Brian [x]  [Pick another |v]
42303  * 
42304  * @constructor
42305  * Create a new item.
42306  * @param {Object} config Configuration options
42307  */
42308  
42309 Roo.form.ComboBoxArray.Item = function(config) {
42310     config.id = Roo.id();
42311     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42312 }
42313
42314 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42315     data : {},
42316     cb: false,
42317     displayField : false,
42318     tipField : false,
42319     
42320     
42321     defaultAutoCreate : {
42322         tag: 'div',
42323         cls: 'x-cbarray-item',
42324         cn : [ 
42325             { tag: 'div' },
42326             {
42327                 tag: 'img',
42328                 width:16,
42329                 height : 16,
42330                 src : Roo.BLANK_IMAGE_URL ,
42331                 align: 'center'
42332             }
42333         ]
42334         
42335     },
42336     
42337  
42338     onRender : function(ct, position)
42339     {
42340         Roo.form.Field.superclass.onRender.call(this, ct, position);
42341         
42342         if(!this.el){
42343             var cfg = this.getAutoCreate();
42344             this.el = ct.createChild(cfg, position);
42345         }
42346         
42347         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42348         
42349         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42350             this.cb.renderer(this.data) :
42351             String.format('{0}',this.data[this.displayField]);
42352         
42353             
42354         this.el.child('div').dom.setAttribute('qtip',
42355                         String.format('{0}',this.data[this.tipField])
42356         );
42357         
42358         this.el.child('img').on('click', this.remove, this);
42359         
42360     },
42361    
42362     remove : function()
42363     {
42364         if(this.cb.disabled){
42365             return;
42366         }
42367         
42368         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42369             this.cb.items.remove(this);
42370             this.el.child('img').un('click', this.remove, this);
42371             this.el.remove();
42372             this.cb.updateHiddenEl();
42373
42374             this.cb.fireEvent('remove', this.cb, this);
42375         }
42376         
42377     }
42378 });/*
42379  * Based on:
42380  * Ext JS Library 1.1.1
42381  * Copyright(c) 2006-2007, Ext JS, LLC.
42382  *
42383  * Originally Released Under LGPL - original licence link has changed is not relivant.
42384  *
42385  * Fork - LGPL
42386  * <script type="text/javascript">
42387  */
42388 /**
42389  * @class Roo.form.Checkbox
42390  * @extends Roo.form.Field
42391  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42392  * @constructor
42393  * Creates a new Checkbox
42394  * @param {Object} config Configuration options
42395  */
42396 Roo.form.Checkbox = function(config){
42397     Roo.form.Checkbox.superclass.constructor.call(this, config);
42398     this.addEvents({
42399         /**
42400          * @event check
42401          * Fires when the checkbox is checked or unchecked.
42402              * @param {Roo.form.Checkbox} this This checkbox
42403              * @param {Boolean} checked The new checked value
42404              */
42405         check : true
42406     });
42407 };
42408
42409 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42410     /**
42411      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42412      */
42413     focusClass : undefined,
42414     /**
42415      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42416      */
42417     fieldClass: "x-form-field",
42418     /**
42419      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42420      */
42421     checked: false,
42422     /**
42423      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42424      * {tag: "input", type: "checkbox", autocomplete: "off"})
42425      */
42426     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42427     /**
42428      * @cfg {String} boxLabel The text that appears beside the checkbox
42429      */
42430     boxLabel : "",
42431     /**
42432      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42433      */  
42434     inputValue : '1',
42435     /**
42436      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42437      */
42438      valueOff: '0', // value when not checked..
42439
42440     actionMode : 'viewEl', 
42441     //
42442     // private
42443     itemCls : 'x-menu-check-item x-form-item',
42444     groupClass : 'x-menu-group-item',
42445     inputType : 'hidden',
42446     
42447     
42448     inSetChecked: false, // check that we are not calling self...
42449     
42450     inputElement: false, // real input element?
42451     basedOn: false, // ????
42452     
42453     isFormField: true, // not sure where this is needed!!!!
42454
42455     onResize : function(){
42456         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42457         if(!this.boxLabel){
42458             this.el.alignTo(this.wrap, 'c-c');
42459         }
42460     },
42461
42462     initEvents : function(){
42463         Roo.form.Checkbox.superclass.initEvents.call(this);
42464         this.el.on("click", this.onClick,  this);
42465         this.el.on("change", this.onClick,  this);
42466     },
42467
42468
42469     getResizeEl : function(){
42470         return this.wrap;
42471     },
42472
42473     getPositionEl : function(){
42474         return this.wrap;
42475     },
42476
42477     // private
42478     onRender : function(ct, position){
42479         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42480         /*
42481         if(this.inputValue !== undefined){
42482             this.el.dom.value = this.inputValue;
42483         }
42484         */
42485         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42486         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42487         var viewEl = this.wrap.createChild({ 
42488             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42489         this.viewEl = viewEl;   
42490         this.wrap.on('click', this.onClick,  this); 
42491         
42492         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42493         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42494         
42495         
42496         
42497         if(this.boxLabel){
42498             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42499         //    viewEl.on('click', this.onClick,  this); 
42500         }
42501         //if(this.checked){
42502             this.setChecked(this.checked);
42503         //}else{
42504             //this.checked = this.el.dom;
42505         //}
42506
42507     },
42508
42509     // private
42510     initValue : Roo.emptyFn,
42511
42512     /**
42513      * Returns the checked state of the checkbox.
42514      * @return {Boolean} True if checked, else false
42515      */
42516     getValue : function(){
42517         if(this.el){
42518             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42519         }
42520         return this.valueOff;
42521         
42522     },
42523
42524         // private
42525     onClick : function(){ 
42526         if (this.disabled) {
42527             return;
42528         }
42529         this.setChecked(!this.checked);
42530
42531         //if(this.el.dom.checked != this.checked){
42532         //    this.setValue(this.el.dom.checked);
42533        // }
42534     },
42535
42536     /**
42537      * Sets the checked state of the checkbox.
42538      * On is always based on a string comparison between inputValue and the param.
42539      * @param {Boolean/String} value - the value to set 
42540      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42541      */
42542     setValue : function(v,suppressEvent){
42543         
42544         
42545         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42546         //if(this.el && this.el.dom){
42547         //    this.el.dom.checked = this.checked;
42548         //    this.el.dom.defaultChecked = this.checked;
42549         //}
42550         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42551         //this.fireEvent("check", this, this.checked);
42552     },
42553     // private..
42554     setChecked : function(state,suppressEvent)
42555     {
42556         if (this.inSetChecked) {
42557             this.checked = state;
42558             return;
42559         }
42560         
42561     
42562         if(this.wrap){
42563             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42564         }
42565         this.checked = state;
42566         if(suppressEvent !== true){
42567             this.fireEvent('check', this, state);
42568         }
42569         this.inSetChecked = true;
42570         this.el.dom.value = state ? this.inputValue : this.valueOff;
42571         this.inSetChecked = false;
42572         
42573     },
42574     // handle setting of hidden value by some other method!!?!?
42575     setFromHidden: function()
42576     {
42577         if(!this.el){
42578             return;
42579         }
42580         //console.log("SET FROM HIDDEN");
42581         //alert('setFrom hidden');
42582         this.setValue(this.el.dom.value);
42583     },
42584     
42585     onDestroy : function()
42586     {
42587         if(this.viewEl){
42588             Roo.get(this.viewEl).remove();
42589         }
42590          
42591         Roo.form.Checkbox.superclass.onDestroy.call(this);
42592     },
42593     
42594     setBoxLabel : function(str)
42595     {
42596         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42597     }
42598
42599 });/*
42600  * Based on:
42601  * Ext JS Library 1.1.1
42602  * Copyright(c) 2006-2007, Ext JS, LLC.
42603  *
42604  * Originally Released Under LGPL - original licence link has changed is not relivant.
42605  *
42606  * Fork - LGPL
42607  * <script type="text/javascript">
42608  */
42609  
42610 /**
42611  * @class Roo.form.Radio
42612  * @extends Roo.form.Checkbox
42613  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42614  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42615  * @constructor
42616  * Creates a new Radio
42617  * @param {Object} config Configuration options
42618  */
42619 Roo.form.Radio = function(){
42620     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42621 };
42622 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42623     inputType: 'radio',
42624
42625     /**
42626      * If this radio is part of a group, it will return the selected value
42627      * @return {String}
42628      */
42629     getGroupValue : function(){
42630         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42631     },
42632     
42633     
42634     onRender : function(ct, position){
42635         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42636         
42637         if(this.inputValue !== undefined){
42638             this.el.dom.value = this.inputValue;
42639         }
42640          
42641         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42642         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42643         //var viewEl = this.wrap.createChild({ 
42644         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42645         //this.viewEl = viewEl;   
42646         //this.wrap.on('click', this.onClick,  this); 
42647         
42648         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42649         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42650         
42651         
42652         
42653         if(this.boxLabel){
42654             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42655         //    viewEl.on('click', this.onClick,  this); 
42656         }
42657          if(this.checked){
42658             this.el.dom.checked =   'checked' ;
42659         }
42660          
42661     } 
42662     
42663     
42664 });//<script type="text/javascript">
42665
42666 /*
42667  * Based  Ext JS Library 1.1.1
42668  * Copyright(c) 2006-2007, Ext JS, LLC.
42669  * LGPL
42670  *
42671  */
42672  
42673 /**
42674  * @class Roo.HtmlEditorCore
42675  * @extends Roo.Component
42676  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42677  *
42678  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42679  */
42680
42681 Roo.HtmlEditorCore = function(config){
42682     
42683     
42684     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42685     
42686     
42687     this.addEvents({
42688         /**
42689          * @event initialize
42690          * Fires when the editor is fully initialized (including the iframe)
42691          * @param {Roo.HtmlEditorCore} this
42692          */
42693         initialize: true,
42694         /**
42695          * @event activate
42696          * Fires when the editor is first receives the focus. Any insertion must wait
42697          * until after this event.
42698          * @param {Roo.HtmlEditorCore} this
42699          */
42700         activate: true,
42701          /**
42702          * @event beforesync
42703          * Fires before the textarea is updated with content from the editor iframe. Return false
42704          * to cancel the sync.
42705          * @param {Roo.HtmlEditorCore} this
42706          * @param {String} html
42707          */
42708         beforesync: true,
42709          /**
42710          * @event beforepush
42711          * Fires before the iframe editor is updated with content from the textarea. Return false
42712          * to cancel the push.
42713          * @param {Roo.HtmlEditorCore} this
42714          * @param {String} html
42715          */
42716         beforepush: true,
42717          /**
42718          * @event sync
42719          * Fires when the textarea is updated with content from the editor iframe.
42720          * @param {Roo.HtmlEditorCore} this
42721          * @param {String} html
42722          */
42723         sync: true,
42724          /**
42725          * @event push
42726          * Fires when the iframe editor is updated with content from the textarea.
42727          * @param {Roo.HtmlEditorCore} this
42728          * @param {String} html
42729          */
42730         push: true,
42731         
42732         /**
42733          * @event editorevent
42734          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42735          * @param {Roo.HtmlEditorCore} this
42736          */
42737         editorevent: true
42738         
42739     });
42740     
42741     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42742     
42743     // defaults : white / black...
42744     this.applyBlacklists();
42745     
42746     
42747     
42748 };
42749
42750
42751 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42752
42753
42754      /**
42755      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42756      */
42757     
42758     owner : false,
42759     
42760      /**
42761      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42762      *                        Roo.resizable.
42763      */
42764     resizable : false,
42765      /**
42766      * @cfg {Number} height (in pixels)
42767      */   
42768     height: 300,
42769    /**
42770      * @cfg {Number} width (in pixels)
42771      */   
42772     width: 500,
42773     
42774     /**
42775      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42776      * 
42777      */
42778     stylesheets: false,
42779     
42780     // id of frame..
42781     frameId: false,
42782     
42783     // private properties
42784     validationEvent : false,
42785     deferHeight: true,
42786     initialized : false,
42787     activated : false,
42788     sourceEditMode : false,
42789     onFocus : Roo.emptyFn,
42790     iframePad:3,
42791     hideMode:'offsets',
42792     
42793     clearUp: true,
42794     
42795     // blacklist + whitelisted elements..
42796     black: false,
42797     white: false,
42798      
42799     
42800
42801     /**
42802      * Protected method that will not generally be called directly. It
42803      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42804      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42805      */
42806     getDocMarkup : function(){
42807         // body styles..
42808         var st = '';
42809         
42810         // inherit styels from page...?? 
42811         if (this.stylesheets === false) {
42812             
42813             Roo.get(document.head).select('style').each(function(node) {
42814                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42815             });
42816             
42817             Roo.get(document.head).select('link').each(function(node) { 
42818                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42819             });
42820             
42821         } else if (!this.stylesheets.length) {
42822                 // simple..
42823                 st = '<style type="text/css">' +
42824                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42825                    '</style>';
42826         } else { 
42827             
42828         }
42829         
42830         st +=  '<style type="text/css">' +
42831             'IMG { cursor: pointer } ' +
42832         '</style>';
42833
42834         
42835         return '<html><head>' + st  +
42836             //<style type="text/css">' +
42837             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42838             //'</style>' +
42839             ' </head><body class="roo-htmleditor-body"></body></html>';
42840     },
42841
42842     // private
42843     onRender : function(ct, position)
42844     {
42845         var _t = this;
42846         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42847         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42848         
42849         
42850         this.el.dom.style.border = '0 none';
42851         this.el.dom.setAttribute('tabIndex', -1);
42852         this.el.addClass('x-hidden hide');
42853         
42854         
42855         
42856         if(Roo.isIE){ // fix IE 1px bogus margin
42857             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42858         }
42859        
42860         
42861         this.frameId = Roo.id();
42862         
42863          
42864         
42865         var iframe = this.owner.wrap.createChild({
42866             tag: 'iframe',
42867             cls: 'form-control', // bootstrap..
42868             id: this.frameId,
42869             name: this.frameId,
42870             frameBorder : 'no',
42871             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42872         }, this.el
42873         );
42874         
42875         
42876         this.iframe = iframe.dom;
42877
42878          this.assignDocWin();
42879         
42880         this.doc.designMode = 'on';
42881        
42882         this.doc.open();
42883         this.doc.write(this.getDocMarkup());
42884         this.doc.close();
42885
42886         
42887         var task = { // must defer to wait for browser to be ready
42888             run : function(){
42889                 //console.log("run task?" + this.doc.readyState);
42890                 this.assignDocWin();
42891                 if(this.doc.body || this.doc.readyState == 'complete'){
42892                     try {
42893                         this.doc.designMode="on";
42894                     } catch (e) {
42895                         return;
42896                     }
42897                     Roo.TaskMgr.stop(task);
42898                     this.initEditor.defer(10, this);
42899                 }
42900             },
42901             interval : 10,
42902             duration: 10000,
42903             scope: this
42904         };
42905         Roo.TaskMgr.start(task);
42906
42907     },
42908
42909     // private
42910     onResize : function(w, h)
42911     {
42912          Roo.log('resize: ' +w + ',' + h );
42913         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42914         if(!this.iframe){
42915             return;
42916         }
42917         if(typeof w == 'number'){
42918             
42919             this.iframe.style.width = w + 'px';
42920         }
42921         if(typeof h == 'number'){
42922             
42923             this.iframe.style.height = h + 'px';
42924             if(this.doc){
42925                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42926             }
42927         }
42928         
42929     },
42930
42931     /**
42932      * Toggles the editor between standard and source edit mode.
42933      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42934      */
42935     toggleSourceEdit : function(sourceEditMode){
42936         
42937         this.sourceEditMode = sourceEditMode === true;
42938         
42939         if(this.sourceEditMode){
42940  
42941             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42942             
42943         }else{
42944             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42945             //this.iframe.className = '';
42946             this.deferFocus();
42947         }
42948         //this.setSize(this.owner.wrap.getSize());
42949         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42950     },
42951
42952     
42953   
42954
42955     /**
42956      * Protected method that will not generally be called directly. If you need/want
42957      * custom HTML cleanup, this is the method you should override.
42958      * @param {String} html The HTML to be cleaned
42959      * return {String} The cleaned HTML
42960      */
42961     cleanHtml : function(html){
42962         html = String(html);
42963         if(html.length > 5){
42964             if(Roo.isSafari){ // strip safari nonsense
42965                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42966             }
42967         }
42968         if(html == '&nbsp;'){
42969             html = '';
42970         }
42971         return html;
42972     },
42973
42974     /**
42975      * HTML Editor -> Textarea
42976      * Protected method that will not generally be called directly. Syncs the contents
42977      * of the editor iframe with the textarea.
42978      */
42979     syncValue : function(){
42980         if(this.initialized){
42981             var bd = (this.doc.body || this.doc.documentElement);
42982             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42983             var html = bd.innerHTML;
42984             if(Roo.isSafari){
42985                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42986                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42987                 if(m && m[1]){
42988                     html = '<div style="'+m[0]+'">' + html + '</div>';
42989                 }
42990             }
42991             html = this.cleanHtml(html);
42992             // fix up the special chars.. normaly like back quotes in word...
42993             // however we do not want to do this with chinese..
42994             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42995                 var cc = b.charCodeAt();
42996                 if (
42997                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42998                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42999                     (cc >= 0xf900 && cc < 0xfb00 )
43000                 ) {
43001                         return b;
43002                 }
43003                 return "&#"+cc+";" 
43004             });
43005             if(this.owner.fireEvent('beforesync', this, html) !== false){
43006                 this.el.dom.value = html;
43007                 this.owner.fireEvent('sync', this, html);
43008             }
43009         }
43010     },
43011
43012     /**
43013      * Protected method that will not generally be called directly. Pushes the value of the textarea
43014      * into the iframe editor.
43015      */
43016     pushValue : function(){
43017         if(this.initialized){
43018             var v = this.el.dom.value.trim();
43019             
43020 //            if(v.length < 1){
43021 //                v = '&#160;';
43022 //            }
43023             
43024             if(this.owner.fireEvent('beforepush', this, v) !== false){
43025                 var d = (this.doc.body || this.doc.documentElement);
43026                 d.innerHTML = v;
43027                 this.cleanUpPaste();
43028                 this.el.dom.value = d.innerHTML;
43029                 this.owner.fireEvent('push', this, v);
43030             }
43031         }
43032     },
43033
43034     // private
43035     deferFocus : function(){
43036         this.focus.defer(10, this);
43037     },
43038
43039     // doc'ed in Field
43040     focus : function(){
43041         if(this.win && !this.sourceEditMode){
43042             this.win.focus();
43043         }else{
43044             this.el.focus();
43045         }
43046     },
43047     
43048     assignDocWin: function()
43049     {
43050         var iframe = this.iframe;
43051         
43052          if(Roo.isIE){
43053             this.doc = iframe.contentWindow.document;
43054             this.win = iframe.contentWindow;
43055         } else {
43056 //            if (!Roo.get(this.frameId)) {
43057 //                return;
43058 //            }
43059 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43060 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43061             
43062             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43063                 return;
43064             }
43065             
43066             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43067             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43068         }
43069     },
43070     
43071     // private
43072     initEditor : function(){
43073         //console.log("INIT EDITOR");
43074         this.assignDocWin();
43075         
43076         
43077         
43078         this.doc.designMode="on";
43079         this.doc.open();
43080         this.doc.write(this.getDocMarkup());
43081         this.doc.close();
43082         
43083         var dbody = (this.doc.body || this.doc.documentElement);
43084         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43085         // this copies styles from the containing element into thsi one..
43086         // not sure why we need all of this..
43087         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43088         
43089         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43090         //ss['background-attachment'] = 'fixed'; // w3c
43091         dbody.bgProperties = 'fixed'; // ie
43092         //Roo.DomHelper.applyStyles(dbody, ss);
43093         Roo.EventManager.on(this.doc, {
43094             //'mousedown': this.onEditorEvent,
43095             'mouseup': this.onEditorEvent,
43096             'dblclick': this.onEditorEvent,
43097             'click': this.onEditorEvent,
43098             'keyup': this.onEditorEvent,
43099             buffer:100,
43100             scope: this
43101         });
43102         if(Roo.isGecko){
43103             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43104         }
43105         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43106             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43107         }
43108         this.initialized = true;
43109
43110         this.owner.fireEvent('initialize', this);
43111         this.pushValue();
43112     },
43113
43114     // private
43115     onDestroy : function(){
43116         
43117         
43118         
43119         if(this.rendered){
43120             
43121             //for (var i =0; i < this.toolbars.length;i++) {
43122             //    // fixme - ask toolbars for heights?
43123             //    this.toolbars[i].onDestroy();
43124            // }
43125             
43126             //this.wrap.dom.innerHTML = '';
43127             //this.wrap.remove();
43128         }
43129     },
43130
43131     // private
43132     onFirstFocus : function(){
43133         
43134         this.assignDocWin();
43135         
43136         
43137         this.activated = true;
43138          
43139     
43140         if(Roo.isGecko){ // prevent silly gecko errors
43141             this.win.focus();
43142             var s = this.win.getSelection();
43143             if(!s.focusNode || s.focusNode.nodeType != 3){
43144                 var r = s.getRangeAt(0);
43145                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43146                 r.collapse(true);
43147                 this.deferFocus();
43148             }
43149             try{
43150                 this.execCmd('useCSS', true);
43151                 this.execCmd('styleWithCSS', false);
43152             }catch(e){}
43153         }
43154         this.owner.fireEvent('activate', this);
43155     },
43156
43157     // private
43158     adjustFont: function(btn){
43159         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43160         //if(Roo.isSafari){ // safari
43161         //    adjust *= 2;
43162        // }
43163         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43164         if(Roo.isSafari){ // safari
43165             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43166             v =  (v < 10) ? 10 : v;
43167             v =  (v > 48) ? 48 : v;
43168             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43169             
43170         }
43171         
43172         
43173         v = Math.max(1, v+adjust);
43174         
43175         this.execCmd('FontSize', v  );
43176     },
43177
43178     onEditorEvent : function(e)
43179     {
43180         this.owner.fireEvent('editorevent', this, e);
43181       //  this.updateToolbar();
43182         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43183     },
43184
43185     insertTag : function(tg)
43186     {
43187         // could be a bit smarter... -> wrap the current selected tRoo..
43188         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43189             
43190             range = this.createRange(this.getSelection());
43191             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43192             wrappingNode.appendChild(range.extractContents());
43193             range.insertNode(wrappingNode);
43194
43195             return;
43196             
43197             
43198             
43199         }
43200         this.execCmd("formatblock",   tg);
43201         
43202     },
43203     
43204     insertText : function(txt)
43205     {
43206         
43207         
43208         var range = this.createRange();
43209         range.deleteContents();
43210                //alert(Sender.getAttribute('label'));
43211                
43212         range.insertNode(this.doc.createTextNode(txt));
43213     } ,
43214     
43215      
43216
43217     /**
43218      * Executes a Midas editor command on the editor document and performs necessary focus and
43219      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43220      * @param {String} cmd The Midas command
43221      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43222      */
43223     relayCmd : function(cmd, value){
43224         this.win.focus();
43225         this.execCmd(cmd, value);
43226         this.owner.fireEvent('editorevent', this);
43227         //this.updateToolbar();
43228         this.owner.deferFocus();
43229     },
43230
43231     /**
43232      * Executes a Midas editor command directly on the editor document.
43233      * For visual commands, you should use {@link #relayCmd} instead.
43234      * <b>This should only be called after the editor is initialized.</b>
43235      * @param {String} cmd The Midas command
43236      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43237      */
43238     execCmd : function(cmd, value){
43239         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43240         this.syncValue();
43241     },
43242  
43243  
43244    
43245     /**
43246      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43247      * to insert tRoo.
43248      * @param {String} text | dom node.. 
43249      */
43250     insertAtCursor : function(text)
43251     {
43252         
43253         
43254         
43255         if(!this.activated){
43256             return;
43257         }
43258         /*
43259         if(Roo.isIE){
43260             this.win.focus();
43261             var r = this.doc.selection.createRange();
43262             if(r){
43263                 r.collapse(true);
43264                 r.pasteHTML(text);
43265                 this.syncValue();
43266                 this.deferFocus();
43267             
43268             }
43269             return;
43270         }
43271         */
43272         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43273             this.win.focus();
43274             
43275             
43276             // from jquery ui (MIT licenced)
43277             var range, node;
43278             var win = this.win;
43279             
43280             if (win.getSelection && win.getSelection().getRangeAt) {
43281                 range = win.getSelection().getRangeAt(0);
43282                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43283                 range.insertNode(node);
43284             } else if (win.document.selection && win.document.selection.createRange) {
43285                 // no firefox support
43286                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43287                 win.document.selection.createRange().pasteHTML(txt);
43288             } else {
43289                 // no firefox support
43290                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43291                 this.execCmd('InsertHTML', txt);
43292             } 
43293             
43294             this.syncValue();
43295             
43296             this.deferFocus();
43297         }
43298     },
43299  // private
43300     mozKeyPress : function(e){
43301         if(e.ctrlKey){
43302             var c = e.getCharCode(), cmd;
43303           
43304             if(c > 0){
43305                 c = String.fromCharCode(c).toLowerCase();
43306                 switch(c){
43307                     case 'b':
43308                         cmd = 'bold';
43309                         break;
43310                     case 'i':
43311                         cmd = 'italic';
43312                         break;
43313                     
43314                     case 'u':
43315                         cmd = 'underline';
43316                         break;
43317                     
43318                     case 'v':
43319                         this.cleanUpPaste.defer(100, this);
43320                         return;
43321                         
43322                 }
43323                 if(cmd){
43324                     this.win.focus();
43325                     this.execCmd(cmd);
43326                     this.deferFocus();
43327                     e.preventDefault();
43328                 }
43329                 
43330             }
43331         }
43332     },
43333
43334     // private
43335     fixKeys : function(){ // load time branching for fastest keydown performance
43336         if(Roo.isIE){
43337             return function(e){
43338                 var k = e.getKey(), r;
43339                 if(k == e.TAB){
43340                     e.stopEvent();
43341                     r = this.doc.selection.createRange();
43342                     if(r){
43343                         r.collapse(true);
43344                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43345                         this.deferFocus();
43346                     }
43347                     return;
43348                 }
43349                 
43350                 if(k == e.ENTER){
43351                     r = this.doc.selection.createRange();
43352                     if(r){
43353                         var target = r.parentElement();
43354                         if(!target || target.tagName.toLowerCase() != 'li'){
43355                             e.stopEvent();
43356                             r.pasteHTML('<br />');
43357                             r.collapse(false);
43358                             r.select();
43359                         }
43360                     }
43361                 }
43362                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43363                     this.cleanUpPaste.defer(100, this);
43364                     return;
43365                 }
43366                 
43367                 
43368             };
43369         }else if(Roo.isOpera){
43370             return function(e){
43371                 var k = e.getKey();
43372                 if(k == e.TAB){
43373                     e.stopEvent();
43374                     this.win.focus();
43375                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43376                     this.deferFocus();
43377                 }
43378                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43379                     this.cleanUpPaste.defer(100, this);
43380                     return;
43381                 }
43382                 
43383             };
43384         }else if(Roo.isSafari){
43385             return function(e){
43386                 var k = e.getKey();
43387                 
43388                 if(k == e.TAB){
43389                     e.stopEvent();
43390                     this.execCmd('InsertText','\t');
43391                     this.deferFocus();
43392                     return;
43393                 }
43394                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43395                     this.cleanUpPaste.defer(100, this);
43396                     return;
43397                 }
43398                 
43399              };
43400         }
43401     }(),
43402     
43403     getAllAncestors: function()
43404     {
43405         var p = this.getSelectedNode();
43406         var a = [];
43407         if (!p) {
43408             a.push(p); // push blank onto stack..
43409             p = this.getParentElement();
43410         }
43411         
43412         
43413         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43414             a.push(p);
43415             p = p.parentNode;
43416         }
43417         a.push(this.doc.body);
43418         return a;
43419     },
43420     lastSel : false,
43421     lastSelNode : false,
43422     
43423     
43424     getSelection : function() 
43425     {
43426         this.assignDocWin();
43427         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43428     },
43429     
43430     getSelectedNode: function() 
43431     {
43432         // this may only work on Gecko!!!
43433         
43434         // should we cache this!!!!
43435         
43436         
43437         
43438          
43439         var range = this.createRange(this.getSelection()).cloneRange();
43440         
43441         if (Roo.isIE) {
43442             var parent = range.parentElement();
43443             while (true) {
43444                 var testRange = range.duplicate();
43445                 testRange.moveToElementText(parent);
43446                 if (testRange.inRange(range)) {
43447                     break;
43448                 }
43449                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43450                     break;
43451                 }
43452                 parent = parent.parentElement;
43453             }
43454             return parent;
43455         }
43456         
43457         // is ancestor a text element.
43458         var ac =  range.commonAncestorContainer;
43459         if (ac.nodeType == 3) {
43460             ac = ac.parentNode;
43461         }
43462         
43463         var ar = ac.childNodes;
43464          
43465         var nodes = [];
43466         var other_nodes = [];
43467         var has_other_nodes = false;
43468         for (var i=0;i<ar.length;i++) {
43469             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43470                 continue;
43471             }
43472             // fullly contained node.
43473             
43474             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43475                 nodes.push(ar[i]);
43476                 continue;
43477             }
43478             
43479             // probably selected..
43480             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43481                 other_nodes.push(ar[i]);
43482                 continue;
43483             }
43484             // outer..
43485             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43486                 continue;
43487             }
43488             
43489             
43490             has_other_nodes = true;
43491         }
43492         if (!nodes.length && other_nodes.length) {
43493             nodes= other_nodes;
43494         }
43495         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43496             return false;
43497         }
43498         
43499         return nodes[0];
43500     },
43501     createRange: function(sel)
43502     {
43503         // this has strange effects when using with 
43504         // top toolbar - not sure if it's a great idea.
43505         //this.editor.contentWindow.focus();
43506         if (typeof sel != "undefined") {
43507             try {
43508                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43509             } catch(e) {
43510                 return this.doc.createRange();
43511             }
43512         } else {
43513             return this.doc.createRange();
43514         }
43515     },
43516     getParentElement: function()
43517     {
43518         
43519         this.assignDocWin();
43520         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43521         
43522         var range = this.createRange(sel);
43523          
43524         try {
43525             var p = range.commonAncestorContainer;
43526             while (p.nodeType == 3) { // text node
43527                 p = p.parentNode;
43528             }
43529             return p;
43530         } catch (e) {
43531             return null;
43532         }
43533     
43534     },
43535     /***
43536      *
43537      * Range intersection.. the hard stuff...
43538      *  '-1' = before
43539      *  '0' = hits..
43540      *  '1' = after.
43541      *         [ -- selected range --- ]
43542      *   [fail]                        [fail]
43543      *
43544      *    basically..
43545      *      if end is before start or  hits it. fail.
43546      *      if start is after end or hits it fail.
43547      *
43548      *   if either hits (but other is outside. - then it's not 
43549      *   
43550      *    
43551      **/
43552     
43553     
43554     // @see http://www.thismuchiknow.co.uk/?p=64.
43555     rangeIntersectsNode : function(range, node)
43556     {
43557         var nodeRange = node.ownerDocument.createRange();
43558         try {
43559             nodeRange.selectNode(node);
43560         } catch (e) {
43561             nodeRange.selectNodeContents(node);
43562         }
43563     
43564         var rangeStartRange = range.cloneRange();
43565         rangeStartRange.collapse(true);
43566     
43567         var rangeEndRange = range.cloneRange();
43568         rangeEndRange.collapse(false);
43569     
43570         var nodeStartRange = nodeRange.cloneRange();
43571         nodeStartRange.collapse(true);
43572     
43573         var nodeEndRange = nodeRange.cloneRange();
43574         nodeEndRange.collapse(false);
43575     
43576         return rangeStartRange.compareBoundaryPoints(
43577                  Range.START_TO_START, nodeEndRange) == -1 &&
43578                rangeEndRange.compareBoundaryPoints(
43579                  Range.START_TO_START, nodeStartRange) == 1;
43580         
43581          
43582     },
43583     rangeCompareNode : function(range, node)
43584     {
43585         var nodeRange = node.ownerDocument.createRange();
43586         try {
43587             nodeRange.selectNode(node);
43588         } catch (e) {
43589             nodeRange.selectNodeContents(node);
43590         }
43591         
43592         
43593         range.collapse(true);
43594     
43595         nodeRange.collapse(true);
43596      
43597         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43598         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43599          
43600         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43601         
43602         var nodeIsBefore   =  ss == 1;
43603         var nodeIsAfter    = ee == -1;
43604         
43605         if (nodeIsBefore && nodeIsAfter) {
43606             return 0; // outer
43607         }
43608         if (!nodeIsBefore && nodeIsAfter) {
43609             return 1; //right trailed.
43610         }
43611         
43612         if (nodeIsBefore && !nodeIsAfter) {
43613             return 2;  // left trailed.
43614         }
43615         // fully contined.
43616         return 3;
43617     },
43618
43619     // private? - in a new class?
43620     cleanUpPaste :  function()
43621     {
43622         // cleans up the whole document..
43623         Roo.log('cleanuppaste');
43624         
43625         this.cleanUpChildren(this.doc.body);
43626         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43627         if (clean != this.doc.body.innerHTML) {
43628             this.doc.body.innerHTML = clean;
43629         }
43630         
43631     },
43632     
43633     cleanWordChars : function(input) {// change the chars to hex code
43634         var he = Roo.HtmlEditorCore;
43635         
43636         var output = input;
43637         Roo.each(he.swapCodes, function(sw) { 
43638             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43639             
43640             output = output.replace(swapper, sw[1]);
43641         });
43642         
43643         return output;
43644     },
43645     
43646     
43647     cleanUpChildren : function (n)
43648     {
43649         if (!n.childNodes.length) {
43650             return;
43651         }
43652         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43653            this.cleanUpChild(n.childNodes[i]);
43654         }
43655     },
43656     
43657     
43658         
43659     
43660     cleanUpChild : function (node)
43661     {
43662         var ed = this;
43663         //console.log(node);
43664         if (node.nodeName == "#text") {
43665             // clean up silly Windows -- stuff?
43666             return; 
43667         }
43668         if (node.nodeName == "#comment") {
43669             node.parentNode.removeChild(node);
43670             // clean up silly Windows -- stuff?
43671             return; 
43672         }
43673         var lcname = node.tagName.toLowerCase();
43674         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43675         // whitelist of tags..
43676         
43677         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43678             // remove node.
43679             node.parentNode.removeChild(node);
43680             return;
43681             
43682         }
43683         
43684         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43685         
43686         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43687         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43688         
43689         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43690         //    remove_keep_children = true;
43691         //}
43692         
43693         if (remove_keep_children) {
43694             this.cleanUpChildren(node);
43695             // inserts everything just before this node...
43696             while (node.childNodes.length) {
43697                 var cn = node.childNodes[0];
43698                 node.removeChild(cn);
43699                 node.parentNode.insertBefore(cn, node);
43700             }
43701             node.parentNode.removeChild(node);
43702             return;
43703         }
43704         
43705         if (!node.attributes || !node.attributes.length) {
43706             this.cleanUpChildren(node);
43707             return;
43708         }
43709         
43710         function cleanAttr(n,v)
43711         {
43712             
43713             if (v.match(/^\./) || v.match(/^\//)) {
43714                 return;
43715             }
43716             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43717                 return;
43718             }
43719             if (v.match(/^#/)) {
43720                 return;
43721             }
43722 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43723             node.removeAttribute(n);
43724             
43725         }
43726         
43727         var cwhite = this.cwhite;
43728         var cblack = this.cblack;
43729             
43730         function cleanStyle(n,v)
43731         {
43732             if (v.match(/expression/)) { //XSS?? should we even bother..
43733                 node.removeAttribute(n);
43734                 return;
43735             }
43736             
43737             var parts = v.split(/;/);
43738             var clean = [];
43739             
43740             Roo.each(parts, function(p) {
43741                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43742                 if (!p.length) {
43743                     return true;
43744                 }
43745                 var l = p.split(':').shift().replace(/\s+/g,'');
43746                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43747                 
43748                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43749 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43750                     //node.removeAttribute(n);
43751                     return true;
43752                 }
43753                 //Roo.log()
43754                 // only allow 'c whitelisted system attributes'
43755                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43756 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43757                     //node.removeAttribute(n);
43758                     return true;
43759                 }
43760                 
43761                 
43762                  
43763                 
43764                 clean.push(p);
43765                 return true;
43766             });
43767             if (clean.length) { 
43768                 node.setAttribute(n, clean.join(';'));
43769             } else {
43770                 node.removeAttribute(n);
43771             }
43772             
43773         }
43774         
43775         
43776         for (var i = node.attributes.length-1; i > -1 ; i--) {
43777             var a = node.attributes[i];
43778             //console.log(a);
43779             
43780             if (a.name.toLowerCase().substr(0,2)=='on')  {
43781                 node.removeAttribute(a.name);
43782                 continue;
43783             }
43784             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43785                 node.removeAttribute(a.name);
43786                 continue;
43787             }
43788             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43789                 cleanAttr(a.name,a.value); // fixme..
43790                 continue;
43791             }
43792             if (a.name == 'style') {
43793                 cleanStyle(a.name,a.value);
43794                 continue;
43795             }
43796             /// clean up MS crap..
43797             // tecnically this should be a list of valid class'es..
43798             
43799             
43800             if (a.name == 'class') {
43801                 if (a.value.match(/^Mso/)) {
43802                     node.className = '';
43803                 }
43804                 
43805                 if (a.value.match(/body/)) {
43806                     node.className = '';
43807                 }
43808                 continue;
43809             }
43810             
43811             // style cleanup!?
43812             // class cleanup?
43813             
43814         }
43815         
43816         
43817         this.cleanUpChildren(node);
43818         
43819         
43820     },
43821     
43822     /**
43823      * Clean up MS wordisms...
43824      */
43825     cleanWord : function(node)
43826     {
43827         
43828         
43829         if (!node) {
43830             this.cleanWord(this.doc.body);
43831             return;
43832         }
43833         if (node.nodeName == "#text") {
43834             // clean up silly Windows -- stuff?
43835             return; 
43836         }
43837         if (node.nodeName == "#comment") {
43838             node.parentNode.removeChild(node);
43839             // clean up silly Windows -- stuff?
43840             return; 
43841         }
43842         
43843         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43844             node.parentNode.removeChild(node);
43845             return;
43846         }
43847         
43848         // remove - but keep children..
43849         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43850             while (node.childNodes.length) {
43851                 var cn = node.childNodes[0];
43852                 node.removeChild(cn);
43853                 node.parentNode.insertBefore(cn, node);
43854             }
43855             node.parentNode.removeChild(node);
43856             this.iterateChildren(node, this.cleanWord);
43857             return;
43858         }
43859         // clean styles
43860         if (node.className.length) {
43861             
43862             var cn = node.className.split(/\W+/);
43863             var cna = [];
43864             Roo.each(cn, function(cls) {
43865                 if (cls.match(/Mso[a-zA-Z]+/)) {
43866                     return;
43867                 }
43868                 cna.push(cls);
43869             });
43870             node.className = cna.length ? cna.join(' ') : '';
43871             if (!cna.length) {
43872                 node.removeAttribute("class");
43873             }
43874         }
43875         
43876         if (node.hasAttribute("lang")) {
43877             node.removeAttribute("lang");
43878         }
43879         
43880         if (node.hasAttribute("style")) {
43881             
43882             var styles = node.getAttribute("style").split(";");
43883             var nstyle = [];
43884             Roo.each(styles, function(s) {
43885                 if (!s.match(/:/)) {
43886                     return;
43887                 }
43888                 var kv = s.split(":");
43889                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43890                     return;
43891                 }
43892                 // what ever is left... we allow.
43893                 nstyle.push(s);
43894             });
43895             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43896             if (!nstyle.length) {
43897                 node.removeAttribute('style');
43898             }
43899         }
43900         this.iterateChildren(node, this.cleanWord);
43901         
43902         
43903         
43904     },
43905     /**
43906      * iterateChildren of a Node, calling fn each time, using this as the scole..
43907      * @param {DomNode} node node to iterate children of.
43908      * @param {Function} fn method of this class to call on each item.
43909      */
43910     iterateChildren : function(node, fn)
43911     {
43912         if (!node.childNodes.length) {
43913                 return;
43914         }
43915         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43916            fn.call(this, node.childNodes[i])
43917         }
43918     },
43919     
43920     
43921     /**
43922      * cleanTableWidths.
43923      *
43924      * Quite often pasting from word etc.. results in tables with column and widths.
43925      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43926      *
43927      */
43928     cleanTableWidths : function(node)
43929     {
43930          
43931          
43932         if (!node) {
43933             this.cleanTableWidths(this.doc.body);
43934             return;
43935         }
43936         
43937         // ignore list...
43938         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43939             return; 
43940         }
43941         Roo.log(node.tagName);
43942         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43943             this.iterateChildren(node, this.cleanTableWidths);
43944             return;
43945         }
43946         if (node.hasAttribute('width')) {
43947             node.removeAttribute('width');
43948         }
43949         
43950          
43951         if (node.hasAttribute("style")) {
43952             // pretty basic...
43953             
43954             var styles = node.getAttribute("style").split(";");
43955             var nstyle = [];
43956             Roo.each(styles, function(s) {
43957                 if (!s.match(/:/)) {
43958                     return;
43959                 }
43960                 var kv = s.split(":");
43961                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43962                     return;
43963                 }
43964                 // what ever is left... we allow.
43965                 nstyle.push(s);
43966             });
43967             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43968             if (!nstyle.length) {
43969                 node.removeAttribute('style');
43970             }
43971         }
43972         
43973         this.iterateChildren(node, this.cleanTableWidths);
43974         
43975         
43976     },
43977     
43978     
43979     
43980     
43981     domToHTML : function(currentElement, depth, nopadtext) {
43982         
43983         depth = depth || 0;
43984         nopadtext = nopadtext || false;
43985     
43986         if (!currentElement) {
43987             return this.domToHTML(this.doc.body);
43988         }
43989         
43990         //Roo.log(currentElement);
43991         var j;
43992         var allText = false;
43993         var nodeName = currentElement.nodeName;
43994         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43995         
43996         if  (nodeName == '#text') {
43997             
43998             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43999         }
44000         
44001         
44002         var ret = '';
44003         if (nodeName != 'BODY') {
44004              
44005             var i = 0;
44006             // Prints the node tagName, such as <A>, <IMG>, etc
44007             if (tagName) {
44008                 var attr = [];
44009                 for(i = 0; i < currentElement.attributes.length;i++) {
44010                     // quoting?
44011                     var aname = currentElement.attributes.item(i).name;
44012                     if (!currentElement.attributes.item(i).value.length) {
44013                         continue;
44014                     }
44015                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44016                 }
44017                 
44018                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44019             } 
44020             else {
44021                 
44022                 // eack
44023             }
44024         } else {
44025             tagName = false;
44026         }
44027         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44028             return ret;
44029         }
44030         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44031             nopadtext = true;
44032         }
44033         
44034         
44035         // Traverse the tree
44036         i = 0;
44037         var currentElementChild = currentElement.childNodes.item(i);
44038         var allText = true;
44039         var innerHTML  = '';
44040         lastnode = '';
44041         while (currentElementChild) {
44042             // Formatting code (indent the tree so it looks nice on the screen)
44043             var nopad = nopadtext;
44044             if (lastnode == 'SPAN') {
44045                 nopad  = true;
44046             }
44047             // text
44048             if  (currentElementChild.nodeName == '#text') {
44049                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44050                 toadd = nopadtext ? toadd : toadd.trim();
44051                 if (!nopad && toadd.length > 80) {
44052                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44053                 }
44054                 innerHTML  += toadd;
44055                 
44056                 i++;
44057                 currentElementChild = currentElement.childNodes.item(i);
44058                 lastNode = '';
44059                 continue;
44060             }
44061             allText = false;
44062             
44063             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44064                 
44065             // Recursively traverse the tree structure of the child node
44066             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44067             lastnode = currentElementChild.nodeName;
44068             i++;
44069             currentElementChild=currentElement.childNodes.item(i);
44070         }
44071         
44072         ret += innerHTML;
44073         
44074         if (!allText) {
44075                 // The remaining code is mostly for formatting the tree
44076             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44077         }
44078         
44079         
44080         if (tagName) {
44081             ret+= "</"+tagName+">";
44082         }
44083         return ret;
44084         
44085     },
44086         
44087     applyBlacklists : function()
44088     {
44089         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44090         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44091         
44092         this.white = [];
44093         this.black = [];
44094         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44095             if (b.indexOf(tag) > -1) {
44096                 return;
44097             }
44098             this.white.push(tag);
44099             
44100         }, this);
44101         
44102         Roo.each(w, function(tag) {
44103             if (b.indexOf(tag) > -1) {
44104                 return;
44105             }
44106             if (this.white.indexOf(tag) > -1) {
44107                 return;
44108             }
44109             this.white.push(tag);
44110             
44111         }, this);
44112         
44113         
44114         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44115             if (w.indexOf(tag) > -1) {
44116                 return;
44117             }
44118             this.black.push(tag);
44119             
44120         }, this);
44121         
44122         Roo.each(b, function(tag) {
44123             if (w.indexOf(tag) > -1) {
44124                 return;
44125             }
44126             if (this.black.indexOf(tag) > -1) {
44127                 return;
44128             }
44129             this.black.push(tag);
44130             
44131         }, this);
44132         
44133         
44134         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44135         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44136         
44137         this.cwhite = [];
44138         this.cblack = [];
44139         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44140             if (b.indexOf(tag) > -1) {
44141                 return;
44142             }
44143             this.cwhite.push(tag);
44144             
44145         }, this);
44146         
44147         Roo.each(w, function(tag) {
44148             if (b.indexOf(tag) > -1) {
44149                 return;
44150             }
44151             if (this.cwhite.indexOf(tag) > -1) {
44152                 return;
44153             }
44154             this.cwhite.push(tag);
44155             
44156         }, this);
44157         
44158         
44159         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44160             if (w.indexOf(tag) > -1) {
44161                 return;
44162             }
44163             this.cblack.push(tag);
44164             
44165         }, this);
44166         
44167         Roo.each(b, function(tag) {
44168             if (w.indexOf(tag) > -1) {
44169                 return;
44170             }
44171             if (this.cblack.indexOf(tag) > -1) {
44172                 return;
44173             }
44174             this.cblack.push(tag);
44175             
44176         }, this);
44177     },
44178     
44179     setStylesheets : function(stylesheets)
44180     {
44181         if(typeof(stylesheets) == 'string'){
44182             Roo.get(this.iframe.contentDocument.head).createChild({
44183                 tag : 'link',
44184                 rel : 'stylesheet',
44185                 type : 'text/css',
44186                 href : stylesheets
44187             });
44188             
44189             return;
44190         }
44191         var _this = this;
44192      
44193         Roo.each(stylesheets, function(s) {
44194             if(!s.length){
44195                 return;
44196             }
44197             
44198             Roo.get(_this.iframe.contentDocument.head).createChild({
44199                 tag : 'link',
44200                 rel : 'stylesheet',
44201                 type : 'text/css',
44202                 href : s
44203             });
44204         });
44205
44206         
44207     },
44208     
44209     removeStylesheets : function()
44210     {
44211         var _this = this;
44212         
44213         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44214             s.remove();
44215         });
44216     }
44217     
44218     // hide stuff that is not compatible
44219     /**
44220      * @event blur
44221      * @hide
44222      */
44223     /**
44224      * @event change
44225      * @hide
44226      */
44227     /**
44228      * @event focus
44229      * @hide
44230      */
44231     /**
44232      * @event specialkey
44233      * @hide
44234      */
44235     /**
44236      * @cfg {String} fieldClass @hide
44237      */
44238     /**
44239      * @cfg {String} focusClass @hide
44240      */
44241     /**
44242      * @cfg {String} autoCreate @hide
44243      */
44244     /**
44245      * @cfg {String} inputType @hide
44246      */
44247     /**
44248      * @cfg {String} invalidClass @hide
44249      */
44250     /**
44251      * @cfg {String} invalidText @hide
44252      */
44253     /**
44254      * @cfg {String} msgFx @hide
44255      */
44256     /**
44257      * @cfg {String} validateOnBlur @hide
44258      */
44259 });
44260
44261 Roo.HtmlEditorCore.white = [
44262         'area', 'br', 'img', 'input', 'hr', 'wbr',
44263         
44264        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44265        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44266        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44267        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44268        'table',   'ul',         'xmp', 
44269        
44270        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44271       'thead',   'tr', 
44272      
44273       'dir', 'menu', 'ol', 'ul', 'dl',
44274        
44275       'embed',  'object'
44276 ];
44277
44278
44279 Roo.HtmlEditorCore.black = [
44280     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44281         'applet', // 
44282         'base',   'basefont', 'bgsound', 'blink',  'body', 
44283         'frame',  'frameset', 'head',    'html',   'ilayer', 
44284         'iframe', 'layer',  'link',     'meta',    'object',   
44285         'script', 'style' ,'title',  'xml' // clean later..
44286 ];
44287 Roo.HtmlEditorCore.clean = [
44288     'script', 'style', 'title', 'xml'
44289 ];
44290 Roo.HtmlEditorCore.remove = [
44291     'font'
44292 ];
44293 // attributes..
44294
44295 Roo.HtmlEditorCore.ablack = [
44296     'on'
44297 ];
44298     
44299 Roo.HtmlEditorCore.aclean = [ 
44300     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44301 ];
44302
44303 // protocols..
44304 Roo.HtmlEditorCore.pwhite= [
44305         'http',  'https',  'mailto'
44306 ];
44307
44308 // white listed style attributes.
44309 Roo.HtmlEditorCore.cwhite= [
44310       //  'text-align', /// default is to allow most things..
44311       
44312          
44313 //        'font-size'//??
44314 ];
44315
44316 // black listed style attributes.
44317 Roo.HtmlEditorCore.cblack= [
44318       //  'font-size' -- this can be set by the project 
44319 ];
44320
44321
44322 Roo.HtmlEditorCore.swapCodes   =[ 
44323     [    8211, "--" ], 
44324     [    8212, "--" ], 
44325     [    8216,  "'" ],  
44326     [    8217, "'" ],  
44327     [    8220, '"' ],  
44328     [    8221, '"' ],  
44329     [    8226, "*" ],  
44330     [    8230, "..." ]
44331 ]; 
44332
44333     //<script type="text/javascript">
44334
44335 /*
44336  * Ext JS Library 1.1.1
44337  * Copyright(c) 2006-2007, Ext JS, LLC.
44338  * Licence LGPL
44339  * 
44340  */
44341  
44342  
44343 Roo.form.HtmlEditor = function(config){
44344     
44345     
44346     
44347     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44348     
44349     if (!this.toolbars) {
44350         this.toolbars = [];
44351     }
44352     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44353     
44354     
44355 };
44356
44357 /**
44358  * @class Roo.form.HtmlEditor
44359  * @extends Roo.form.Field
44360  * Provides a lightweight HTML Editor component.
44361  *
44362  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44363  * 
44364  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44365  * supported by this editor.</b><br/><br/>
44366  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44367  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44368  */
44369 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44370     /**
44371      * @cfg {Boolean} clearUp
44372      */
44373     clearUp : true,
44374       /**
44375      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44376      */
44377     toolbars : false,
44378    
44379      /**
44380      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44381      *                        Roo.resizable.
44382      */
44383     resizable : false,
44384      /**
44385      * @cfg {Number} height (in pixels)
44386      */   
44387     height: 300,
44388    /**
44389      * @cfg {Number} width (in pixels)
44390      */   
44391     width: 500,
44392     
44393     /**
44394      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44395      * 
44396      */
44397     stylesheets: false,
44398     
44399     
44400      /**
44401      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44402      * 
44403      */
44404     cblack: false,
44405     /**
44406      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44407      * 
44408      */
44409     cwhite: false,
44410     
44411      /**
44412      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44413      * 
44414      */
44415     black: false,
44416     /**
44417      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44418      * 
44419      */
44420     white: false,
44421     
44422     // id of frame..
44423     frameId: false,
44424     
44425     // private properties
44426     validationEvent : false,
44427     deferHeight: true,
44428     initialized : false,
44429     activated : false,
44430     
44431     onFocus : Roo.emptyFn,
44432     iframePad:3,
44433     hideMode:'offsets',
44434     
44435     actionMode : 'container', // defaults to hiding it...
44436     
44437     defaultAutoCreate : { // modified by initCompnoent..
44438         tag: "textarea",
44439         style:"width:500px;height:300px;",
44440         autocomplete: "new-password"
44441     },
44442
44443     // private
44444     initComponent : function(){
44445         this.addEvents({
44446             /**
44447              * @event initialize
44448              * Fires when the editor is fully initialized (including the iframe)
44449              * @param {HtmlEditor} this
44450              */
44451             initialize: true,
44452             /**
44453              * @event activate
44454              * Fires when the editor is first receives the focus. Any insertion must wait
44455              * until after this event.
44456              * @param {HtmlEditor} this
44457              */
44458             activate: true,
44459              /**
44460              * @event beforesync
44461              * Fires before the textarea is updated with content from the editor iframe. Return false
44462              * to cancel the sync.
44463              * @param {HtmlEditor} this
44464              * @param {String} html
44465              */
44466             beforesync: true,
44467              /**
44468              * @event beforepush
44469              * Fires before the iframe editor is updated with content from the textarea. Return false
44470              * to cancel the push.
44471              * @param {HtmlEditor} this
44472              * @param {String} html
44473              */
44474             beforepush: true,
44475              /**
44476              * @event sync
44477              * Fires when the textarea is updated with content from the editor iframe.
44478              * @param {HtmlEditor} this
44479              * @param {String} html
44480              */
44481             sync: true,
44482              /**
44483              * @event push
44484              * Fires when the iframe editor is updated with content from the textarea.
44485              * @param {HtmlEditor} this
44486              * @param {String} html
44487              */
44488             push: true,
44489              /**
44490              * @event editmodechange
44491              * Fires when the editor switches edit modes
44492              * @param {HtmlEditor} this
44493              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44494              */
44495             editmodechange: true,
44496             /**
44497              * @event editorevent
44498              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44499              * @param {HtmlEditor} this
44500              */
44501             editorevent: true,
44502             /**
44503              * @event firstfocus
44504              * Fires when on first focus - needed by toolbars..
44505              * @param {HtmlEditor} this
44506              */
44507             firstfocus: true,
44508             /**
44509              * @event autosave
44510              * Auto save the htmlEditor value as a file into Events
44511              * @param {HtmlEditor} this
44512              */
44513             autosave: true,
44514             /**
44515              * @event savedpreview
44516              * preview the saved version of htmlEditor
44517              * @param {HtmlEditor} this
44518              */
44519             savedpreview: true,
44520             
44521             /**
44522             * @event stylesheetsclick
44523             * Fires when press the Sytlesheets button
44524             * @param {Roo.HtmlEditorCore} this
44525             */
44526             stylesheetsclick: true
44527         });
44528         this.defaultAutoCreate =  {
44529             tag: "textarea",
44530             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44531             autocomplete: "new-password"
44532         };
44533     },
44534
44535     /**
44536      * Protected method that will not generally be called directly. It
44537      * is called when the editor creates its toolbar. Override this method if you need to
44538      * add custom toolbar buttons.
44539      * @param {HtmlEditor} editor
44540      */
44541     createToolbar : function(editor){
44542         Roo.log("create toolbars");
44543         if (!editor.toolbars || !editor.toolbars.length) {
44544             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44545         }
44546         
44547         for (var i =0 ; i < editor.toolbars.length;i++) {
44548             editor.toolbars[i] = Roo.factory(
44549                     typeof(editor.toolbars[i]) == 'string' ?
44550                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44551                 Roo.form.HtmlEditor);
44552             editor.toolbars[i].init(editor);
44553         }
44554          
44555         
44556     },
44557
44558      
44559     // private
44560     onRender : function(ct, position)
44561     {
44562         var _t = this;
44563         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44564         
44565         this.wrap = this.el.wrap({
44566             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44567         });
44568         
44569         this.editorcore.onRender(ct, position);
44570          
44571         if (this.resizable) {
44572             this.resizeEl = new Roo.Resizable(this.wrap, {
44573                 pinned : true,
44574                 wrap: true,
44575                 dynamic : true,
44576                 minHeight : this.height,
44577                 height: this.height,
44578                 handles : this.resizable,
44579                 width: this.width,
44580                 listeners : {
44581                     resize : function(r, w, h) {
44582                         _t.onResize(w,h); // -something
44583                     }
44584                 }
44585             });
44586             
44587         }
44588         this.createToolbar(this);
44589        
44590         
44591         if(!this.width){
44592             this.setSize(this.wrap.getSize());
44593         }
44594         if (this.resizeEl) {
44595             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44596             // should trigger onReize..
44597         }
44598         
44599         this.keyNav = new Roo.KeyNav(this.el, {
44600             
44601             "tab" : function(e){
44602                 e.preventDefault();
44603                 
44604                 var value = this.getValue();
44605                 
44606                 var start = this.el.dom.selectionStart;
44607                 var end = this.el.dom.selectionEnd;
44608                 
44609                 if(!e.shiftKey){
44610                     
44611                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44612                     this.el.dom.setSelectionRange(end + 1, end + 1);
44613                     return;
44614                 }
44615                 
44616                 var f = value.substring(0, start).split("\t");
44617                 
44618                 if(f.pop().length != 0){
44619                     return;
44620                 }
44621                 
44622                 this.setValue(f.join("\t") + value.substring(end));
44623                 this.el.dom.setSelectionRange(start - 1, start - 1);
44624                 
44625             },
44626             
44627             "home" : function(e){
44628                 e.preventDefault();
44629                 
44630                 var curr = this.el.dom.selectionStart;
44631                 var lines = this.getValue().split("\n");
44632                 
44633                 if(!lines.length){
44634                     return;
44635                 }
44636                 
44637                 if(e.ctrlKey){
44638                     this.el.dom.setSelectionRange(0, 0);
44639                     return;
44640                 }
44641                 
44642                 var pos = 0;
44643                 
44644                 for (var i = 0; i < lines.length;i++) {
44645                     pos += lines[i].length;
44646                     
44647                     if(i != 0){
44648                         pos += 1;
44649                     }
44650                     
44651                     if(pos < curr){
44652                         continue;
44653                     }
44654                     
44655                     pos -= lines[i].length;
44656                     
44657                     break;
44658                 }
44659                 
44660                 if(!e.shiftKey){
44661                     this.el.dom.setSelectionRange(pos, pos);
44662                     return;
44663                 }
44664                 
44665                 this.el.dom.selectionStart = pos;
44666                 this.el.dom.selectionEnd = curr;
44667             },
44668             
44669             "end" : function(e){
44670                 e.preventDefault();
44671                 
44672                 var curr = this.el.dom.selectionStart;
44673                 var lines = this.getValue().split("\n");
44674                 
44675                 if(!lines.length){
44676                     return;
44677                 }
44678                 
44679                 if(e.ctrlKey){
44680                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44681                     return;
44682                 }
44683                 
44684                 var pos = 0;
44685                 
44686                 for (var i = 0; i < lines.length;i++) {
44687                     
44688                     pos += lines[i].length;
44689                     
44690                     if(i != 0){
44691                         pos += 1;
44692                     }
44693                     
44694                     if(pos < curr){
44695                         continue;
44696                     }
44697                     
44698                     break;
44699                 }
44700                 
44701                 if(!e.shiftKey){
44702                     this.el.dom.setSelectionRange(pos, pos);
44703                     return;
44704                 }
44705                 
44706                 this.el.dom.selectionStart = curr;
44707                 this.el.dom.selectionEnd = pos;
44708             },
44709
44710             scope : this,
44711
44712             doRelay : function(foo, bar, hname){
44713                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44714             },
44715
44716             forceKeyDown: true
44717         });
44718         
44719 //        if(this.autosave && this.w){
44720 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44721 //        }
44722     },
44723
44724     // private
44725     onResize : function(w, h)
44726     {
44727         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44728         var ew = false;
44729         var eh = false;
44730         
44731         if(this.el ){
44732             if(typeof w == 'number'){
44733                 var aw = w - this.wrap.getFrameWidth('lr');
44734                 this.el.setWidth(this.adjustWidth('textarea', aw));
44735                 ew = aw;
44736             }
44737             if(typeof h == 'number'){
44738                 var tbh = 0;
44739                 for (var i =0; i < this.toolbars.length;i++) {
44740                     // fixme - ask toolbars for heights?
44741                     tbh += this.toolbars[i].tb.el.getHeight();
44742                     if (this.toolbars[i].footer) {
44743                         tbh += this.toolbars[i].footer.el.getHeight();
44744                     }
44745                 }
44746                 
44747                 
44748                 
44749                 
44750                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44751                 ah -= 5; // knock a few pixes off for look..
44752 //                Roo.log(ah);
44753                 this.el.setHeight(this.adjustWidth('textarea', ah));
44754                 var eh = ah;
44755             }
44756         }
44757         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44758         this.editorcore.onResize(ew,eh);
44759         
44760     },
44761
44762     /**
44763      * Toggles the editor between standard and source edit mode.
44764      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44765      */
44766     toggleSourceEdit : function(sourceEditMode)
44767     {
44768         this.editorcore.toggleSourceEdit(sourceEditMode);
44769         
44770         if(this.editorcore.sourceEditMode){
44771             Roo.log('editor - showing textarea');
44772             
44773 //            Roo.log('in');
44774 //            Roo.log(this.syncValue());
44775             this.editorcore.syncValue();
44776             this.el.removeClass('x-hidden');
44777             this.el.dom.removeAttribute('tabIndex');
44778             this.el.focus();
44779             
44780             for (var i = 0; i < this.toolbars.length; i++) {
44781                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44782                     this.toolbars[i].tb.hide();
44783                     this.toolbars[i].footer.hide();
44784                 }
44785             }
44786             
44787         }else{
44788             Roo.log('editor - hiding textarea');
44789 //            Roo.log('out')
44790 //            Roo.log(this.pushValue()); 
44791             this.editorcore.pushValue();
44792             
44793             this.el.addClass('x-hidden');
44794             this.el.dom.setAttribute('tabIndex', -1);
44795             
44796             for (var i = 0; i < this.toolbars.length; i++) {
44797                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44798                     this.toolbars[i].tb.show();
44799                     this.toolbars[i].footer.show();
44800                 }
44801             }
44802             
44803             //this.deferFocus();
44804         }
44805         
44806         this.setSize(this.wrap.getSize());
44807         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44808         
44809         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44810     },
44811  
44812     // private (for BoxComponent)
44813     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44814
44815     // private (for BoxComponent)
44816     getResizeEl : function(){
44817         return this.wrap;
44818     },
44819
44820     // private (for BoxComponent)
44821     getPositionEl : function(){
44822         return this.wrap;
44823     },
44824
44825     // private
44826     initEvents : function(){
44827         this.originalValue = this.getValue();
44828     },
44829
44830     /**
44831      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44832      * @method
44833      */
44834     markInvalid : Roo.emptyFn,
44835     /**
44836      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44837      * @method
44838      */
44839     clearInvalid : Roo.emptyFn,
44840
44841     setValue : function(v){
44842         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44843         this.editorcore.pushValue();
44844     },
44845
44846      
44847     // private
44848     deferFocus : function(){
44849         this.focus.defer(10, this);
44850     },
44851
44852     // doc'ed in Field
44853     focus : function(){
44854         this.editorcore.focus();
44855         
44856     },
44857       
44858
44859     // private
44860     onDestroy : function(){
44861         
44862         
44863         
44864         if(this.rendered){
44865             
44866             for (var i =0; i < this.toolbars.length;i++) {
44867                 // fixme - ask toolbars for heights?
44868                 this.toolbars[i].onDestroy();
44869             }
44870             
44871             this.wrap.dom.innerHTML = '';
44872             this.wrap.remove();
44873         }
44874     },
44875
44876     // private
44877     onFirstFocus : function(){
44878         //Roo.log("onFirstFocus");
44879         this.editorcore.onFirstFocus();
44880          for (var i =0; i < this.toolbars.length;i++) {
44881             this.toolbars[i].onFirstFocus();
44882         }
44883         
44884     },
44885     
44886     // private
44887     syncValue : function()
44888     {
44889         this.editorcore.syncValue();
44890     },
44891     
44892     pushValue : function()
44893     {
44894         this.editorcore.pushValue();
44895     },
44896     
44897     setStylesheets : function(stylesheets)
44898     {
44899         this.editorcore.setStylesheets(stylesheets);
44900     },
44901     
44902     removeStylesheets : function()
44903     {
44904         this.editorcore.removeStylesheets();
44905     }
44906      
44907     
44908     // hide stuff that is not compatible
44909     /**
44910      * @event blur
44911      * @hide
44912      */
44913     /**
44914      * @event change
44915      * @hide
44916      */
44917     /**
44918      * @event focus
44919      * @hide
44920      */
44921     /**
44922      * @event specialkey
44923      * @hide
44924      */
44925     /**
44926      * @cfg {String} fieldClass @hide
44927      */
44928     /**
44929      * @cfg {String} focusClass @hide
44930      */
44931     /**
44932      * @cfg {String} autoCreate @hide
44933      */
44934     /**
44935      * @cfg {String} inputType @hide
44936      */
44937     /**
44938      * @cfg {String} invalidClass @hide
44939      */
44940     /**
44941      * @cfg {String} invalidText @hide
44942      */
44943     /**
44944      * @cfg {String} msgFx @hide
44945      */
44946     /**
44947      * @cfg {String} validateOnBlur @hide
44948      */
44949 });
44950  
44951     // <script type="text/javascript">
44952 /*
44953  * Based on
44954  * Ext JS Library 1.1.1
44955  * Copyright(c) 2006-2007, Ext JS, LLC.
44956  *  
44957  
44958  */
44959
44960 /**
44961  * @class Roo.form.HtmlEditorToolbar1
44962  * Basic Toolbar
44963  * 
44964  * Usage:
44965  *
44966  new Roo.form.HtmlEditor({
44967     ....
44968     toolbars : [
44969         new Roo.form.HtmlEditorToolbar1({
44970             disable : { fonts: 1 , format: 1, ..., ... , ...],
44971             btns : [ .... ]
44972         })
44973     }
44974      
44975  * 
44976  * @cfg {Object} disable List of elements to disable..
44977  * @cfg {Array} btns List of additional buttons.
44978  * 
44979  * 
44980  * NEEDS Extra CSS? 
44981  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44982  */
44983  
44984 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44985 {
44986     
44987     Roo.apply(this, config);
44988     
44989     // default disabled, based on 'good practice'..
44990     this.disable = this.disable || {};
44991     Roo.applyIf(this.disable, {
44992         fontSize : true,
44993         colors : true,
44994         specialElements : true
44995     });
44996     
44997     
44998     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44999     // dont call parent... till later.
45000 }
45001
45002 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45003     
45004     tb: false,
45005     
45006     rendered: false,
45007     
45008     editor : false,
45009     editorcore : false,
45010     /**
45011      * @cfg {Object} disable  List of toolbar elements to disable
45012          
45013      */
45014     disable : false,
45015     
45016     
45017      /**
45018      * @cfg {String} createLinkText The default text for the create link prompt
45019      */
45020     createLinkText : 'Please enter the URL for the link:',
45021     /**
45022      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45023      */
45024     defaultLinkValue : 'http:/'+'/',
45025    
45026     
45027       /**
45028      * @cfg {Array} fontFamilies An array of available font families
45029      */
45030     fontFamilies : [
45031         'Arial',
45032         'Courier New',
45033         'Tahoma',
45034         'Times New Roman',
45035         'Verdana'
45036     ],
45037     
45038     specialChars : [
45039            "&#169;",
45040           "&#174;",     
45041           "&#8482;",    
45042           "&#163;" ,    
45043          // "&#8212;",    
45044           "&#8230;",    
45045           "&#247;" ,    
45046         //  "&#225;" ,     ?? a acute?
45047            "&#8364;"    , //Euro
45048        //   "&#8220;"    ,
45049         //  "&#8221;"    ,
45050         //  "&#8226;"    ,
45051           "&#176;"  //   , // degrees
45052
45053          // "&#233;"     , // e ecute
45054          // "&#250;"     , // u ecute?
45055     ],
45056     
45057     specialElements : [
45058         {
45059             text: "Insert Table",
45060             xtype: 'MenuItem',
45061             xns : Roo.Menu,
45062             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45063                 
45064         },
45065         {    
45066             text: "Insert Image",
45067             xtype: 'MenuItem',
45068             xns : Roo.Menu,
45069             ihtml : '<img src="about:blank"/>'
45070             
45071         }
45072         
45073          
45074     ],
45075     
45076     
45077     inputElements : [ 
45078             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45079             "input:submit", "input:button", "select", "textarea", "label" ],
45080     formats : [
45081         ["p"] ,  
45082         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45083         ["pre"],[ "code"], 
45084         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45085         ['div'],['span']
45086     ],
45087     
45088     cleanStyles : [
45089         "font-size"
45090     ],
45091      /**
45092      * @cfg {String} defaultFont default font to use.
45093      */
45094     defaultFont: 'tahoma',
45095    
45096     fontSelect : false,
45097     
45098     
45099     formatCombo : false,
45100     
45101     init : function(editor)
45102     {
45103         this.editor = editor;
45104         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45105         var editorcore = this.editorcore;
45106         
45107         var _t = this;
45108         
45109         var fid = editorcore.frameId;
45110         var etb = this;
45111         function btn(id, toggle, handler){
45112             var xid = fid + '-'+ id ;
45113             return {
45114                 id : xid,
45115                 cmd : id,
45116                 cls : 'x-btn-icon x-edit-'+id,
45117                 enableToggle:toggle !== false,
45118                 scope: _t, // was editor...
45119                 handler:handler||_t.relayBtnCmd,
45120                 clickEvent:'mousedown',
45121                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45122                 tabIndex:-1
45123             };
45124         }
45125         
45126         
45127         
45128         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45129         this.tb = tb;
45130          // stop form submits
45131         tb.el.on('click', function(e){
45132             e.preventDefault(); // what does this do?
45133         });
45134
45135         if(!this.disable.font) { // && !Roo.isSafari){
45136             /* why no safari for fonts 
45137             editor.fontSelect = tb.el.createChild({
45138                 tag:'select',
45139                 tabIndex: -1,
45140                 cls:'x-font-select',
45141                 html: this.createFontOptions()
45142             });
45143             
45144             editor.fontSelect.on('change', function(){
45145                 var font = editor.fontSelect.dom.value;
45146                 editor.relayCmd('fontname', font);
45147                 editor.deferFocus();
45148             }, editor);
45149             
45150             tb.add(
45151                 editor.fontSelect.dom,
45152                 '-'
45153             );
45154             */
45155             
45156         };
45157         if(!this.disable.formats){
45158             this.formatCombo = new Roo.form.ComboBox({
45159                 store: new Roo.data.SimpleStore({
45160                     id : 'tag',
45161                     fields: ['tag'],
45162                     data : this.formats // from states.js
45163                 }),
45164                 blockFocus : true,
45165                 name : '',
45166                 //autoCreate : {tag: "div",  size: "20"},
45167                 displayField:'tag',
45168                 typeAhead: false,
45169                 mode: 'local',
45170                 editable : false,
45171                 triggerAction: 'all',
45172                 emptyText:'Add tag',
45173                 selectOnFocus:true,
45174                 width:135,
45175                 listeners : {
45176                     'select': function(c, r, i) {
45177                         editorcore.insertTag(r.get('tag'));
45178                         editor.focus();
45179                     }
45180                 }
45181
45182             });
45183             tb.addField(this.formatCombo);
45184             
45185         }
45186         
45187         if(!this.disable.format){
45188             tb.add(
45189                 btn('bold'),
45190                 btn('italic'),
45191                 btn('underline'),
45192                 btn('strikethrough')
45193             );
45194         };
45195         if(!this.disable.fontSize){
45196             tb.add(
45197                 '-',
45198                 
45199                 
45200                 btn('increasefontsize', false, editorcore.adjustFont),
45201                 btn('decreasefontsize', false, editorcore.adjustFont)
45202             );
45203         };
45204         
45205         
45206         if(!this.disable.colors){
45207             tb.add(
45208                 '-', {
45209                     id:editorcore.frameId +'-forecolor',
45210                     cls:'x-btn-icon x-edit-forecolor',
45211                     clickEvent:'mousedown',
45212                     tooltip: this.buttonTips['forecolor'] || undefined,
45213                     tabIndex:-1,
45214                     menu : new Roo.menu.ColorMenu({
45215                         allowReselect: true,
45216                         focus: Roo.emptyFn,
45217                         value:'000000',
45218                         plain:true,
45219                         selectHandler: function(cp, color){
45220                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45221                             editor.deferFocus();
45222                         },
45223                         scope: editorcore,
45224                         clickEvent:'mousedown'
45225                     })
45226                 }, {
45227                     id:editorcore.frameId +'backcolor',
45228                     cls:'x-btn-icon x-edit-backcolor',
45229                     clickEvent:'mousedown',
45230                     tooltip: this.buttonTips['backcolor'] || undefined,
45231                     tabIndex:-1,
45232                     menu : new Roo.menu.ColorMenu({
45233                         focus: Roo.emptyFn,
45234                         value:'FFFFFF',
45235                         plain:true,
45236                         allowReselect: true,
45237                         selectHandler: function(cp, color){
45238                             if(Roo.isGecko){
45239                                 editorcore.execCmd('useCSS', false);
45240                                 editorcore.execCmd('hilitecolor', color);
45241                                 editorcore.execCmd('useCSS', true);
45242                                 editor.deferFocus();
45243                             }else{
45244                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45245                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45246                                 editor.deferFocus();
45247                             }
45248                         },
45249                         scope:editorcore,
45250                         clickEvent:'mousedown'
45251                     })
45252                 }
45253             );
45254         };
45255         // now add all the items...
45256         
45257
45258         if(!this.disable.alignments){
45259             tb.add(
45260                 '-',
45261                 btn('justifyleft'),
45262                 btn('justifycenter'),
45263                 btn('justifyright')
45264             );
45265         };
45266
45267         //if(!Roo.isSafari){
45268             if(!this.disable.links){
45269                 tb.add(
45270                     '-',
45271                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45272                 );
45273             };
45274
45275             if(!this.disable.lists){
45276                 tb.add(
45277                     '-',
45278                     btn('insertorderedlist'),
45279                     btn('insertunorderedlist')
45280                 );
45281             }
45282             if(!this.disable.sourceEdit){
45283                 tb.add(
45284                     '-',
45285                     btn('sourceedit', true, function(btn){
45286                         this.toggleSourceEdit(btn.pressed);
45287                     })
45288                 );
45289             }
45290         //}
45291         
45292         var smenu = { };
45293         // special menu.. - needs to be tidied up..
45294         if (!this.disable.special) {
45295             smenu = {
45296                 text: "&#169;",
45297                 cls: 'x-edit-none',
45298                 
45299                 menu : {
45300                     items : []
45301                 }
45302             };
45303             for (var i =0; i < this.specialChars.length; i++) {
45304                 smenu.menu.items.push({
45305                     
45306                     html: this.specialChars[i],
45307                     handler: function(a,b) {
45308                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45309                         //editor.insertAtCursor(a.html);
45310                         
45311                     },
45312                     tabIndex:-1
45313                 });
45314             }
45315             
45316             
45317             tb.add(smenu);
45318             
45319             
45320         }
45321         
45322         var cmenu = { };
45323         if (!this.disable.cleanStyles) {
45324             cmenu = {
45325                 cls: 'x-btn-icon x-btn-clear',
45326                 
45327                 menu : {
45328                     items : []
45329                 }
45330             };
45331             for (var i =0; i < this.cleanStyles.length; i++) {
45332                 cmenu.menu.items.push({
45333                     actiontype : this.cleanStyles[i],
45334                     html: 'Remove ' + this.cleanStyles[i],
45335                     handler: function(a,b) {
45336 //                        Roo.log(a);
45337 //                        Roo.log(b);
45338                         var c = Roo.get(editorcore.doc.body);
45339                         c.select('[style]').each(function(s) {
45340                             s.dom.style.removeProperty(a.actiontype);
45341                         });
45342                         editorcore.syncValue();
45343                     },
45344                     tabIndex:-1
45345                 });
45346             }
45347              cmenu.menu.items.push({
45348                 actiontype : 'tablewidths',
45349                 html: 'Remove Table Widths',
45350                 handler: function(a,b) {
45351                     editorcore.cleanTableWidths();
45352                     editorcore.syncValue();
45353                 },
45354                 tabIndex:-1
45355             });
45356             cmenu.menu.items.push({
45357                 actiontype : 'word',
45358                 html: 'Remove MS Word Formating',
45359                 handler: function(a,b) {
45360                     editorcore.cleanWord();
45361                     editorcore.syncValue();
45362                 },
45363                 tabIndex:-1
45364             });
45365             
45366             cmenu.menu.items.push({
45367                 actiontype : 'all',
45368                 html: 'Remove All Styles',
45369                 handler: function(a,b) {
45370                     
45371                     var c = Roo.get(editorcore.doc.body);
45372                     c.select('[style]').each(function(s) {
45373                         s.dom.removeAttribute('style');
45374                     });
45375                     editorcore.syncValue();
45376                 },
45377                 tabIndex:-1
45378             });
45379             
45380             cmenu.menu.items.push({
45381                 actiontype : 'all',
45382                 html: 'Remove All CSS Classes',
45383                 handler: function(a,b) {
45384                     
45385                     var c = Roo.get(editorcore.doc.body);
45386                     c.select('[class]').each(function(s) {
45387                         s.dom.className = '';
45388                     });
45389                     editorcore.syncValue();
45390                 },
45391                 tabIndex:-1
45392             });
45393             
45394              cmenu.menu.items.push({
45395                 actiontype : 'tidy',
45396                 html: 'Tidy HTML Source',
45397                 handler: function(a,b) {
45398                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45399                     editorcore.syncValue();
45400                 },
45401                 tabIndex:-1
45402             });
45403             
45404             
45405             tb.add(cmenu);
45406         }
45407          
45408         if (!this.disable.specialElements) {
45409             var semenu = {
45410                 text: "Other;",
45411                 cls: 'x-edit-none',
45412                 menu : {
45413                     items : []
45414                 }
45415             };
45416             for (var i =0; i < this.specialElements.length; i++) {
45417                 semenu.menu.items.push(
45418                     Roo.apply({ 
45419                         handler: function(a,b) {
45420                             editor.insertAtCursor(this.ihtml);
45421                         }
45422                     }, this.specialElements[i])
45423                 );
45424                     
45425             }
45426             
45427             tb.add(semenu);
45428             
45429             
45430         }
45431          
45432         
45433         if (this.btns) {
45434             for(var i =0; i< this.btns.length;i++) {
45435                 var b = Roo.factory(this.btns[i],Roo.form);
45436                 b.cls =  'x-edit-none';
45437                 
45438                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45439                     b.cls += ' x-init-enable';
45440                 }
45441                 
45442                 b.scope = editorcore;
45443                 tb.add(b);
45444             }
45445         
45446         }
45447         
45448         
45449         
45450         // disable everything...
45451         
45452         this.tb.items.each(function(item){
45453             
45454            if(
45455                 item.id != editorcore.frameId+ '-sourceedit' && 
45456                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45457             ){
45458                 
45459                 item.disable();
45460             }
45461         });
45462         this.rendered = true;
45463         
45464         // the all the btns;
45465         editor.on('editorevent', this.updateToolbar, this);
45466         // other toolbars need to implement this..
45467         //editor.on('editmodechange', this.updateToolbar, this);
45468     },
45469     
45470     
45471     relayBtnCmd : function(btn) {
45472         this.editorcore.relayCmd(btn.cmd);
45473     },
45474     // private used internally
45475     createLink : function(){
45476         Roo.log("create link?");
45477         var url = prompt(this.createLinkText, this.defaultLinkValue);
45478         if(url && url != 'http:/'+'/'){
45479             this.editorcore.relayCmd('createlink', url);
45480         }
45481     },
45482
45483     
45484     /**
45485      * Protected method that will not generally be called directly. It triggers
45486      * a toolbar update by reading the markup state of the current selection in the editor.
45487      */
45488     updateToolbar: function(){
45489
45490         if(!this.editorcore.activated){
45491             this.editor.onFirstFocus();
45492             return;
45493         }
45494
45495         var btns = this.tb.items.map, 
45496             doc = this.editorcore.doc,
45497             frameId = this.editorcore.frameId;
45498
45499         if(!this.disable.font && !Roo.isSafari){
45500             /*
45501             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45502             if(name != this.fontSelect.dom.value){
45503                 this.fontSelect.dom.value = name;
45504             }
45505             */
45506         }
45507         if(!this.disable.format){
45508             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45509             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45510             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45511             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45512         }
45513         if(!this.disable.alignments){
45514             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45515             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45516             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45517         }
45518         if(!Roo.isSafari && !this.disable.lists){
45519             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45520             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45521         }
45522         
45523         var ans = this.editorcore.getAllAncestors();
45524         if (this.formatCombo) {
45525             
45526             
45527             var store = this.formatCombo.store;
45528             this.formatCombo.setValue("");
45529             for (var i =0; i < ans.length;i++) {
45530                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45531                     // select it..
45532                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45533                     break;
45534                 }
45535             }
45536         }
45537         
45538         
45539         
45540         // hides menus... - so this cant be on a menu...
45541         Roo.menu.MenuMgr.hideAll();
45542
45543         //this.editorsyncValue();
45544     },
45545    
45546     
45547     createFontOptions : function(){
45548         var buf = [], fs = this.fontFamilies, ff, lc;
45549         
45550         
45551         
45552         for(var i = 0, len = fs.length; i< len; i++){
45553             ff = fs[i];
45554             lc = ff.toLowerCase();
45555             buf.push(
45556                 '<option value="',lc,'" style="font-family:',ff,';"',
45557                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45558                     ff,
45559                 '</option>'
45560             );
45561         }
45562         return buf.join('');
45563     },
45564     
45565     toggleSourceEdit : function(sourceEditMode){
45566         
45567         Roo.log("toolbar toogle");
45568         if(sourceEditMode === undefined){
45569             sourceEditMode = !this.sourceEditMode;
45570         }
45571         this.sourceEditMode = sourceEditMode === true;
45572         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45573         // just toggle the button?
45574         if(btn.pressed !== this.sourceEditMode){
45575             btn.toggle(this.sourceEditMode);
45576             return;
45577         }
45578         
45579         if(sourceEditMode){
45580             Roo.log("disabling buttons");
45581             this.tb.items.each(function(item){
45582                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45583                     item.disable();
45584                 }
45585             });
45586           
45587         }else{
45588             Roo.log("enabling buttons");
45589             if(this.editorcore.initialized){
45590                 this.tb.items.each(function(item){
45591                     item.enable();
45592                 });
45593             }
45594             
45595         }
45596         Roo.log("calling toggole on editor");
45597         // tell the editor that it's been pressed..
45598         this.editor.toggleSourceEdit(sourceEditMode);
45599        
45600     },
45601      /**
45602      * Object collection of toolbar tooltips for the buttons in the editor. The key
45603      * is the command id associated with that button and the value is a valid QuickTips object.
45604      * For example:
45605 <pre><code>
45606 {
45607     bold : {
45608         title: 'Bold (Ctrl+B)',
45609         text: 'Make the selected text bold.',
45610         cls: 'x-html-editor-tip'
45611     },
45612     italic : {
45613         title: 'Italic (Ctrl+I)',
45614         text: 'Make the selected text italic.',
45615         cls: 'x-html-editor-tip'
45616     },
45617     ...
45618 </code></pre>
45619     * @type Object
45620      */
45621     buttonTips : {
45622         bold : {
45623             title: 'Bold (Ctrl+B)',
45624             text: 'Make the selected text bold.',
45625             cls: 'x-html-editor-tip'
45626         },
45627         italic : {
45628             title: 'Italic (Ctrl+I)',
45629             text: 'Make the selected text italic.',
45630             cls: 'x-html-editor-tip'
45631         },
45632         underline : {
45633             title: 'Underline (Ctrl+U)',
45634             text: 'Underline the selected text.',
45635             cls: 'x-html-editor-tip'
45636         },
45637         strikethrough : {
45638             title: 'Strikethrough',
45639             text: 'Strikethrough the selected text.',
45640             cls: 'x-html-editor-tip'
45641         },
45642         increasefontsize : {
45643             title: 'Grow Text',
45644             text: 'Increase the font size.',
45645             cls: 'x-html-editor-tip'
45646         },
45647         decreasefontsize : {
45648             title: 'Shrink Text',
45649             text: 'Decrease the font size.',
45650             cls: 'x-html-editor-tip'
45651         },
45652         backcolor : {
45653             title: 'Text Highlight Color',
45654             text: 'Change the background color of the selected text.',
45655             cls: 'x-html-editor-tip'
45656         },
45657         forecolor : {
45658             title: 'Font Color',
45659             text: 'Change the color of the selected text.',
45660             cls: 'x-html-editor-tip'
45661         },
45662         justifyleft : {
45663             title: 'Align Text Left',
45664             text: 'Align text to the left.',
45665             cls: 'x-html-editor-tip'
45666         },
45667         justifycenter : {
45668             title: 'Center Text',
45669             text: 'Center text in the editor.',
45670             cls: 'x-html-editor-tip'
45671         },
45672         justifyright : {
45673             title: 'Align Text Right',
45674             text: 'Align text to the right.',
45675             cls: 'x-html-editor-tip'
45676         },
45677         insertunorderedlist : {
45678             title: 'Bullet List',
45679             text: 'Start a bulleted list.',
45680             cls: 'x-html-editor-tip'
45681         },
45682         insertorderedlist : {
45683             title: 'Numbered List',
45684             text: 'Start a numbered list.',
45685             cls: 'x-html-editor-tip'
45686         },
45687         createlink : {
45688             title: 'Hyperlink',
45689             text: 'Make the selected text a hyperlink.',
45690             cls: 'x-html-editor-tip'
45691         },
45692         sourceedit : {
45693             title: 'Source Edit',
45694             text: 'Switch to source editing mode.',
45695             cls: 'x-html-editor-tip'
45696         }
45697     },
45698     // private
45699     onDestroy : function(){
45700         if(this.rendered){
45701             
45702             this.tb.items.each(function(item){
45703                 if(item.menu){
45704                     item.menu.removeAll();
45705                     if(item.menu.el){
45706                         item.menu.el.destroy();
45707                     }
45708                 }
45709                 item.destroy();
45710             });
45711              
45712         }
45713     },
45714     onFirstFocus: function() {
45715         this.tb.items.each(function(item){
45716            item.enable();
45717         });
45718     }
45719 });
45720
45721
45722
45723
45724 // <script type="text/javascript">
45725 /*
45726  * Based on
45727  * Ext JS Library 1.1.1
45728  * Copyright(c) 2006-2007, Ext JS, LLC.
45729  *  
45730  
45731  */
45732
45733  
45734 /**
45735  * @class Roo.form.HtmlEditor.ToolbarContext
45736  * Context Toolbar
45737  * 
45738  * Usage:
45739  *
45740  new Roo.form.HtmlEditor({
45741     ....
45742     toolbars : [
45743         { xtype: 'ToolbarStandard', styles : {} }
45744         { xtype: 'ToolbarContext', disable : {} }
45745     ]
45746 })
45747
45748      
45749  * 
45750  * @config : {Object} disable List of elements to disable.. (not done yet.)
45751  * @config : {Object} styles  Map of styles available.
45752  * 
45753  */
45754
45755 Roo.form.HtmlEditor.ToolbarContext = function(config)
45756 {
45757     
45758     Roo.apply(this, config);
45759     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45760     // dont call parent... till later.
45761     this.styles = this.styles || {};
45762 }
45763
45764  
45765
45766 Roo.form.HtmlEditor.ToolbarContext.types = {
45767     'IMG' : {
45768         width : {
45769             title: "Width",
45770             width: 40
45771         },
45772         height:  {
45773             title: "Height",
45774             width: 40
45775         },
45776         align: {
45777             title: "Align",
45778             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45779             width : 80
45780             
45781         },
45782         border: {
45783             title: "Border",
45784             width: 40
45785         },
45786         alt: {
45787             title: "Alt",
45788             width: 120
45789         },
45790         src : {
45791             title: "Src",
45792             width: 220
45793         }
45794         
45795     },
45796     'A' : {
45797         name : {
45798             title: "Name",
45799             width: 50
45800         },
45801         target:  {
45802             title: "Target",
45803             width: 120
45804         },
45805         href:  {
45806             title: "Href",
45807             width: 220
45808         } // border?
45809         
45810     },
45811     'TABLE' : {
45812         rows : {
45813             title: "Rows",
45814             width: 20
45815         },
45816         cols : {
45817             title: "Cols",
45818             width: 20
45819         },
45820         width : {
45821             title: "Width",
45822             width: 40
45823         },
45824         height : {
45825             title: "Height",
45826             width: 40
45827         },
45828         border : {
45829             title: "Border",
45830             width: 20
45831         }
45832     },
45833     'TD' : {
45834         width : {
45835             title: "Width",
45836             width: 40
45837         },
45838         height : {
45839             title: "Height",
45840             width: 40
45841         },   
45842         align: {
45843             title: "Align",
45844             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45845             width: 80
45846         },
45847         valign: {
45848             title: "Valign",
45849             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45850             width: 80
45851         },
45852         colspan: {
45853             title: "Colspan",
45854             width: 20
45855             
45856         },
45857          'font-family'  : {
45858             title : "Font",
45859             style : 'fontFamily',
45860             displayField: 'display',
45861             optname : 'font-family',
45862             width: 140
45863         }
45864     },
45865     'INPUT' : {
45866         name : {
45867             title: "name",
45868             width: 120
45869         },
45870         value : {
45871             title: "Value",
45872             width: 120
45873         },
45874         width : {
45875             title: "Width",
45876             width: 40
45877         }
45878     },
45879     'LABEL' : {
45880         'for' : {
45881             title: "For",
45882             width: 120
45883         }
45884     },
45885     'TEXTAREA' : {
45886           name : {
45887             title: "name",
45888             width: 120
45889         },
45890         rows : {
45891             title: "Rows",
45892             width: 20
45893         },
45894         cols : {
45895             title: "Cols",
45896             width: 20
45897         }
45898     },
45899     'SELECT' : {
45900         name : {
45901             title: "name",
45902             width: 120
45903         },
45904         selectoptions : {
45905             title: "Options",
45906             width: 200
45907         }
45908     },
45909     
45910     // should we really allow this??
45911     // should this just be 
45912     'BODY' : {
45913         title : {
45914             title: "Title",
45915             width: 200,
45916             disabled : true
45917         }
45918     },
45919     'SPAN' : {
45920         'font-family'  : {
45921             title : "Font",
45922             style : 'fontFamily',
45923             displayField: 'display',
45924             optname : 'font-family',
45925             width: 140
45926         }
45927     },
45928     'DIV' : {
45929         'font-family'  : {
45930             title : "Font",
45931             style : 'fontFamily',
45932             displayField: 'display',
45933             optname : 'font-family',
45934             width: 140
45935         }
45936     },
45937      'P' : {
45938         'font-family'  : {
45939             title : "Font",
45940             style : 'fontFamily',
45941             displayField: 'display',
45942             optname : 'font-family',
45943             width: 140
45944         }
45945     },
45946     
45947     '*' : {
45948         // empty..
45949     }
45950
45951 };
45952
45953 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45954 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45955
45956 Roo.form.HtmlEditor.ToolbarContext.options = {
45957         'font-family'  : [ 
45958                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45959                 [ 'Courier New', 'Courier New'],
45960                 [ 'Tahoma', 'Tahoma'],
45961                 [ 'Times New Roman,serif', 'Times'],
45962                 [ 'Verdana','Verdana' ]
45963         ]
45964 };
45965
45966 // fixme - these need to be configurable..
45967  
45968
45969 //Roo.form.HtmlEditor.ToolbarContext.types
45970
45971
45972 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45973     
45974     tb: false,
45975     
45976     rendered: false,
45977     
45978     editor : false,
45979     editorcore : false,
45980     /**
45981      * @cfg {Object} disable  List of toolbar elements to disable
45982          
45983      */
45984     disable : false,
45985     /**
45986      * @cfg {Object} styles List of styles 
45987      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45988      *
45989      * These must be defined in the page, so they get rendered correctly..
45990      * .headline { }
45991      * TD.underline { }
45992      * 
45993      */
45994     styles : false,
45995     
45996     options: false,
45997     
45998     toolbars : false,
45999     
46000     init : function(editor)
46001     {
46002         this.editor = editor;
46003         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46004         var editorcore = this.editorcore;
46005         
46006         var fid = editorcore.frameId;
46007         var etb = this;
46008         function btn(id, toggle, handler){
46009             var xid = fid + '-'+ id ;
46010             return {
46011                 id : xid,
46012                 cmd : id,
46013                 cls : 'x-btn-icon x-edit-'+id,
46014                 enableToggle:toggle !== false,
46015                 scope: editorcore, // was editor...
46016                 handler:handler||editorcore.relayBtnCmd,
46017                 clickEvent:'mousedown',
46018                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46019                 tabIndex:-1
46020             };
46021         }
46022         // create a new element.
46023         var wdiv = editor.wrap.createChild({
46024                 tag: 'div'
46025             }, editor.wrap.dom.firstChild.nextSibling, true);
46026         
46027         // can we do this more than once??
46028         
46029          // stop form submits
46030       
46031  
46032         // disable everything...
46033         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46034         this.toolbars = {};
46035            
46036         for (var i in  ty) {
46037           
46038             this.toolbars[i] = this.buildToolbar(ty[i],i);
46039         }
46040         this.tb = this.toolbars.BODY;
46041         this.tb.el.show();
46042         this.buildFooter();
46043         this.footer.show();
46044         editor.on('hide', function( ) { this.footer.hide() }, this);
46045         editor.on('show', function( ) { this.footer.show() }, this);
46046         
46047          
46048         this.rendered = true;
46049         
46050         // the all the btns;
46051         editor.on('editorevent', this.updateToolbar, this);
46052         // other toolbars need to implement this..
46053         //editor.on('editmodechange', this.updateToolbar, this);
46054     },
46055     
46056     
46057     
46058     /**
46059      * Protected method that will not generally be called directly. It triggers
46060      * a toolbar update by reading the markup state of the current selection in the editor.
46061      *
46062      * Note you can force an update by calling on('editorevent', scope, false)
46063      */
46064     updateToolbar: function(editor,ev,sel){
46065
46066         //Roo.log(ev);
46067         // capture mouse up - this is handy for selecting images..
46068         // perhaps should go somewhere else...
46069         if(!this.editorcore.activated){
46070              this.editor.onFirstFocus();
46071             return;
46072         }
46073         
46074         
46075         
46076         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46077         // selectNode - might want to handle IE?
46078         if (ev &&
46079             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46080             ev.target && ev.target.tagName == 'IMG') {
46081             // they have click on an image...
46082             // let's see if we can change the selection...
46083             sel = ev.target;
46084          
46085               var nodeRange = sel.ownerDocument.createRange();
46086             try {
46087                 nodeRange.selectNode(sel);
46088             } catch (e) {
46089                 nodeRange.selectNodeContents(sel);
46090             }
46091             //nodeRange.collapse(true);
46092             var s = this.editorcore.win.getSelection();
46093             s.removeAllRanges();
46094             s.addRange(nodeRange);
46095         }  
46096         
46097       
46098         var updateFooter = sel ? false : true;
46099         
46100         
46101         var ans = this.editorcore.getAllAncestors();
46102         
46103         // pick
46104         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46105         
46106         if (!sel) { 
46107             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46108             sel = sel ? sel : this.editorcore.doc.body;
46109             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46110             
46111         }
46112         // pick a menu that exists..
46113         var tn = sel.tagName.toUpperCase();
46114         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46115         
46116         tn = sel.tagName.toUpperCase();
46117         
46118         var lastSel = this.tb.selectedNode;
46119         
46120         this.tb.selectedNode = sel;
46121         
46122         // if current menu does not match..
46123         
46124         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46125                 
46126             this.tb.el.hide();
46127             ///console.log("show: " + tn);
46128             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46129             this.tb.el.show();
46130             // update name
46131             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46132             
46133             
46134             // update attributes
46135             if (this.tb.fields) {
46136                 this.tb.fields.each(function(e) {
46137                     if (e.stylename) {
46138                         e.setValue(sel.style[e.stylename]);
46139                         return;
46140                     } 
46141                    e.setValue(sel.getAttribute(e.attrname));
46142                 });
46143             }
46144             
46145             var hasStyles = false;
46146             for(var i in this.styles) {
46147                 hasStyles = true;
46148                 break;
46149             }
46150             
46151             // update styles
46152             if (hasStyles) { 
46153                 var st = this.tb.fields.item(0);
46154                 
46155                 st.store.removeAll();
46156                
46157                 
46158                 var cn = sel.className.split(/\s+/);
46159                 
46160                 var avs = [];
46161                 if (this.styles['*']) {
46162                     
46163                     Roo.each(this.styles['*'], function(v) {
46164                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46165                     });
46166                 }
46167                 if (this.styles[tn]) { 
46168                     Roo.each(this.styles[tn], function(v) {
46169                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46170                     });
46171                 }
46172                 
46173                 st.store.loadData(avs);
46174                 st.collapse();
46175                 st.setValue(cn);
46176             }
46177             // flag our selected Node.
46178             this.tb.selectedNode = sel;
46179            
46180            
46181             Roo.menu.MenuMgr.hideAll();
46182
46183         }
46184         
46185         if (!updateFooter) {
46186             //this.footDisp.dom.innerHTML = ''; 
46187             return;
46188         }
46189         // update the footer
46190         //
46191         var html = '';
46192         
46193         this.footerEls = ans.reverse();
46194         Roo.each(this.footerEls, function(a,i) {
46195             if (!a) { return; }
46196             html += html.length ? ' &gt; '  :  '';
46197             
46198             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46199             
46200         });
46201        
46202         // 
46203         var sz = this.footDisp.up('td').getSize();
46204         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46205         this.footDisp.dom.style.marginLeft = '5px';
46206         
46207         this.footDisp.dom.style.overflow = 'hidden';
46208         
46209         this.footDisp.dom.innerHTML = html;
46210             
46211         //this.editorsyncValue();
46212     },
46213      
46214     
46215    
46216        
46217     // private
46218     onDestroy : function(){
46219         if(this.rendered){
46220             
46221             this.tb.items.each(function(item){
46222                 if(item.menu){
46223                     item.menu.removeAll();
46224                     if(item.menu.el){
46225                         item.menu.el.destroy();
46226                     }
46227                 }
46228                 item.destroy();
46229             });
46230              
46231         }
46232     },
46233     onFirstFocus: function() {
46234         // need to do this for all the toolbars..
46235         this.tb.items.each(function(item){
46236            item.enable();
46237         });
46238     },
46239     buildToolbar: function(tlist, nm)
46240     {
46241         var editor = this.editor;
46242         var editorcore = this.editorcore;
46243          // create a new element.
46244         var wdiv = editor.wrap.createChild({
46245                 tag: 'div'
46246             }, editor.wrap.dom.firstChild.nextSibling, true);
46247         
46248        
46249         var tb = new Roo.Toolbar(wdiv);
46250         // add the name..
46251         
46252         tb.add(nm+ ":&nbsp;");
46253         
46254         var styles = [];
46255         for(var i in this.styles) {
46256             styles.push(i);
46257         }
46258         
46259         // styles...
46260         if (styles && styles.length) {
46261             
46262             // this needs a multi-select checkbox...
46263             tb.addField( new Roo.form.ComboBox({
46264                 store: new Roo.data.SimpleStore({
46265                     id : 'val',
46266                     fields: ['val', 'selected'],
46267                     data : [] 
46268                 }),
46269                 name : '-roo-edit-className',
46270                 attrname : 'className',
46271                 displayField: 'val',
46272                 typeAhead: false,
46273                 mode: 'local',
46274                 editable : false,
46275                 triggerAction: 'all',
46276                 emptyText:'Select Style',
46277                 selectOnFocus:true,
46278                 width: 130,
46279                 listeners : {
46280                     'select': function(c, r, i) {
46281                         // initial support only for on class per el..
46282                         tb.selectedNode.className =  r ? r.get('val') : '';
46283                         editorcore.syncValue();
46284                     }
46285                 }
46286     
46287             }));
46288         }
46289         
46290         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46291         var tbops = tbc.options;
46292         
46293         for (var i in tlist) {
46294             
46295             var item = tlist[i];
46296             tb.add(item.title + ":&nbsp;");
46297             
46298             
46299             //optname == used so you can configure the options available..
46300             var opts = item.opts ? item.opts : false;
46301             if (item.optname) {
46302                 opts = tbops[item.optname];
46303            
46304             }
46305             
46306             if (opts) {
46307                 // opts == pulldown..
46308                 tb.addField( new Roo.form.ComboBox({
46309                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46310                         id : 'val',
46311                         fields: ['val', 'display'],
46312                         data : opts  
46313                     }),
46314                     name : '-roo-edit-' + i,
46315                     attrname : i,
46316                     stylename : item.style ? item.style : false,
46317                     displayField: item.displayField ? item.displayField : 'val',
46318                     valueField :  'val',
46319                     typeAhead: false,
46320                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46321                     editable : false,
46322                     triggerAction: 'all',
46323                     emptyText:'Select',
46324                     selectOnFocus:true,
46325                     width: item.width ? item.width  : 130,
46326                     listeners : {
46327                         'select': function(c, r, i) {
46328                             if (c.stylename) {
46329                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46330                                 return;
46331                             }
46332                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46333                         }
46334                     }
46335
46336                 }));
46337                 continue;
46338                     
46339                  
46340                 
46341                 tb.addField( new Roo.form.TextField({
46342                     name: i,
46343                     width: 100,
46344                     //allowBlank:false,
46345                     value: ''
46346                 }));
46347                 continue;
46348             }
46349             tb.addField( new Roo.form.TextField({
46350                 name: '-roo-edit-' + i,
46351                 attrname : i,
46352                 
46353                 width: item.width,
46354                 //allowBlank:true,
46355                 value: '',
46356                 listeners: {
46357                     'change' : function(f, nv, ov) {
46358                         tb.selectedNode.setAttribute(f.attrname, nv);
46359                         editorcore.syncValue();
46360                     }
46361                 }
46362             }));
46363              
46364         }
46365         
46366         var _this = this;
46367         
46368         if(nm == 'BODY'){
46369             tb.addSeparator();
46370         
46371             tb.addButton( {
46372                 text: 'Stylesheets',
46373
46374                 listeners : {
46375                     click : function ()
46376                     {
46377                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46378                     }
46379                 }
46380             });
46381         }
46382         
46383         tb.addFill();
46384         tb.addButton( {
46385             text: 'Remove Tag',
46386     
46387             listeners : {
46388                 click : function ()
46389                 {
46390                     // remove
46391                     // undo does not work.
46392                      
46393                     var sn = tb.selectedNode;
46394                     
46395                     var pn = sn.parentNode;
46396                     
46397                     var stn =  sn.childNodes[0];
46398                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46399                     while (sn.childNodes.length) {
46400                         var node = sn.childNodes[0];
46401                         sn.removeChild(node);
46402                         //Roo.log(node);
46403                         pn.insertBefore(node, sn);
46404                         
46405                     }
46406                     pn.removeChild(sn);
46407                     var range = editorcore.createRange();
46408         
46409                     range.setStart(stn,0);
46410                     range.setEnd(en,0); //????
46411                     //range.selectNode(sel);
46412                     
46413                     
46414                     var selection = editorcore.getSelection();
46415                     selection.removeAllRanges();
46416                     selection.addRange(range);
46417                     
46418                     
46419                     
46420                     //_this.updateToolbar(null, null, pn);
46421                     _this.updateToolbar(null, null, null);
46422                     _this.footDisp.dom.innerHTML = ''; 
46423                 }
46424             }
46425             
46426                     
46427                 
46428             
46429         });
46430         
46431         
46432         tb.el.on('click', function(e){
46433             e.preventDefault(); // what does this do?
46434         });
46435         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46436         tb.el.hide();
46437         tb.name = nm;
46438         // dont need to disable them... as they will get hidden
46439         return tb;
46440          
46441         
46442     },
46443     buildFooter : function()
46444     {
46445         
46446         var fel = this.editor.wrap.createChild();
46447         this.footer = new Roo.Toolbar(fel);
46448         // toolbar has scrolly on left / right?
46449         var footDisp= new Roo.Toolbar.Fill();
46450         var _t = this;
46451         this.footer.add(
46452             {
46453                 text : '&lt;',
46454                 xtype: 'Button',
46455                 handler : function() {
46456                     _t.footDisp.scrollTo('left',0,true)
46457                 }
46458             }
46459         );
46460         this.footer.add( footDisp );
46461         this.footer.add( 
46462             {
46463                 text : '&gt;',
46464                 xtype: 'Button',
46465                 handler : function() {
46466                     // no animation..
46467                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46468                 }
46469             }
46470         );
46471         var fel = Roo.get(footDisp.el);
46472         fel.addClass('x-editor-context');
46473         this.footDispWrap = fel; 
46474         this.footDispWrap.overflow  = 'hidden';
46475         
46476         this.footDisp = fel.createChild();
46477         this.footDispWrap.on('click', this.onContextClick, this)
46478         
46479         
46480     },
46481     onContextClick : function (ev,dom)
46482     {
46483         ev.preventDefault();
46484         var  cn = dom.className;
46485         //Roo.log(cn);
46486         if (!cn.match(/x-ed-loc-/)) {
46487             return;
46488         }
46489         var n = cn.split('-').pop();
46490         var ans = this.footerEls;
46491         var sel = ans[n];
46492         
46493          // pick
46494         var range = this.editorcore.createRange();
46495         
46496         range.selectNodeContents(sel);
46497         //range.selectNode(sel);
46498         
46499         
46500         var selection = this.editorcore.getSelection();
46501         selection.removeAllRanges();
46502         selection.addRange(range);
46503         
46504         
46505         
46506         this.updateToolbar(null, null, sel);
46507         
46508         
46509     }
46510     
46511     
46512     
46513     
46514     
46515 });
46516
46517
46518
46519
46520
46521 /*
46522  * Based on:
46523  * Ext JS Library 1.1.1
46524  * Copyright(c) 2006-2007, Ext JS, LLC.
46525  *
46526  * Originally Released Under LGPL - original licence link has changed is not relivant.
46527  *
46528  * Fork - LGPL
46529  * <script type="text/javascript">
46530  */
46531  
46532 /**
46533  * @class Roo.form.BasicForm
46534  * @extends Roo.util.Observable
46535  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46536  * @constructor
46537  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46538  * @param {Object} config Configuration options
46539  */
46540 Roo.form.BasicForm = function(el, config){
46541     this.allItems = [];
46542     this.childForms = [];
46543     Roo.apply(this, config);
46544     /*
46545      * The Roo.form.Field items in this form.
46546      * @type MixedCollection
46547      */
46548      
46549      
46550     this.items = new Roo.util.MixedCollection(false, function(o){
46551         return o.id || (o.id = Roo.id());
46552     });
46553     this.addEvents({
46554         /**
46555          * @event beforeaction
46556          * Fires before any action is performed. Return false to cancel the action.
46557          * @param {Form} this
46558          * @param {Action} action The action to be performed
46559          */
46560         beforeaction: true,
46561         /**
46562          * @event actionfailed
46563          * Fires when an action fails.
46564          * @param {Form} this
46565          * @param {Action} action The action that failed
46566          */
46567         actionfailed : true,
46568         /**
46569          * @event actioncomplete
46570          * Fires when an action is completed.
46571          * @param {Form} this
46572          * @param {Action} action The action that completed
46573          */
46574         actioncomplete : true
46575     });
46576     if(el){
46577         this.initEl(el);
46578     }
46579     Roo.form.BasicForm.superclass.constructor.call(this);
46580 };
46581
46582 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46583     /**
46584      * @cfg {String} method
46585      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46586      */
46587     /**
46588      * @cfg {DataReader} reader
46589      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46590      * This is optional as there is built-in support for processing JSON.
46591      */
46592     /**
46593      * @cfg {DataReader} errorReader
46594      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46595      * This is completely optional as there is built-in support for processing JSON.
46596      */
46597     /**
46598      * @cfg {String} url
46599      * The URL to use for form actions if one isn't supplied in the action options.
46600      */
46601     /**
46602      * @cfg {Boolean} fileUpload
46603      * Set to true if this form is a file upload.
46604      */
46605      
46606     /**
46607      * @cfg {Object} baseParams
46608      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46609      */
46610      /**
46611      
46612     /**
46613      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46614      */
46615     timeout: 30,
46616
46617     // private
46618     activeAction : null,
46619
46620     /**
46621      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46622      * or setValues() data instead of when the form was first created.
46623      */
46624     trackResetOnLoad : false,
46625     
46626     
46627     /**
46628      * childForms - used for multi-tab forms
46629      * @type {Array}
46630      */
46631     childForms : false,
46632     
46633     /**
46634      * allItems - full list of fields.
46635      * @type {Array}
46636      */
46637     allItems : false,
46638     
46639     /**
46640      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46641      * element by passing it or its id or mask the form itself by passing in true.
46642      * @type Mixed
46643      */
46644     waitMsgTarget : false,
46645
46646     // private
46647     initEl : function(el){
46648         this.el = Roo.get(el);
46649         this.id = this.el.id || Roo.id();
46650         this.el.on('submit', this.onSubmit, this);
46651         this.el.addClass('x-form');
46652     },
46653
46654     // private
46655     onSubmit : function(e){
46656         e.stopEvent();
46657     },
46658
46659     /**
46660      * Returns true if client-side validation on the form is successful.
46661      * @return Boolean
46662      */
46663     isValid : function(){
46664         var valid = true;
46665         this.items.each(function(f){
46666            if(!f.validate()){
46667                valid = false;
46668            }
46669         });
46670         return valid;
46671     },
46672
46673     /**
46674      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46675      * @return Boolean
46676      */
46677     isDirty : function(){
46678         var dirty = false;
46679         this.items.each(function(f){
46680            if(f.isDirty()){
46681                dirty = true;
46682                return false;
46683            }
46684         });
46685         return dirty;
46686     },
46687     
46688     /**
46689      * Returns true if any fields in this form have changed since their original load. (New version)
46690      * @return Boolean
46691      */
46692     
46693     hasChanged : function()
46694     {
46695         var dirty = false;
46696         this.items.each(function(f){
46697            if(f.hasChanged()){
46698                dirty = true;
46699                return false;
46700            }
46701         });
46702         return dirty;
46703         
46704     },
46705     /**
46706      * Resets all hasChanged to 'false' -
46707      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46708      * So hasChanged storage is only to be used for this purpose
46709      * @return Boolean
46710      */
46711     resetHasChanged : function()
46712     {
46713         this.items.each(function(f){
46714            f.resetHasChanged();
46715         });
46716         
46717     },
46718     
46719     
46720     /**
46721      * Performs a predefined action (submit or load) or custom actions you define on this form.
46722      * @param {String} actionName The name of the action type
46723      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46724      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46725      * accept other config options):
46726      * <pre>
46727 Property          Type             Description
46728 ----------------  ---------------  ----------------------------------------------------------------------------------
46729 url               String           The url for the action (defaults to the form's url)
46730 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46731 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46732 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46733                                    validate the form on the client (defaults to false)
46734      * </pre>
46735      * @return {BasicForm} this
46736      */
46737     doAction : function(action, options){
46738         if(typeof action == 'string'){
46739             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46740         }
46741         if(this.fireEvent('beforeaction', this, action) !== false){
46742             this.beforeAction(action);
46743             action.run.defer(100, action);
46744         }
46745         return this;
46746     },
46747
46748     /**
46749      * Shortcut to do a submit action.
46750      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46751      * @return {BasicForm} this
46752      */
46753     submit : function(options){
46754         this.doAction('submit', options);
46755         return this;
46756     },
46757
46758     /**
46759      * Shortcut to do a load action.
46760      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46761      * @return {BasicForm} this
46762      */
46763     load : function(options){
46764         this.doAction('load', options);
46765         return this;
46766     },
46767
46768     /**
46769      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46770      * @param {Record} record The record to edit
46771      * @return {BasicForm} this
46772      */
46773     updateRecord : function(record){
46774         record.beginEdit();
46775         var fs = record.fields;
46776         fs.each(function(f){
46777             var field = this.findField(f.name);
46778             if(field){
46779                 record.set(f.name, field.getValue());
46780             }
46781         }, this);
46782         record.endEdit();
46783         return this;
46784     },
46785
46786     /**
46787      * Loads an Roo.data.Record into this form.
46788      * @param {Record} record The record to load
46789      * @return {BasicForm} this
46790      */
46791     loadRecord : function(record){
46792         this.setValues(record.data);
46793         return this;
46794     },
46795
46796     // private
46797     beforeAction : function(action){
46798         var o = action.options;
46799         
46800        
46801         if(this.waitMsgTarget === true){
46802             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46803         }else if(this.waitMsgTarget){
46804             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46805             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46806         }else {
46807             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46808         }
46809          
46810     },
46811
46812     // private
46813     afterAction : function(action, success){
46814         this.activeAction = null;
46815         var o = action.options;
46816         
46817         if(this.waitMsgTarget === true){
46818             this.el.unmask();
46819         }else if(this.waitMsgTarget){
46820             this.waitMsgTarget.unmask();
46821         }else{
46822             Roo.MessageBox.updateProgress(1);
46823             Roo.MessageBox.hide();
46824         }
46825          
46826         if(success){
46827             if(o.reset){
46828                 this.reset();
46829             }
46830             Roo.callback(o.success, o.scope, [this, action]);
46831             this.fireEvent('actioncomplete', this, action);
46832             
46833         }else{
46834             
46835             // failure condition..
46836             // we have a scenario where updates need confirming.
46837             // eg. if a locking scenario exists..
46838             // we look for { errors : { needs_confirm : true }} in the response.
46839             if (
46840                 (typeof(action.result) != 'undefined')  &&
46841                 (typeof(action.result.errors) != 'undefined')  &&
46842                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46843            ){
46844                 var _t = this;
46845                 Roo.MessageBox.confirm(
46846                     "Change requires confirmation",
46847                     action.result.errorMsg,
46848                     function(r) {
46849                         if (r != 'yes') {
46850                             return;
46851                         }
46852                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46853                     }
46854                     
46855                 );
46856                 
46857                 
46858                 
46859                 return;
46860             }
46861             
46862             Roo.callback(o.failure, o.scope, [this, action]);
46863             // show an error message if no failed handler is set..
46864             if (!this.hasListener('actionfailed')) {
46865                 Roo.MessageBox.alert("Error",
46866                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46867                         action.result.errorMsg :
46868                         "Saving Failed, please check your entries or try again"
46869                 );
46870             }
46871             
46872             this.fireEvent('actionfailed', this, action);
46873         }
46874         
46875     },
46876
46877     /**
46878      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46879      * @param {String} id The value to search for
46880      * @return Field
46881      */
46882     findField : function(id){
46883         var field = this.items.get(id);
46884         if(!field){
46885             this.items.each(function(f){
46886                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46887                     field = f;
46888                     return false;
46889                 }
46890             });
46891         }
46892         return field || null;
46893     },
46894
46895     /**
46896      * Add a secondary form to this one, 
46897      * Used to provide tabbed forms. One form is primary, with hidden values 
46898      * which mirror the elements from the other forms.
46899      * 
46900      * @param {Roo.form.Form} form to add.
46901      * 
46902      */
46903     addForm : function(form)
46904     {
46905        
46906         if (this.childForms.indexOf(form) > -1) {
46907             // already added..
46908             return;
46909         }
46910         this.childForms.push(form);
46911         var n = '';
46912         Roo.each(form.allItems, function (fe) {
46913             
46914             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46915             if (this.findField(n)) { // already added..
46916                 return;
46917             }
46918             var add = new Roo.form.Hidden({
46919                 name : n
46920             });
46921             add.render(this.el);
46922             
46923             this.add( add );
46924         }, this);
46925         
46926     },
46927     /**
46928      * Mark fields in this form invalid in bulk.
46929      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46930      * @return {BasicForm} this
46931      */
46932     markInvalid : function(errors){
46933         if(errors instanceof Array){
46934             for(var i = 0, len = errors.length; i < len; i++){
46935                 var fieldError = errors[i];
46936                 var f = this.findField(fieldError.id);
46937                 if(f){
46938                     f.markInvalid(fieldError.msg);
46939                 }
46940             }
46941         }else{
46942             var field, id;
46943             for(id in errors){
46944                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46945                     field.markInvalid(errors[id]);
46946                 }
46947             }
46948         }
46949         Roo.each(this.childForms || [], function (f) {
46950             f.markInvalid(errors);
46951         });
46952         
46953         return this;
46954     },
46955
46956     /**
46957      * Set values for fields in this form in bulk.
46958      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46959      * @return {BasicForm} this
46960      */
46961     setValues : function(values){
46962         if(values instanceof Array){ // array of objects
46963             for(var i = 0, len = values.length; i < len; i++){
46964                 var v = values[i];
46965                 var f = this.findField(v.id);
46966                 if(f){
46967                     f.setValue(v.value);
46968                     if(this.trackResetOnLoad){
46969                         f.originalValue = f.getValue();
46970                     }
46971                 }
46972             }
46973         }else{ // object hash
46974             var field, id;
46975             for(id in values){
46976                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46977                     
46978                     if (field.setFromData && 
46979                         field.valueField && 
46980                         field.displayField &&
46981                         // combos' with local stores can 
46982                         // be queried via setValue()
46983                         // to set their value..
46984                         (field.store && !field.store.isLocal)
46985                         ) {
46986                         // it's a combo
46987                         var sd = { };
46988                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46989                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46990                         field.setFromData(sd);
46991                         
46992                     } else {
46993                         field.setValue(values[id]);
46994                     }
46995                     
46996                     
46997                     if(this.trackResetOnLoad){
46998                         field.originalValue = field.getValue();
46999                     }
47000                 }
47001             }
47002         }
47003         this.resetHasChanged();
47004         
47005         
47006         Roo.each(this.childForms || [], function (f) {
47007             f.setValues(values);
47008             f.resetHasChanged();
47009         });
47010                 
47011         return this;
47012     },
47013
47014     /**
47015      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47016      * they are returned as an array.
47017      * @param {Boolean} asString
47018      * @return {Object}
47019      */
47020     getValues : function(asString){
47021         if (this.childForms) {
47022             // copy values from the child forms
47023             Roo.each(this.childForms, function (f) {
47024                 this.setValues(f.getValues());
47025             }, this);
47026         }
47027         
47028         
47029         
47030         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47031         if(asString === true){
47032             return fs;
47033         }
47034         return Roo.urlDecode(fs);
47035     },
47036     
47037     /**
47038      * Returns the fields in this form as an object with key/value pairs. 
47039      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47040      * @return {Object}
47041      */
47042     getFieldValues : function(with_hidden)
47043     {
47044         if (this.childForms) {
47045             // copy values from the child forms
47046             // should this call getFieldValues - probably not as we do not currently copy
47047             // hidden fields when we generate..
47048             Roo.each(this.childForms, function (f) {
47049                 this.setValues(f.getValues());
47050             }, this);
47051         }
47052         
47053         var ret = {};
47054         this.items.each(function(f){
47055             if (!f.getName()) {
47056                 return;
47057             }
47058             var v = f.getValue();
47059             if (f.inputType =='radio') {
47060                 if (typeof(ret[f.getName()]) == 'undefined') {
47061                     ret[f.getName()] = ''; // empty..
47062                 }
47063                 
47064                 if (!f.el.dom.checked) {
47065                     return;
47066                     
47067                 }
47068                 v = f.el.dom.value;
47069                 
47070             }
47071             
47072             // not sure if this supported any more..
47073             if ((typeof(v) == 'object') && f.getRawValue) {
47074                 v = f.getRawValue() ; // dates..
47075             }
47076             // combo boxes where name != hiddenName...
47077             if (f.name != f.getName()) {
47078                 ret[f.name] = f.getRawValue();
47079             }
47080             ret[f.getName()] = v;
47081         });
47082         
47083         return ret;
47084     },
47085
47086     /**
47087      * Clears all invalid messages in this form.
47088      * @return {BasicForm} this
47089      */
47090     clearInvalid : function(){
47091         this.items.each(function(f){
47092            f.clearInvalid();
47093         });
47094         
47095         Roo.each(this.childForms || [], function (f) {
47096             f.clearInvalid();
47097         });
47098         
47099         
47100         return this;
47101     },
47102
47103     /**
47104      * Resets this form.
47105      * @return {BasicForm} this
47106      */
47107     reset : function(){
47108         this.items.each(function(f){
47109             f.reset();
47110         });
47111         
47112         Roo.each(this.childForms || [], function (f) {
47113             f.reset();
47114         });
47115         this.resetHasChanged();
47116         
47117         return this;
47118     },
47119
47120     /**
47121      * Add Roo.form components to this form.
47122      * @param {Field} field1
47123      * @param {Field} field2 (optional)
47124      * @param {Field} etc (optional)
47125      * @return {BasicForm} this
47126      */
47127     add : function(){
47128         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47129         return this;
47130     },
47131
47132
47133     /**
47134      * Removes a field from the items collection (does NOT remove its markup).
47135      * @param {Field} field
47136      * @return {BasicForm} this
47137      */
47138     remove : function(field){
47139         this.items.remove(field);
47140         return this;
47141     },
47142
47143     /**
47144      * Looks at the fields in this form, checks them for an id attribute,
47145      * and calls applyTo on the existing dom element with that id.
47146      * @return {BasicForm} this
47147      */
47148     render : function(){
47149         this.items.each(function(f){
47150             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47151                 f.applyTo(f.id);
47152             }
47153         });
47154         return this;
47155     },
47156
47157     /**
47158      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47159      * @param {Object} values
47160      * @return {BasicForm} this
47161      */
47162     applyToFields : function(o){
47163         this.items.each(function(f){
47164            Roo.apply(f, o);
47165         });
47166         return this;
47167     },
47168
47169     /**
47170      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47171      * @param {Object} values
47172      * @return {BasicForm} this
47173      */
47174     applyIfToFields : function(o){
47175         this.items.each(function(f){
47176            Roo.applyIf(f, o);
47177         });
47178         return this;
47179     }
47180 });
47181
47182 // back compat
47183 Roo.BasicForm = Roo.form.BasicForm;/*
47184  * Based on:
47185  * Ext JS Library 1.1.1
47186  * Copyright(c) 2006-2007, Ext JS, LLC.
47187  *
47188  * Originally Released Under LGPL - original licence link has changed is not relivant.
47189  *
47190  * Fork - LGPL
47191  * <script type="text/javascript">
47192  */
47193
47194 /**
47195  * @class Roo.form.Form
47196  * @extends Roo.form.BasicForm
47197  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47198  * @constructor
47199  * @param {Object} config Configuration options
47200  */
47201 Roo.form.Form = function(config){
47202     var xitems =  [];
47203     if (config.items) {
47204         xitems = config.items;
47205         delete config.items;
47206     }
47207    
47208     
47209     Roo.form.Form.superclass.constructor.call(this, null, config);
47210     this.url = this.url || this.action;
47211     if(!this.root){
47212         this.root = new Roo.form.Layout(Roo.applyIf({
47213             id: Roo.id()
47214         }, config));
47215     }
47216     this.active = this.root;
47217     /**
47218      * Array of all the buttons that have been added to this form via {@link addButton}
47219      * @type Array
47220      */
47221     this.buttons = [];
47222     this.allItems = [];
47223     this.addEvents({
47224         /**
47225          * @event clientvalidation
47226          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47227          * @param {Form} this
47228          * @param {Boolean} valid true if the form has passed client-side validation
47229          */
47230         clientvalidation: true,
47231         /**
47232          * @event rendered
47233          * Fires when the form is rendered
47234          * @param {Roo.form.Form} form
47235          */
47236         rendered : true
47237     });
47238     
47239     if (this.progressUrl) {
47240             // push a hidden field onto the list of fields..
47241             this.addxtype( {
47242                     xns: Roo.form, 
47243                     xtype : 'Hidden', 
47244                     name : 'UPLOAD_IDENTIFIER' 
47245             });
47246         }
47247         
47248     
47249     Roo.each(xitems, this.addxtype, this);
47250     
47251     
47252     
47253 };
47254
47255 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47256     /**
47257      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47258      */
47259     /**
47260      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47261      */
47262     /**
47263      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47264      */
47265     buttonAlign:'center',
47266
47267     /**
47268      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47269      */
47270     minButtonWidth:75,
47271
47272     /**
47273      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47274      * This property cascades to child containers if not set.
47275      */
47276     labelAlign:'left',
47277
47278     /**
47279      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47280      * fires a looping event with that state. This is required to bind buttons to the valid
47281      * state using the config value formBind:true on the button.
47282      */
47283     monitorValid : false,
47284
47285     /**
47286      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47287      */
47288     monitorPoll : 200,
47289     
47290     /**
47291      * @cfg {String} progressUrl - Url to return progress data 
47292      */
47293     
47294     progressUrl : false,
47295   
47296     /**
47297      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47298      * fields are added and the column is closed. If no fields are passed the column remains open
47299      * until end() is called.
47300      * @param {Object} config The config to pass to the column
47301      * @param {Field} field1 (optional)
47302      * @param {Field} field2 (optional)
47303      * @param {Field} etc (optional)
47304      * @return Column The column container object
47305      */
47306     column : function(c){
47307         var col = new Roo.form.Column(c);
47308         this.start(col);
47309         if(arguments.length > 1){ // duplicate code required because of Opera
47310             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47311             this.end();
47312         }
47313         return col;
47314     },
47315
47316     /**
47317      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47318      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47319      * until end() is called.
47320      * @param {Object} config The config to pass to the fieldset
47321      * @param {Field} field1 (optional)
47322      * @param {Field} field2 (optional)
47323      * @param {Field} etc (optional)
47324      * @return FieldSet The fieldset container object
47325      */
47326     fieldset : function(c){
47327         var fs = new Roo.form.FieldSet(c);
47328         this.start(fs);
47329         if(arguments.length > 1){ // duplicate code required because of Opera
47330             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47331             this.end();
47332         }
47333         return fs;
47334     },
47335
47336     /**
47337      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47338      * fields are added and the container is closed. If no fields are passed the container remains open
47339      * until end() is called.
47340      * @param {Object} config The config to pass to the Layout
47341      * @param {Field} field1 (optional)
47342      * @param {Field} field2 (optional)
47343      * @param {Field} etc (optional)
47344      * @return Layout The container object
47345      */
47346     container : function(c){
47347         var l = new Roo.form.Layout(c);
47348         this.start(l);
47349         if(arguments.length > 1){ // duplicate code required because of Opera
47350             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47351             this.end();
47352         }
47353         return l;
47354     },
47355
47356     /**
47357      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47358      * @param {Object} container A Roo.form.Layout or subclass of Layout
47359      * @return {Form} this
47360      */
47361     start : function(c){
47362         // cascade label info
47363         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47364         this.active.stack.push(c);
47365         c.ownerCt = this.active;
47366         this.active = c;
47367         return this;
47368     },
47369
47370     /**
47371      * Closes the current open container
47372      * @return {Form} this
47373      */
47374     end : function(){
47375         if(this.active == this.root){
47376             return this;
47377         }
47378         this.active = this.active.ownerCt;
47379         return this;
47380     },
47381
47382     /**
47383      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47384      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47385      * as the label of the field.
47386      * @param {Field} field1
47387      * @param {Field} field2 (optional)
47388      * @param {Field} etc. (optional)
47389      * @return {Form} this
47390      */
47391     add : function(){
47392         this.active.stack.push.apply(this.active.stack, arguments);
47393         this.allItems.push.apply(this.allItems,arguments);
47394         var r = [];
47395         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47396             if(a[i].isFormField){
47397                 r.push(a[i]);
47398             }
47399         }
47400         if(r.length > 0){
47401             Roo.form.Form.superclass.add.apply(this, r);
47402         }
47403         return this;
47404     },
47405     
47406
47407     
47408     
47409     
47410      /**
47411      * Find any element that has been added to a form, using it's ID or name
47412      * This can include framesets, columns etc. along with regular fields..
47413      * @param {String} id - id or name to find.
47414      
47415      * @return {Element} e - or false if nothing found.
47416      */
47417     findbyId : function(id)
47418     {
47419         var ret = false;
47420         if (!id) {
47421             return ret;
47422         }
47423         Roo.each(this.allItems, function(f){
47424             if (f.id == id || f.name == id ){
47425                 ret = f;
47426                 return false;
47427             }
47428         });
47429         return ret;
47430     },
47431
47432     
47433     
47434     /**
47435      * Render this form into the passed container. This should only be called once!
47436      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47437      * @return {Form} this
47438      */
47439     render : function(ct)
47440     {
47441         
47442         
47443         
47444         ct = Roo.get(ct);
47445         var o = this.autoCreate || {
47446             tag: 'form',
47447             method : this.method || 'POST',
47448             id : this.id || Roo.id()
47449         };
47450         this.initEl(ct.createChild(o));
47451
47452         this.root.render(this.el);
47453         
47454        
47455              
47456         this.items.each(function(f){
47457             f.render('x-form-el-'+f.id);
47458         });
47459
47460         if(this.buttons.length > 0){
47461             // tables are required to maintain order and for correct IE layout
47462             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47463                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47464                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47465             }}, null, true);
47466             var tr = tb.getElementsByTagName('tr')[0];
47467             for(var i = 0, len = this.buttons.length; i < len; i++) {
47468                 var b = this.buttons[i];
47469                 var td = document.createElement('td');
47470                 td.className = 'x-form-btn-td';
47471                 b.render(tr.appendChild(td));
47472             }
47473         }
47474         if(this.monitorValid){ // initialize after render
47475             this.startMonitoring();
47476         }
47477         this.fireEvent('rendered', this);
47478         return this;
47479     },
47480
47481     /**
47482      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47483      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47484      * object or a valid Roo.DomHelper element config
47485      * @param {Function} handler The function called when the button is clicked
47486      * @param {Object} scope (optional) The scope of the handler function
47487      * @return {Roo.Button}
47488      */
47489     addButton : function(config, handler, scope){
47490         var bc = {
47491             handler: handler,
47492             scope: scope,
47493             minWidth: this.minButtonWidth,
47494             hideParent:true
47495         };
47496         if(typeof config == "string"){
47497             bc.text = config;
47498         }else{
47499             Roo.apply(bc, config);
47500         }
47501         var btn = new Roo.Button(null, bc);
47502         this.buttons.push(btn);
47503         return btn;
47504     },
47505
47506      /**
47507      * Adds a series of form elements (using the xtype property as the factory method.
47508      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47509      * @param {Object} config 
47510      */
47511     
47512     addxtype : function()
47513     {
47514         var ar = Array.prototype.slice.call(arguments, 0);
47515         var ret = false;
47516         for(var i = 0; i < ar.length; i++) {
47517             if (!ar[i]) {
47518                 continue; // skip -- if this happends something invalid got sent, we 
47519                 // should ignore it, as basically that interface element will not show up
47520                 // and that should be pretty obvious!!
47521             }
47522             
47523             if (Roo.form[ar[i].xtype]) {
47524                 ar[i].form = this;
47525                 var fe = Roo.factory(ar[i], Roo.form);
47526                 if (!ret) {
47527                     ret = fe;
47528                 }
47529                 fe.form = this;
47530                 if (fe.store) {
47531                     fe.store.form = this;
47532                 }
47533                 if (fe.isLayout) {  
47534                          
47535                     this.start(fe);
47536                     this.allItems.push(fe);
47537                     if (fe.items && fe.addxtype) {
47538                         fe.addxtype.apply(fe, fe.items);
47539                         delete fe.items;
47540                     }
47541                      this.end();
47542                     continue;
47543                 }
47544                 
47545                 
47546                  
47547                 this.add(fe);
47548               //  console.log('adding ' + ar[i].xtype);
47549             }
47550             if (ar[i].xtype == 'Button') {  
47551                 //console.log('adding button');
47552                 //console.log(ar[i]);
47553                 this.addButton(ar[i]);
47554                 this.allItems.push(fe);
47555                 continue;
47556             }
47557             
47558             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47559                 alert('end is not supported on xtype any more, use items');
47560             //    this.end();
47561             //    //console.log('adding end');
47562             }
47563             
47564         }
47565         return ret;
47566     },
47567     
47568     /**
47569      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47570      * option "monitorValid"
47571      */
47572     startMonitoring : function(){
47573         if(!this.bound){
47574             this.bound = true;
47575             Roo.TaskMgr.start({
47576                 run : this.bindHandler,
47577                 interval : this.monitorPoll || 200,
47578                 scope: this
47579             });
47580         }
47581     },
47582
47583     /**
47584      * Stops monitoring of the valid state of this form
47585      */
47586     stopMonitoring : function(){
47587         this.bound = false;
47588     },
47589
47590     // private
47591     bindHandler : function(){
47592         if(!this.bound){
47593             return false; // stops binding
47594         }
47595         var valid = true;
47596         this.items.each(function(f){
47597             if(!f.isValid(true)){
47598                 valid = false;
47599                 return false;
47600             }
47601         });
47602         for(var i = 0, len = this.buttons.length; i < len; i++){
47603             var btn = this.buttons[i];
47604             if(btn.formBind === true && btn.disabled === valid){
47605                 btn.setDisabled(!valid);
47606             }
47607         }
47608         this.fireEvent('clientvalidation', this, valid);
47609     }
47610     
47611     
47612     
47613     
47614     
47615     
47616     
47617     
47618 });
47619
47620
47621 // back compat
47622 Roo.Form = Roo.form.Form;
47623 /*
47624  * Based on:
47625  * Ext JS Library 1.1.1
47626  * Copyright(c) 2006-2007, Ext JS, LLC.
47627  *
47628  * Originally Released Under LGPL - original licence link has changed is not relivant.
47629  *
47630  * Fork - LGPL
47631  * <script type="text/javascript">
47632  */
47633
47634 // as we use this in bootstrap.
47635 Roo.namespace('Roo.form');
47636  /**
47637  * @class Roo.form.Action
47638  * Internal Class used to handle form actions
47639  * @constructor
47640  * @param {Roo.form.BasicForm} el The form element or its id
47641  * @param {Object} config Configuration options
47642  */
47643
47644  
47645  
47646 // define the action interface
47647 Roo.form.Action = function(form, options){
47648     this.form = form;
47649     this.options = options || {};
47650 };
47651 /**
47652  * Client Validation Failed
47653  * @const 
47654  */
47655 Roo.form.Action.CLIENT_INVALID = 'client';
47656 /**
47657  * Server Validation Failed
47658  * @const 
47659  */
47660 Roo.form.Action.SERVER_INVALID = 'server';
47661  /**
47662  * Connect to Server Failed
47663  * @const 
47664  */
47665 Roo.form.Action.CONNECT_FAILURE = 'connect';
47666 /**
47667  * Reading Data from Server Failed
47668  * @const 
47669  */
47670 Roo.form.Action.LOAD_FAILURE = 'load';
47671
47672 Roo.form.Action.prototype = {
47673     type : 'default',
47674     failureType : undefined,
47675     response : undefined,
47676     result : undefined,
47677
47678     // interface method
47679     run : function(options){
47680
47681     },
47682
47683     // interface method
47684     success : function(response){
47685
47686     },
47687
47688     // interface method
47689     handleResponse : function(response){
47690
47691     },
47692
47693     // default connection failure
47694     failure : function(response){
47695         
47696         this.response = response;
47697         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47698         this.form.afterAction(this, false);
47699     },
47700
47701     processResponse : function(response){
47702         this.response = response;
47703         if(!response.responseText){
47704             return true;
47705         }
47706         this.result = this.handleResponse(response);
47707         return this.result;
47708     },
47709
47710     // utility functions used internally
47711     getUrl : function(appendParams){
47712         var url = this.options.url || this.form.url || this.form.el.dom.action;
47713         if(appendParams){
47714             var p = this.getParams();
47715             if(p){
47716                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47717             }
47718         }
47719         return url;
47720     },
47721
47722     getMethod : function(){
47723         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47724     },
47725
47726     getParams : function(){
47727         var bp = this.form.baseParams;
47728         var p = this.options.params;
47729         if(p){
47730             if(typeof p == "object"){
47731                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47732             }else if(typeof p == 'string' && bp){
47733                 p += '&' + Roo.urlEncode(bp);
47734             }
47735         }else if(bp){
47736             p = Roo.urlEncode(bp);
47737         }
47738         return p;
47739     },
47740
47741     createCallback : function(){
47742         return {
47743             success: this.success,
47744             failure: this.failure,
47745             scope: this,
47746             timeout: (this.form.timeout*1000),
47747             upload: this.form.fileUpload ? this.success : undefined
47748         };
47749     }
47750 };
47751
47752 Roo.form.Action.Submit = function(form, options){
47753     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47754 };
47755
47756 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47757     type : 'submit',
47758
47759     haveProgress : false,
47760     uploadComplete : false,
47761     
47762     // uploadProgress indicator.
47763     uploadProgress : function()
47764     {
47765         if (!this.form.progressUrl) {
47766             return;
47767         }
47768         
47769         if (!this.haveProgress) {
47770             Roo.MessageBox.progress("Uploading", "Uploading");
47771         }
47772         if (this.uploadComplete) {
47773            Roo.MessageBox.hide();
47774            return;
47775         }
47776         
47777         this.haveProgress = true;
47778    
47779         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47780         
47781         var c = new Roo.data.Connection();
47782         c.request({
47783             url : this.form.progressUrl,
47784             params: {
47785                 id : uid
47786             },
47787             method: 'GET',
47788             success : function(req){
47789                //console.log(data);
47790                 var rdata = false;
47791                 var edata;
47792                 try  {
47793                    rdata = Roo.decode(req.responseText)
47794                 } catch (e) {
47795                     Roo.log("Invalid data from server..");
47796                     Roo.log(edata);
47797                     return;
47798                 }
47799                 if (!rdata || !rdata.success) {
47800                     Roo.log(rdata);
47801                     Roo.MessageBox.alert(Roo.encode(rdata));
47802                     return;
47803                 }
47804                 var data = rdata.data;
47805                 
47806                 if (this.uploadComplete) {
47807                    Roo.MessageBox.hide();
47808                    return;
47809                 }
47810                    
47811                 if (data){
47812                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47813                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47814                     );
47815                 }
47816                 this.uploadProgress.defer(2000,this);
47817             },
47818        
47819             failure: function(data) {
47820                 Roo.log('progress url failed ');
47821                 Roo.log(data);
47822             },
47823             scope : this
47824         });
47825            
47826     },
47827     
47828     
47829     run : function()
47830     {
47831         // run get Values on the form, so it syncs any secondary forms.
47832         this.form.getValues();
47833         
47834         var o = this.options;
47835         var method = this.getMethod();
47836         var isPost = method == 'POST';
47837         if(o.clientValidation === false || this.form.isValid()){
47838             
47839             if (this.form.progressUrl) {
47840                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47841                     (new Date() * 1) + '' + Math.random());
47842                     
47843             } 
47844             
47845             
47846             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47847                 form:this.form.el.dom,
47848                 url:this.getUrl(!isPost),
47849                 method: method,
47850                 params:isPost ? this.getParams() : null,
47851                 isUpload: this.form.fileUpload
47852             }));
47853             
47854             this.uploadProgress();
47855
47856         }else if (o.clientValidation !== false){ // client validation failed
47857             this.failureType = Roo.form.Action.CLIENT_INVALID;
47858             this.form.afterAction(this, false);
47859         }
47860     },
47861
47862     success : function(response)
47863     {
47864         this.uploadComplete= true;
47865         if (this.haveProgress) {
47866             Roo.MessageBox.hide();
47867         }
47868         
47869         
47870         var result = this.processResponse(response);
47871         if(result === true || result.success){
47872             this.form.afterAction(this, true);
47873             return;
47874         }
47875         if(result.errors){
47876             this.form.markInvalid(result.errors);
47877             this.failureType = Roo.form.Action.SERVER_INVALID;
47878         }
47879         this.form.afterAction(this, false);
47880     },
47881     failure : function(response)
47882     {
47883         this.uploadComplete= true;
47884         if (this.haveProgress) {
47885             Roo.MessageBox.hide();
47886         }
47887         
47888         this.response = response;
47889         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47890         this.form.afterAction(this, false);
47891     },
47892     
47893     handleResponse : function(response){
47894         if(this.form.errorReader){
47895             var rs = this.form.errorReader.read(response);
47896             var errors = [];
47897             if(rs.records){
47898                 for(var i = 0, len = rs.records.length; i < len; i++) {
47899                     var r = rs.records[i];
47900                     errors[i] = r.data;
47901                 }
47902             }
47903             if(errors.length < 1){
47904                 errors = null;
47905             }
47906             return {
47907                 success : rs.success,
47908                 errors : errors
47909             };
47910         }
47911         var ret = false;
47912         try {
47913             ret = Roo.decode(response.responseText);
47914         } catch (e) {
47915             ret = {
47916                 success: false,
47917                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47918                 errors : []
47919             };
47920         }
47921         return ret;
47922         
47923     }
47924 });
47925
47926
47927 Roo.form.Action.Load = function(form, options){
47928     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47929     this.reader = this.form.reader;
47930 };
47931
47932 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47933     type : 'load',
47934
47935     run : function(){
47936         
47937         Roo.Ajax.request(Roo.apply(
47938                 this.createCallback(), {
47939                     method:this.getMethod(),
47940                     url:this.getUrl(false),
47941                     params:this.getParams()
47942         }));
47943     },
47944
47945     success : function(response){
47946         
47947         var result = this.processResponse(response);
47948         if(result === true || !result.success || !result.data){
47949             this.failureType = Roo.form.Action.LOAD_FAILURE;
47950             this.form.afterAction(this, false);
47951             return;
47952         }
47953         this.form.clearInvalid();
47954         this.form.setValues(result.data);
47955         this.form.afterAction(this, true);
47956     },
47957
47958     handleResponse : function(response){
47959         if(this.form.reader){
47960             var rs = this.form.reader.read(response);
47961             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47962             return {
47963                 success : rs.success,
47964                 data : data
47965             };
47966         }
47967         return Roo.decode(response.responseText);
47968     }
47969 });
47970
47971 Roo.form.Action.ACTION_TYPES = {
47972     'load' : Roo.form.Action.Load,
47973     'submit' : Roo.form.Action.Submit
47974 };/*
47975  * Based on:
47976  * Ext JS Library 1.1.1
47977  * Copyright(c) 2006-2007, Ext JS, LLC.
47978  *
47979  * Originally Released Under LGPL - original licence link has changed is not relivant.
47980  *
47981  * Fork - LGPL
47982  * <script type="text/javascript">
47983  */
47984  
47985 /**
47986  * @class Roo.form.Layout
47987  * @extends Roo.Component
47988  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47989  * @constructor
47990  * @param {Object} config Configuration options
47991  */
47992 Roo.form.Layout = function(config){
47993     var xitems = [];
47994     if (config.items) {
47995         xitems = config.items;
47996         delete config.items;
47997     }
47998     Roo.form.Layout.superclass.constructor.call(this, config);
47999     this.stack = [];
48000     Roo.each(xitems, this.addxtype, this);
48001      
48002 };
48003
48004 Roo.extend(Roo.form.Layout, Roo.Component, {
48005     /**
48006      * @cfg {String/Object} autoCreate
48007      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48008      */
48009     /**
48010      * @cfg {String/Object/Function} style
48011      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48012      * a function which returns such a specification.
48013      */
48014     /**
48015      * @cfg {String} labelAlign
48016      * Valid values are "left," "top" and "right" (defaults to "left")
48017      */
48018     /**
48019      * @cfg {Number} labelWidth
48020      * Fixed width in pixels of all field labels (defaults to undefined)
48021      */
48022     /**
48023      * @cfg {Boolean} clear
48024      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48025      */
48026     clear : true,
48027     /**
48028      * @cfg {String} labelSeparator
48029      * The separator to use after field labels (defaults to ':')
48030      */
48031     labelSeparator : ':',
48032     /**
48033      * @cfg {Boolean} hideLabels
48034      * True to suppress the display of field labels in this layout (defaults to false)
48035      */
48036     hideLabels : false,
48037
48038     // private
48039     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48040     
48041     isLayout : true,
48042     
48043     // private
48044     onRender : function(ct, position){
48045         if(this.el){ // from markup
48046             this.el = Roo.get(this.el);
48047         }else {  // generate
48048             var cfg = this.getAutoCreate();
48049             this.el = ct.createChild(cfg, position);
48050         }
48051         if(this.style){
48052             this.el.applyStyles(this.style);
48053         }
48054         if(this.labelAlign){
48055             this.el.addClass('x-form-label-'+this.labelAlign);
48056         }
48057         if(this.hideLabels){
48058             this.labelStyle = "display:none";
48059             this.elementStyle = "padding-left:0;";
48060         }else{
48061             if(typeof this.labelWidth == 'number'){
48062                 this.labelStyle = "width:"+this.labelWidth+"px;";
48063                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48064             }
48065             if(this.labelAlign == 'top'){
48066                 this.labelStyle = "width:auto;";
48067                 this.elementStyle = "padding-left:0;";
48068             }
48069         }
48070         var stack = this.stack;
48071         var slen = stack.length;
48072         if(slen > 0){
48073             if(!this.fieldTpl){
48074                 var t = new Roo.Template(
48075                     '<div class="x-form-item {5}">',
48076                         '<label for="{0}" style="{2}">{1}{4}</label>',
48077                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48078                         '</div>',
48079                     '</div><div class="x-form-clear-left"></div>'
48080                 );
48081                 t.disableFormats = true;
48082                 t.compile();
48083                 Roo.form.Layout.prototype.fieldTpl = t;
48084             }
48085             for(var i = 0; i < slen; i++) {
48086                 if(stack[i].isFormField){
48087                     this.renderField(stack[i]);
48088                 }else{
48089                     this.renderComponent(stack[i]);
48090                 }
48091             }
48092         }
48093         if(this.clear){
48094             this.el.createChild({cls:'x-form-clear'});
48095         }
48096     },
48097
48098     // private
48099     renderField : function(f){
48100         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48101                f.id, //0
48102                f.fieldLabel, //1
48103                f.labelStyle||this.labelStyle||'', //2
48104                this.elementStyle||'', //3
48105                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48106                f.itemCls||this.itemCls||''  //5
48107        ], true).getPrevSibling());
48108     },
48109
48110     // private
48111     renderComponent : function(c){
48112         c.render(c.isLayout ? this.el : this.el.createChild());    
48113     },
48114     /**
48115      * Adds a object form elements (using the xtype property as the factory method.)
48116      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48117      * @param {Object} config 
48118      */
48119     addxtype : function(o)
48120     {
48121         // create the lement.
48122         o.form = this.form;
48123         var fe = Roo.factory(o, Roo.form);
48124         this.form.allItems.push(fe);
48125         this.stack.push(fe);
48126         
48127         if (fe.isFormField) {
48128             this.form.items.add(fe);
48129         }
48130          
48131         return fe;
48132     }
48133 });
48134
48135 /**
48136  * @class Roo.form.Column
48137  * @extends Roo.form.Layout
48138  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48139  * @constructor
48140  * @param {Object} config Configuration options
48141  */
48142 Roo.form.Column = function(config){
48143     Roo.form.Column.superclass.constructor.call(this, config);
48144 };
48145
48146 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48147     /**
48148      * @cfg {Number/String} width
48149      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48150      */
48151     /**
48152      * @cfg {String/Object} autoCreate
48153      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48154      */
48155
48156     // private
48157     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48158
48159     // private
48160     onRender : function(ct, position){
48161         Roo.form.Column.superclass.onRender.call(this, ct, position);
48162         if(this.width){
48163             this.el.setWidth(this.width);
48164         }
48165     }
48166 });
48167
48168
48169 /**
48170  * @class Roo.form.Row
48171  * @extends Roo.form.Layout
48172  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48173  * @constructor
48174  * @param {Object} config Configuration options
48175  */
48176
48177  
48178 Roo.form.Row = function(config){
48179     Roo.form.Row.superclass.constructor.call(this, config);
48180 };
48181  
48182 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48183       /**
48184      * @cfg {Number/String} width
48185      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48186      */
48187     /**
48188      * @cfg {Number/String} height
48189      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48190      */
48191     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48192     
48193     padWidth : 20,
48194     // private
48195     onRender : function(ct, position){
48196         //console.log('row render');
48197         if(!this.rowTpl){
48198             var t = new Roo.Template(
48199                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48200                     '<label for="{0}" style="{2}">{1}{4}</label>',
48201                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48202                     '</div>',
48203                 '</div>'
48204             );
48205             t.disableFormats = true;
48206             t.compile();
48207             Roo.form.Layout.prototype.rowTpl = t;
48208         }
48209         this.fieldTpl = this.rowTpl;
48210         
48211         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48212         var labelWidth = 100;
48213         
48214         if ((this.labelAlign != 'top')) {
48215             if (typeof this.labelWidth == 'number') {
48216                 labelWidth = this.labelWidth
48217             }
48218             this.padWidth =  20 + labelWidth;
48219             
48220         }
48221         
48222         Roo.form.Column.superclass.onRender.call(this, ct, position);
48223         if(this.width){
48224             this.el.setWidth(this.width);
48225         }
48226         if(this.height){
48227             this.el.setHeight(this.height);
48228         }
48229     },
48230     
48231     // private
48232     renderField : function(f){
48233         f.fieldEl = this.fieldTpl.append(this.el, [
48234                f.id, f.fieldLabel,
48235                f.labelStyle||this.labelStyle||'',
48236                this.elementStyle||'',
48237                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48238                f.itemCls||this.itemCls||'',
48239                f.width ? f.width + this.padWidth : 160 + this.padWidth
48240        ],true);
48241     }
48242 });
48243  
48244
48245 /**
48246  * @class Roo.form.FieldSet
48247  * @extends Roo.form.Layout
48248  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48249  * @constructor
48250  * @param {Object} config Configuration options
48251  */
48252 Roo.form.FieldSet = function(config){
48253     Roo.form.FieldSet.superclass.constructor.call(this, config);
48254 };
48255
48256 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48257     /**
48258      * @cfg {String} legend
48259      * The text to display as the legend for the FieldSet (defaults to '')
48260      */
48261     /**
48262      * @cfg {String/Object} autoCreate
48263      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48264      */
48265
48266     // private
48267     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48268
48269     // private
48270     onRender : function(ct, position){
48271         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48272         if(this.legend){
48273             this.setLegend(this.legend);
48274         }
48275     },
48276
48277     // private
48278     setLegend : function(text){
48279         if(this.rendered){
48280             this.el.child('legend').update(text);
48281         }
48282     }
48283 });/*
48284  * Based on:
48285  * Ext JS Library 1.1.1
48286  * Copyright(c) 2006-2007, Ext JS, LLC.
48287  *
48288  * Originally Released Under LGPL - original licence link has changed is not relivant.
48289  *
48290  * Fork - LGPL
48291  * <script type="text/javascript">
48292  */
48293 /**
48294  * @class Roo.form.VTypes
48295  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48296  * @singleton
48297  */
48298 Roo.form.VTypes = function(){
48299     // closure these in so they are only created once.
48300     var alpha = /^[a-zA-Z_]+$/;
48301     var alphanum = /^[a-zA-Z0-9_]+$/;
48302     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48303     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48304
48305     // All these messages and functions are configurable
48306     return {
48307         /**
48308          * The function used to validate email addresses
48309          * @param {String} value The email address
48310          */
48311         'email' : function(v){
48312             return email.test(v);
48313         },
48314         /**
48315          * The error text to display when the email validation function returns false
48316          * @type String
48317          */
48318         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48319         /**
48320          * The keystroke filter mask to be applied on email input
48321          * @type RegExp
48322          */
48323         'emailMask' : /[a-z0-9_\.\-@]/i,
48324
48325         /**
48326          * The function used to validate URLs
48327          * @param {String} value The URL
48328          */
48329         'url' : function(v){
48330             return url.test(v);
48331         },
48332         /**
48333          * The error text to display when the url validation function returns false
48334          * @type String
48335          */
48336         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48337         
48338         /**
48339          * The function used to validate alpha values
48340          * @param {String} value The value
48341          */
48342         'alpha' : function(v){
48343             return alpha.test(v);
48344         },
48345         /**
48346          * The error text to display when the alpha validation function returns false
48347          * @type String
48348          */
48349         'alphaText' : 'This field should only contain letters and _',
48350         /**
48351          * The keystroke filter mask to be applied on alpha input
48352          * @type RegExp
48353          */
48354         'alphaMask' : /[a-z_]/i,
48355
48356         /**
48357          * The function used to validate alphanumeric values
48358          * @param {String} value The value
48359          */
48360         'alphanum' : function(v){
48361             return alphanum.test(v);
48362         },
48363         /**
48364          * The error text to display when the alphanumeric validation function returns false
48365          * @type String
48366          */
48367         'alphanumText' : 'This field should only contain letters, numbers and _',
48368         /**
48369          * The keystroke filter mask to be applied on alphanumeric input
48370          * @type RegExp
48371          */
48372         'alphanumMask' : /[a-z0-9_]/i
48373     };
48374 }();//<script type="text/javascript">
48375
48376 /**
48377  * @class Roo.form.FCKeditor
48378  * @extends Roo.form.TextArea
48379  * Wrapper around the FCKEditor http://www.fckeditor.net
48380  * @constructor
48381  * Creates a new FCKeditor
48382  * @param {Object} config Configuration options
48383  */
48384 Roo.form.FCKeditor = function(config){
48385     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48386     this.addEvents({
48387          /**
48388          * @event editorinit
48389          * Fired when the editor is initialized - you can add extra handlers here..
48390          * @param {FCKeditor} this
48391          * @param {Object} the FCK object.
48392          */
48393         editorinit : true
48394     });
48395     
48396     
48397 };
48398 Roo.form.FCKeditor.editors = { };
48399 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48400 {
48401     //defaultAutoCreate : {
48402     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48403     //},
48404     // private
48405     /**
48406      * @cfg {Object} fck options - see fck manual for details.
48407      */
48408     fckconfig : false,
48409     
48410     /**
48411      * @cfg {Object} fck toolbar set (Basic or Default)
48412      */
48413     toolbarSet : 'Basic',
48414     /**
48415      * @cfg {Object} fck BasePath
48416      */ 
48417     basePath : '/fckeditor/',
48418     
48419     
48420     frame : false,
48421     
48422     value : '',
48423     
48424    
48425     onRender : function(ct, position)
48426     {
48427         if(!this.el){
48428             this.defaultAutoCreate = {
48429                 tag: "textarea",
48430                 style:"width:300px;height:60px;",
48431                 autocomplete: "new-password"
48432             };
48433         }
48434         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48435         /*
48436         if(this.grow){
48437             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48438             if(this.preventScrollbars){
48439                 this.el.setStyle("overflow", "hidden");
48440             }
48441             this.el.setHeight(this.growMin);
48442         }
48443         */
48444         //console.log('onrender' + this.getId() );
48445         Roo.form.FCKeditor.editors[this.getId()] = this;
48446          
48447
48448         this.replaceTextarea() ;
48449         
48450     },
48451     
48452     getEditor : function() {
48453         return this.fckEditor;
48454     },
48455     /**
48456      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48457      * @param {Mixed} value The value to set
48458      */
48459     
48460     
48461     setValue : function(value)
48462     {
48463         //console.log('setValue: ' + value);
48464         
48465         if(typeof(value) == 'undefined') { // not sure why this is happending...
48466             return;
48467         }
48468         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48469         
48470         //if(!this.el || !this.getEditor()) {
48471         //    this.value = value;
48472             //this.setValue.defer(100,this,[value]);    
48473         //    return;
48474         //} 
48475         
48476         if(!this.getEditor()) {
48477             return;
48478         }
48479         
48480         this.getEditor().SetData(value);
48481         
48482         //
48483
48484     },
48485
48486     /**
48487      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48488      * @return {Mixed} value The field value
48489      */
48490     getValue : function()
48491     {
48492         
48493         if (this.frame && this.frame.dom.style.display == 'none') {
48494             return Roo.form.FCKeditor.superclass.getValue.call(this);
48495         }
48496         
48497         if(!this.el || !this.getEditor()) {
48498            
48499            // this.getValue.defer(100,this); 
48500             return this.value;
48501         }
48502        
48503         
48504         var value=this.getEditor().GetData();
48505         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48506         return Roo.form.FCKeditor.superclass.getValue.call(this);
48507         
48508
48509     },
48510
48511     /**
48512      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48513      * @return {Mixed} value The field value
48514      */
48515     getRawValue : function()
48516     {
48517         if (this.frame && this.frame.dom.style.display == 'none') {
48518             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48519         }
48520         
48521         if(!this.el || !this.getEditor()) {
48522             //this.getRawValue.defer(100,this); 
48523             return this.value;
48524             return;
48525         }
48526         
48527         
48528         
48529         var value=this.getEditor().GetData();
48530         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48531         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48532          
48533     },
48534     
48535     setSize : function(w,h) {
48536         
48537         
48538         
48539         //if (this.frame && this.frame.dom.style.display == 'none') {
48540         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48541         //    return;
48542         //}
48543         //if(!this.el || !this.getEditor()) {
48544         //    this.setSize.defer(100,this, [w,h]); 
48545         //    return;
48546         //}
48547         
48548         
48549         
48550         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48551         
48552         this.frame.dom.setAttribute('width', w);
48553         this.frame.dom.setAttribute('height', h);
48554         this.frame.setSize(w,h);
48555         
48556     },
48557     
48558     toggleSourceEdit : function(value) {
48559         
48560       
48561          
48562         this.el.dom.style.display = value ? '' : 'none';
48563         this.frame.dom.style.display = value ?  'none' : '';
48564         
48565     },
48566     
48567     
48568     focus: function(tag)
48569     {
48570         if (this.frame.dom.style.display == 'none') {
48571             return Roo.form.FCKeditor.superclass.focus.call(this);
48572         }
48573         if(!this.el || !this.getEditor()) {
48574             this.focus.defer(100,this, [tag]); 
48575             return;
48576         }
48577         
48578         
48579         
48580         
48581         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48582         this.getEditor().Focus();
48583         if (tgs.length) {
48584             if (!this.getEditor().Selection.GetSelection()) {
48585                 this.focus.defer(100,this, [tag]); 
48586                 return;
48587             }
48588             
48589             
48590             var r = this.getEditor().EditorDocument.createRange();
48591             r.setStart(tgs[0],0);
48592             r.setEnd(tgs[0],0);
48593             this.getEditor().Selection.GetSelection().removeAllRanges();
48594             this.getEditor().Selection.GetSelection().addRange(r);
48595             this.getEditor().Focus();
48596         }
48597         
48598     },
48599     
48600     
48601     
48602     replaceTextarea : function()
48603     {
48604         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48605             return ;
48606         }
48607         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48608         //{
48609             // We must check the elements firstly using the Id and then the name.
48610         var oTextarea = document.getElementById( this.getId() );
48611         
48612         var colElementsByName = document.getElementsByName( this.getId() ) ;
48613          
48614         oTextarea.style.display = 'none' ;
48615
48616         if ( oTextarea.tabIndex ) {            
48617             this.TabIndex = oTextarea.tabIndex ;
48618         }
48619         
48620         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48621         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48622         this.frame = Roo.get(this.getId() + '___Frame')
48623     },
48624     
48625     _getConfigHtml : function()
48626     {
48627         var sConfig = '' ;
48628
48629         for ( var o in this.fckconfig ) {
48630             sConfig += sConfig.length > 0  ? '&amp;' : '';
48631             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48632         }
48633
48634         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48635     },
48636     
48637     
48638     _getIFrameHtml : function()
48639     {
48640         var sFile = 'fckeditor.html' ;
48641         /* no idea what this is about..
48642         try
48643         {
48644             if ( (/fcksource=true/i).test( window.top.location.search ) )
48645                 sFile = 'fckeditor.original.html' ;
48646         }
48647         catch (e) { 
48648         */
48649
48650         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48651         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48652         
48653         
48654         var html = '<iframe id="' + this.getId() +
48655             '___Frame" src="' + sLink +
48656             '" width="' + this.width +
48657             '" height="' + this.height + '"' +
48658             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48659             ' frameborder="0" scrolling="no"></iframe>' ;
48660
48661         return html ;
48662     },
48663     
48664     _insertHtmlBefore : function( html, element )
48665     {
48666         if ( element.insertAdjacentHTML )       {
48667             // IE
48668             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48669         } else { // Gecko
48670             var oRange = document.createRange() ;
48671             oRange.setStartBefore( element ) ;
48672             var oFragment = oRange.createContextualFragment( html );
48673             element.parentNode.insertBefore( oFragment, element ) ;
48674         }
48675     }
48676     
48677     
48678   
48679     
48680     
48681     
48682     
48683
48684 });
48685
48686 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48687
48688 function FCKeditor_OnComplete(editorInstance){
48689     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48690     f.fckEditor = editorInstance;
48691     //console.log("loaded");
48692     f.fireEvent('editorinit', f, editorInstance);
48693
48694   
48695
48696  
48697
48698
48699
48700
48701
48702
48703
48704
48705
48706
48707
48708
48709
48710
48711
48712 //<script type="text/javascript">
48713 /**
48714  * @class Roo.form.GridField
48715  * @extends Roo.form.Field
48716  * Embed a grid (or editable grid into a form)
48717  * STATUS ALPHA
48718  * 
48719  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48720  * it needs 
48721  * xgrid.store = Roo.data.Store
48722  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48723  * xgrid.store.reader = Roo.data.JsonReader 
48724  * 
48725  * 
48726  * @constructor
48727  * Creates a new GridField
48728  * @param {Object} config Configuration options
48729  */
48730 Roo.form.GridField = function(config){
48731     Roo.form.GridField.superclass.constructor.call(this, config);
48732      
48733 };
48734
48735 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48736     /**
48737      * @cfg {Number} width  - used to restrict width of grid..
48738      */
48739     width : 100,
48740     /**
48741      * @cfg {Number} height - used to restrict height of grid..
48742      */
48743     height : 50,
48744      /**
48745      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48746          * 
48747          *}
48748      */
48749     xgrid : false, 
48750     /**
48751      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48752      * {tag: "input", type: "checkbox", autocomplete: "off"})
48753      */
48754    // defaultAutoCreate : { tag: 'div' },
48755     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48756     /**
48757      * @cfg {String} addTitle Text to include for adding a title.
48758      */
48759     addTitle : false,
48760     //
48761     onResize : function(){
48762         Roo.form.Field.superclass.onResize.apply(this, arguments);
48763     },
48764
48765     initEvents : function(){
48766         // Roo.form.Checkbox.superclass.initEvents.call(this);
48767         // has no events...
48768        
48769     },
48770
48771
48772     getResizeEl : function(){
48773         return this.wrap;
48774     },
48775
48776     getPositionEl : function(){
48777         return this.wrap;
48778     },
48779
48780     // private
48781     onRender : function(ct, position){
48782         
48783         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48784         var style = this.style;
48785         delete this.style;
48786         
48787         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48788         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48789         this.viewEl = this.wrap.createChild({ tag: 'div' });
48790         if (style) {
48791             this.viewEl.applyStyles(style);
48792         }
48793         if (this.width) {
48794             this.viewEl.setWidth(this.width);
48795         }
48796         if (this.height) {
48797             this.viewEl.setHeight(this.height);
48798         }
48799         //if(this.inputValue !== undefined){
48800         //this.setValue(this.value);
48801         
48802         
48803         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48804         
48805         
48806         this.grid.render();
48807         this.grid.getDataSource().on('remove', this.refreshValue, this);
48808         this.grid.getDataSource().on('update', this.refreshValue, this);
48809         this.grid.on('afteredit', this.refreshValue, this);
48810  
48811     },
48812      
48813     
48814     /**
48815      * Sets the value of the item. 
48816      * @param {String} either an object  or a string..
48817      */
48818     setValue : function(v){
48819         //this.value = v;
48820         v = v || []; // empty set..
48821         // this does not seem smart - it really only affects memoryproxy grids..
48822         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48823             var ds = this.grid.getDataSource();
48824             // assumes a json reader..
48825             var data = {}
48826             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48827             ds.loadData( data);
48828         }
48829         // clear selection so it does not get stale.
48830         if (this.grid.sm) { 
48831             this.grid.sm.clearSelections();
48832         }
48833         
48834         Roo.form.GridField.superclass.setValue.call(this, v);
48835         this.refreshValue();
48836         // should load data in the grid really....
48837     },
48838     
48839     // private
48840     refreshValue: function() {
48841          var val = [];
48842         this.grid.getDataSource().each(function(r) {
48843             val.push(r.data);
48844         });
48845         this.el.dom.value = Roo.encode(val);
48846     }
48847     
48848      
48849     
48850     
48851 });/*
48852  * Based on:
48853  * Ext JS Library 1.1.1
48854  * Copyright(c) 2006-2007, Ext JS, LLC.
48855  *
48856  * Originally Released Under LGPL - original licence link has changed is not relivant.
48857  *
48858  * Fork - LGPL
48859  * <script type="text/javascript">
48860  */
48861 /**
48862  * @class Roo.form.DisplayField
48863  * @extends Roo.form.Field
48864  * A generic Field to display non-editable data.
48865  * @cfg {Boolean} closable (true|false) default false
48866  * @constructor
48867  * Creates a new Display Field item.
48868  * @param {Object} config Configuration options
48869  */
48870 Roo.form.DisplayField = function(config){
48871     Roo.form.DisplayField.superclass.constructor.call(this, config);
48872     
48873     this.addEvents({
48874         /**
48875          * @event close
48876          * Fires after the click the close btn
48877              * @param {Roo.form.DisplayField} this
48878              */
48879         close : true
48880     });
48881 };
48882
48883 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48884     inputType:      'hidden',
48885     allowBlank:     true,
48886     readOnly:         true,
48887     
48888  
48889     /**
48890      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48891      */
48892     focusClass : undefined,
48893     /**
48894      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48895      */
48896     fieldClass: 'x-form-field',
48897     
48898      /**
48899      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48900      */
48901     valueRenderer: undefined,
48902     
48903     width: 100,
48904     /**
48905      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48906      * {tag: "input", type: "checkbox", autocomplete: "off"})
48907      */
48908      
48909  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48910  
48911     closable : false,
48912     
48913     onResize : function(){
48914         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48915         
48916     },
48917
48918     initEvents : function(){
48919         // Roo.form.Checkbox.superclass.initEvents.call(this);
48920         // has no events...
48921         
48922         if(this.closable){
48923             this.closeEl.on('click', this.onClose, this);
48924         }
48925        
48926     },
48927
48928
48929     getResizeEl : function(){
48930         return this.wrap;
48931     },
48932
48933     getPositionEl : function(){
48934         return this.wrap;
48935     },
48936
48937     // private
48938     onRender : function(ct, position){
48939         
48940         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48941         //if(this.inputValue !== undefined){
48942         this.wrap = this.el.wrap();
48943         
48944         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48945         
48946         if(this.closable){
48947             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48948         }
48949         
48950         if (this.bodyStyle) {
48951             this.viewEl.applyStyles(this.bodyStyle);
48952         }
48953         //this.viewEl.setStyle('padding', '2px');
48954         
48955         this.setValue(this.value);
48956         
48957     },
48958 /*
48959     // private
48960     initValue : Roo.emptyFn,
48961
48962   */
48963
48964         // private
48965     onClick : function(){
48966         
48967     },
48968
48969     /**
48970      * Sets the checked state of the checkbox.
48971      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48972      */
48973     setValue : function(v){
48974         this.value = v;
48975         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48976         // this might be called before we have a dom element..
48977         if (!this.viewEl) {
48978             return;
48979         }
48980         this.viewEl.dom.innerHTML = html;
48981         Roo.form.DisplayField.superclass.setValue.call(this, v);
48982
48983     },
48984     
48985     onClose : function(e)
48986     {
48987         e.preventDefault();
48988         
48989         this.fireEvent('close', this);
48990     }
48991 });/*
48992  * 
48993  * Licence- LGPL
48994  * 
48995  */
48996
48997 /**
48998  * @class Roo.form.DayPicker
48999  * @extends Roo.form.Field
49000  * A Day picker show [M] [T] [W] ....
49001  * @constructor
49002  * Creates a new Day Picker
49003  * @param {Object} config Configuration options
49004  */
49005 Roo.form.DayPicker= function(config){
49006     Roo.form.DayPicker.superclass.constructor.call(this, config);
49007      
49008 };
49009
49010 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49011     /**
49012      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49013      */
49014     focusClass : undefined,
49015     /**
49016      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49017      */
49018     fieldClass: "x-form-field",
49019    
49020     /**
49021      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49022      * {tag: "input", type: "checkbox", autocomplete: "off"})
49023      */
49024     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49025     
49026    
49027     actionMode : 'viewEl', 
49028     //
49029     // private
49030  
49031     inputType : 'hidden',
49032     
49033      
49034     inputElement: false, // real input element?
49035     basedOn: false, // ????
49036     
49037     isFormField: true, // not sure where this is needed!!!!
49038
49039     onResize : function(){
49040         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49041         if(!this.boxLabel){
49042             this.el.alignTo(this.wrap, 'c-c');
49043         }
49044     },
49045
49046     initEvents : function(){
49047         Roo.form.Checkbox.superclass.initEvents.call(this);
49048         this.el.on("click", this.onClick,  this);
49049         this.el.on("change", this.onClick,  this);
49050     },
49051
49052
49053     getResizeEl : function(){
49054         return this.wrap;
49055     },
49056
49057     getPositionEl : function(){
49058         return this.wrap;
49059     },
49060
49061     
49062     // private
49063     onRender : function(ct, position){
49064         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49065        
49066         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49067         
49068         var r1 = '<table><tr>';
49069         var r2 = '<tr class="x-form-daypick-icons">';
49070         for (var i=0; i < 7; i++) {
49071             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49072             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49073         }
49074         
49075         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49076         viewEl.select('img').on('click', this.onClick, this);
49077         this.viewEl = viewEl;   
49078         
49079         
49080         // this will not work on Chrome!!!
49081         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49082         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49083         
49084         
49085           
49086
49087     },
49088
49089     // private
49090     initValue : Roo.emptyFn,
49091
49092     /**
49093      * Returns the checked state of the checkbox.
49094      * @return {Boolean} True if checked, else false
49095      */
49096     getValue : function(){
49097         return this.el.dom.value;
49098         
49099     },
49100
49101         // private
49102     onClick : function(e){ 
49103         //this.setChecked(!this.checked);
49104         Roo.get(e.target).toggleClass('x-menu-item-checked');
49105         this.refreshValue();
49106         //if(this.el.dom.checked != this.checked){
49107         //    this.setValue(this.el.dom.checked);
49108        // }
49109     },
49110     
49111     // private
49112     refreshValue : function()
49113     {
49114         var val = '';
49115         this.viewEl.select('img',true).each(function(e,i,n)  {
49116             val += e.is(".x-menu-item-checked") ? String(n) : '';
49117         });
49118         this.setValue(val, true);
49119     },
49120
49121     /**
49122      * Sets the checked state of the checkbox.
49123      * On is always based on a string comparison between inputValue and the param.
49124      * @param {Boolean/String} value - the value to set 
49125      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49126      */
49127     setValue : function(v,suppressEvent){
49128         if (!this.el.dom) {
49129             return;
49130         }
49131         var old = this.el.dom.value ;
49132         this.el.dom.value = v;
49133         if (suppressEvent) {
49134             return ;
49135         }
49136          
49137         // update display..
49138         this.viewEl.select('img',true).each(function(e,i,n)  {
49139             
49140             var on = e.is(".x-menu-item-checked");
49141             var newv = v.indexOf(String(n)) > -1;
49142             if (on != newv) {
49143                 e.toggleClass('x-menu-item-checked');
49144             }
49145             
49146         });
49147         
49148         
49149         this.fireEvent('change', this, v, old);
49150         
49151         
49152     },
49153    
49154     // handle setting of hidden value by some other method!!?!?
49155     setFromHidden: function()
49156     {
49157         if(!this.el){
49158             return;
49159         }
49160         //console.log("SET FROM HIDDEN");
49161         //alert('setFrom hidden');
49162         this.setValue(this.el.dom.value);
49163     },
49164     
49165     onDestroy : function()
49166     {
49167         if(this.viewEl){
49168             Roo.get(this.viewEl).remove();
49169         }
49170          
49171         Roo.form.DayPicker.superclass.onDestroy.call(this);
49172     }
49173
49174 });/*
49175  * RooJS Library 1.1.1
49176  * Copyright(c) 2008-2011  Alan Knowles
49177  *
49178  * License - LGPL
49179  */
49180  
49181
49182 /**
49183  * @class Roo.form.ComboCheck
49184  * @extends Roo.form.ComboBox
49185  * A combobox for multiple select items.
49186  *
49187  * FIXME - could do with a reset button..
49188  * 
49189  * @constructor
49190  * Create a new ComboCheck
49191  * @param {Object} config Configuration options
49192  */
49193 Roo.form.ComboCheck = function(config){
49194     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49195     // should verify some data...
49196     // like
49197     // hiddenName = required..
49198     // displayField = required
49199     // valudField == required
49200     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49201     var _t = this;
49202     Roo.each(req, function(e) {
49203         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49204             throw "Roo.form.ComboCheck : missing value for: " + e;
49205         }
49206     });
49207     
49208     
49209 };
49210
49211 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49212      
49213      
49214     editable : false,
49215      
49216     selectedClass: 'x-menu-item-checked', 
49217     
49218     // private
49219     onRender : function(ct, position){
49220         var _t = this;
49221         
49222         
49223         
49224         if(!this.tpl){
49225             var cls = 'x-combo-list';
49226
49227             
49228             this.tpl =  new Roo.Template({
49229                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49230                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49231                    '<span>{' + this.displayField + '}</span>' +
49232                     '</div>' 
49233                 
49234             });
49235         }
49236  
49237         
49238         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49239         this.view.singleSelect = false;
49240         this.view.multiSelect = true;
49241         this.view.toggleSelect = true;
49242         this.pageTb.add(new Roo.Toolbar.Fill(), {
49243             
49244             text: 'Done',
49245             handler: function()
49246             {
49247                 _t.collapse();
49248             }
49249         });
49250     },
49251     
49252     onViewOver : function(e, t){
49253         // do nothing...
49254         return;
49255         
49256     },
49257     
49258     onViewClick : function(doFocus,index){
49259         return;
49260         
49261     },
49262     select: function () {
49263         //Roo.log("SELECT CALLED");
49264     },
49265      
49266     selectByValue : function(xv, scrollIntoView){
49267         var ar = this.getValueArray();
49268         var sels = [];
49269         
49270         Roo.each(ar, function(v) {
49271             if(v === undefined || v === null){
49272                 return;
49273             }
49274             var r = this.findRecord(this.valueField, v);
49275             if(r){
49276                 sels.push(this.store.indexOf(r))
49277                 
49278             }
49279         },this);
49280         this.view.select(sels);
49281         return false;
49282     },
49283     
49284     
49285     
49286     onSelect : function(record, index){
49287        // Roo.log("onselect Called");
49288        // this is only called by the clear button now..
49289         this.view.clearSelections();
49290         this.setValue('[]');
49291         if (this.value != this.valueBefore) {
49292             this.fireEvent('change', this, this.value, this.valueBefore);
49293             this.valueBefore = this.value;
49294         }
49295     },
49296     getValueArray : function()
49297     {
49298         var ar = [] ;
49299         
49300         try {
49301             //Roo.log(this.value);
49302             if (typeof(this.value) == 'undefined') {
49303                 return [];
49304             }
49305             var ar = Roo.decode(this.value);
49306             return  ar instanceof Array ? ar : []; //?? valid?
49307             
49308         } catch(e) {
49309             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49310             return [];
49311         }
49312          
49313     },
49314     expand : function ()
49315     {
49316         
49317         Roo.form.ComboCheck.superclass.expand.call(this);
49318         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49319         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49320         
49321
49322     },
49323     
49324     collapse : function(){
49325         Roo.form.ComboCheck.superclass.collapse.call(this);
49326         var sl = this.view.getSelectedIndexes();
49327         var st = this.store;
49328         var nv = [];
49329         var tv = [];
49330         var r;
49331         Roo.each(sl, function(i) {
49332             r = st.getAt(i);
49333             nv.push(r.get(this.valueField));
49334         },this);
49335         this.setValue(Roo.encode(nv));
49336         if (this.value != this.valueBefore) {
49337
49338             this.fireEvent('change', this, this.value, this.valueBefore);
49339             this.valueBefore = this.value;
49340         }
49341         
49342     },
49343     
49344     setValue : function(v){
49345         // Roo.log(v);
49346         this.value = v;
49347         
49348         var vals = this.getValueArray();
49349         var tv = [];
49350         Roo.each(vals, function(k) {
49351             var r = this.findRecord(this.valueField, k);
49352             if(r){
49353                 tv.push(r.data[this.displayField]);
49354             }else if(this.valueNotFoundText !== undefined){
49355                 tv.push( this.valueNotFoundText );
49356             }
49357         },this);
49358        // Roo.log(tv);
49359         
49360         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49361         this.hiddenField.value = v;
49362         this.value = v;
49363     }
49364     
49365 });/*
49366  * Based on:
49367  * Ext JS Library 1.1.1
49368  * Copyright(c) 2006-2007, Ext JS, LLC.
49369  *
49370  * Originally Released Under LGPL - original licence link has changed is not relivant.
49371  *
49372  * Fork - LGPL
49373  * <script type="text/javascript">
49374  */
49375  
49376 /**
49377  * @class Roo.form.Signature
49378  * @extends Roo.form.Field
49379  * Signature field.  
49380  * @constructor
49381  * 
49382  * @param {Object} config Configuration options
49383  */
49384
49385 Roo.form.Signature = function(config){
49386     Roo.form.Signature.superclass.constructor.call(this, config);
49387     
49388     this.addEvents({// not in used??
49389          /**
49390          * @event confirm
49391          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49392              * @param {Roo.form.Signature} combo This combo box
49393              */
49394         'confirm' : true,
49395         /**
49396          * @event reset
49397          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49398              * @param {Roo.form.ComboBox} combo This combo box
49399              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49400              */
49401         'reset' : true
49402     });
49403 };
49404
49405 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49406     /**
49407      * @cfg {Object} labels Label to use when rendering a form.
49408      * defaults to 
49409      * labels : { 
49410      *      clear : "Clear",
49411      *      confirm : "Confirm"
49412      *  }
49413      */
49414     labels : { 
49415         clear : "Clear",
49416         confirm : "Confirm"
49417     },
49418     /**
49419      * @cfg {Number} width The signature panel width (defaults to 300)
49420      */
49421     width: 300,
49422     /**
49423      * @cfg {Number} height The signature panel height (defaults to 100)
49424      */
49425     height : 100,
49426     /**
49427      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49428      */
49429     allowBlank : false,
49430     
49431     //private
49432     // {Object} signPanel The signature SVG panel element (defaults to {})
49433     signPanel : {},
49434     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49435     isMouseDown : false,
49436     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49437     isConfirmed : false,
49438     // {String} signatureTmp SVG mapping string (defaults to empty string)
49439     signatureTmp : '',
49440     
49441     
49442     defaultAutoCreate : { // modified by initCompnoent..
49443         tag: "input",
49444         type:"hidden"
49445     },
49446
49447     // private
49448     onRender : function(ct, position){
49449         
49450         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49451         
49452         this.wrap = this.el.wrap({
49453             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49454         });
49455         
49456         this.createToolbar(this);
49457         this.signPanel = this.wrap.createChild({
49458                 tag: 'div',
49459                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49460             }, this.el
49461         );
49462             
49463         this.svgID = Roo.id();
49464         this.svgEl = this.signPanel.createChild({
49465               xmlns : 'http://www.w3.org/2000/svg',
49466               tag : 'svg',
49467               id : this.svgID + "-svg",
49468               width: this.width,
49469               height: this.height,
49470               viewBox: '0 0 '+this.width+' '+this.height,
49471               cn : [
49472                 {
49473                     tag: "rect",
49474                     id: this.svgID + "-svg-r",
49475                     width: this.width,
49476                     height: this.height,
49477                     fill: "#ffa"
49478                 },
49479                 {
49480                     tag: "line",
49481                     id: this.svgID + "-svg-l",
49482                     x1: "0", // start
49483                     y1: (this.height*0.8), // start set the line in 80% of height
49484                     x2: this.width, // end
49485                     y2: (this.height*0.8), // end set the line in 80% of height
49486                     'stroke': "#666",
49487                     'stroke-width': "1",
49488                     'stroke-dasharray': "3",
49489                     'shape-rendering': "crispEdges",
49490                     'pointer-events': "none"
49491                 },
49492                 {
49493                     tag: "path",
49494                     id: this.svgID + "-svg-p",
49495                     'stroke': "navy",
49496                     'stroke-width': "3",
49497                     'fill': "none",
49498                     'pointer-events': 'none'
49499                 }
49500               ]
49501         });
49502         this.createSVG();
49503         this.svgBox = this.svgEl.dom.getScreenCTM();
49504     },
49505     createSVG : function(){ 
49506         var svg = this.signPanel;
49507         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49508         var t = this;
49509
49510         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49511         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49512         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49513         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49514         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49515         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49516         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49517         
49518     },
49519     isTouchEvent : function(e){
49520         return e.type.match(/^touch/);
49521     },
49522     getCoords : function (e) {
49523         var pt    = this.svgEl.dom.createSVGPoint();
49524         pt.x = e.clientX; 
49525         pt.y = e.clientY;
49526         if (this.isTouchEvent(e)) {
49527             pt.x =  e.targetTouches[0].clientX;
49528             pt.y = e.targetTouches[0].clientY;
49529         }
49530         var a = this.svgEl.dom.getScreenCTM();
49531         var b = a.inverse();
49532         var mx = pt.matrixTransform(b);
49533         return mx.x + ',' + mx.y;
49534     },
49535     //mouse event headler 
49536     down : function (e) {
49537         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49538         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49539         
49540         this.isMouseDown = true;
49541         
49542         e.preventDefault();
49543     },
49544     move : function (e) {
49545         if (this.isMouseDown) {
49546             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49547             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49548         }
49549         
49550         e.preventDefault();
49551     },
49552     up : function (e) {
49553         this.isMouseDown = false;
49554         var sp = this.signatureTmp.split(' ');
49555         
49556         if(sp.length > 1){
49557             if(!sp[sp.length-2].match(/^L/)){
49558                 sp.pop();
49559                 sp.pop();
49560                 sp.push("");
49561                 this.signatureTmp = sp.join(" ");
49562             }
49563         }
49564         if(this.getValue() != this.signatureTmp){
49565             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49566             this.isConfirmed = false;
49567         }
49568         e.preventDefault();
49569     },
49570     
49571     /**
49572      * Protected method that will not generally be called directly. It
49573      * is called when the editor creates its toolbar. Override this method if you need to
49574      * add custom toolbar buttons.
49575      * @param {HtmlEditor} editor
49576      */
49577     createToolbar : function(editor){
49578          function btn(id, toggle, handler){
49579             var xid = fid + '-'+ id ;
49580             return {
49581                 id : xid,
49582                 cmd : id,
49583                 cls : 'x-btn-icon x-edit-'+id,
49584                 enableToggle:toggle !== false,
49585                 scope: editor, // was editor...
49586                 handler:handler||editor.relayBtnCmd,
49587                 clickEvent:'mousedown',
49588                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49589                 tabIndex:-1
49590             };
49591         }
49592         
49593         
49594         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49595         this.tb = tb;
49596         this.tb.add(
49597            {
49598                 cls : ' x-signature-btn x-signature-'+id,
49599                 scope: editor, // was editor...
49600                 handler: this.reset,
49601                 clickEvent:'mousedown',
49602                 text: this.labels.clear
49603             },
49604             {
49605                  xtype : 'Fill',
49606                  xns: Roo.Toolbar
49607             }, 
49608             {
49609                 cls : '  x-signature-btn x-signature-'+id,
49610                 scope: editor, // was editor...
49611                 handler: this.confirmHandler,
49612                 clickEvent:'mousedown',
49613                 text: this.labels.confirm
49614             }
49615         );
49616     
49617     },
49618     //public
49619     /**
49620      * when user is clicked confirm then show this image.....
49621      * 
49622      * @return {String} Image Data URI
49623      */
49624     getImageDataURI : function(){
49625         var svg = this.svgEl.dom.parentNode.innerHTML;
49626         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49627         return src; 
49628     },
49629     /**
49630      * 
49631      * @return {Boolean} this.isConfirmed
49632      */
49633     getConfirmed : function(){
49634         return this.isConfirmed;
49635     },
49636     /**
49637      * 
49638      * @return {Number} this.width
49639      */
49640     getWidth : function(){
49641         return this.width;
49642     },
49643     /**
49644      * 
49645      * @return {Number} this.height
49646      */
49647     getHeight : function(){
49648         return this.height;
49649     },
49650     // private
49651     getSignature : function(){
49652         return this.signatureTmp;
49653     },
49654     // private
49655     reset : function(){
49656         this.signatureTmp = '';
49657         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49658         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49659         this.isConfirmed = false;
49660         Roo.form.Signature.superclass.reset.call(this);
49661     },
49662     setSignature : function(s){
49663         this.signatureTmp = s;
49664         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49665         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49666         this.setValue(s);
49667         this.isConfirmed = false;
49668         Roo.form.Signature.superclass.reset.call(this);
49669     }, 
49670     test : function(){
49671 //        Roo.log(this.signPanel.dom.contentWindow.up())
49672     },
49673     //private
49674     setConfirmed : function(){
49675         
49676         
49677         
49678 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49679     },
49680     // private
49681     confirmHandler : function(){
49682         if(!this.getSignature()){
49683             return;
49684         }
49685         
49686         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49687         this.setValue(this.getSignature());
49688         this.isConfirmed = true;
49689         
49690         this.fireEvent('confirm', this);
49691     },
49692     // private
49693     // Subclasses should provide the validation implementation by overriding this
49694     validateValue : function(value){
49695         if(this.allowBlank){
49696             return true;
49697         }
49698         
49699         if(this.isConfirmed){
49700             return true;
49701         }
49702         return false;
49703     }
49704 });/*
49705  * Based on:
49706  * Ext JS Library 1.1.1
49707  * Copyright(c) 2006-2007, Ext JS, LLC.
49708  *
49709  * Originally Released Under LGPL - original licence link has changed is not relivant.
49710  *
49711  * Fork - LGPL
49712  * <script type="text/javascript">
49713  */
49714  
49715
49716 /**
49717  * @class Roo.form.ComboBox
49718  * @extends Roo.form.TriggerField
49719  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49720  * @constructor
49721  * Create a new ComboBox.
49722  * @param {Object} config Configuration options
49723  */
49724 Roo.form.Select = function(config){
49725     Roo.form.Select.superclass.constructor.call(this, config);
49726      
49727 };
49728
49729 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49730     /**
49731      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49732      */
49733     /**
49734      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49735      * rendering into an Roo.Editor, defaults to false)
49736      */
49737     /**
49738      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49739      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49740      */
49741     /**
49742      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49743      */
49744     /**
49745      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49746      * the dropdown list (defaults to undefined, with no header element)
49747      */
49748
49749      /**
49750      * @cfg {String/Roo.Template} tpl The template to use to render the output
49751      */
49752      
49753     // private
49754     defaultAutoCreate : {tag: "select"  },
49755     /**
49756      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49757      */
49758     listWidth: undefined,
49759     /**
49760      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49761      * mode = 'remote' or 'text' if mode = 'local')
49762      */
49763     displayField: undefined,
49764     /**
49765      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49766      * mode = 'remote' or 'value' if mode = 'local'). 
49767      * Note: use of a valueField requires the user make a selection
49768      * in order for a value to be mapped.
49769      */
49770     valueField: undefined,
49771     
49772     
49773     /**
49774      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49775      * field's data value (defaults to the underlying DOM element's name)
49776      */
49777     hiddenName: undefined,
49778     /**
49779      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49780      */
49781     listClass: '',
49782     /**
49783      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49784      */
49785     selectedClass: 'x-combo-selected',
49786     /**
49787      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49788      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49789      * which displays a downward arrow icon).
49790      */
49791     triggerClass : 'x-form-arrow-trigger',
49792     /**
49793      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49794      */
49795     shadow:'sides',
49796     /**
49797      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49798      * anchor positions (defaults to 'tl-bl')
49799      */
49800     listAlign: 'tl-bl?',
49801     /**
49802      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49803      */
49804     maxHeight: 300,
49805     /**
49806      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49807      * query specified by the allQuery config option (defaults to 'query')
49808      */
49809     triggerAction: 'query',
49810     /**
49811      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49812      * (defaults to 4, does not apply if editable = false)
49813      */
49814     minChars : 4,
49815     /**
49816      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49817      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49818      */
49819     typeAhead: false,
49820     /**
49821      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49822      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49823      */
49824     queryDelay: 500,
49825     /**
49826      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49827      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49828      */
49829     pageSize: 0,
49830     /**
49831      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49832      * when editable = true (defaults to false)
49833      */
49834     selectOnFocus:false,
49835     /**
49836      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49837      */
49838     queryParam: 'query',
49839     /**
49840      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49841      * when mode = 'remote' (defaults to 'Loading...')
49842      */
49843     loadingText: 'Loading...',
49844     /**
49845      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49846      */
49847     resizable: false,
49848     /**
49849      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49850      */
49851     handleHeight : 8,
49852     /**
49853      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49854      * traditional select (defaults to true)
49855      */
49856     editable: true,
49857     /**
49858      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49859      */
49860     allQuery: '',
49861     /**
49862      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49863      */
49864     mode: 'remote',
49865     /**
49866      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49867      * listWidth has a higher value)
49868      */
49869     minListWidth : 70,
49870     /**
49871      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49872      * allow the user to set arbitrary text into the field (defaults to false)
49873      */
49874     forceSelection:false,
49875     /**
49876      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49877      * if typeAhead = true (defaults to 250)
49878      */
49879     typeAheadDelay : 250,
49880     /**
49881      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49882      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49883      */
49884     valueNotFoundText : undefined,
49885     
49886     /**
49887      * @cfg {String} defaultValue The value displayed after loading the store.
49888      */
49889     defaultValue: '',
49890     
49891     /**
49892      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49893      */
49894     blockFocus : false,
49895     
49896     /**
49897      * @cfg {Boolean} disableClear Disable showing of clear button.
49898      */
49899     disableClear : false,
49900     /**
49901      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49902      */
49903     alwaysQuery : false,
49904     
49905     //private
49906     addicon : false,
49907     editicon: false,
49908     
49909     // element that contains real text value.. (when hidden is used..)
49910      
49911     // private
49912     onRender : function(ct, position){
49913         Roo.form.Field.prototype.onRender.call(this, ct, position);
49914         
49915         if(this.store){
49916             this.store.on('beforeload', this.onBeforeLoad, this);
49917             this.store.on('load', this.onLoad, this);
49918             this.store.on('loadexception', this.onLoadException, this);
49919             this.store.load({});
49920         }
49921         
49922         
49923         
49924     },
49925
49926     // private
49927     initEvents : function(){
49928         //Roo.form.ComboBox.superclass.initEvents.call(this);
49929  
49930     },
49931
49932     onDestroy : function(){
49933        
49934         if(this.store){
49935             this.store.un('beforeload', this.onBeforeLoad, this);
49936             this.store.un('load', this.onLoad, this);
49937             this.store.un('loadexception', this.onLoadException, this);
49938         }
49939         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49940     },
49941
49942     // private
49943     fireKey : function(e){
49944         if(e.isNavKeyPress() && !this.list.isVisible()){
49945             this.fireEvent("specialkey", this, e);
49946         }
49947     },
49948
49949     // private
49950     onResize: function(w, h){
49951         
49952         return; 
49953     
49954         
49955     },
49956
49957     /**
49958      * Allow or prevent the user from directly editing the field text.  If false is passed,
49959      * the user will only be able to select from the items defined in the dropdown list.  This method
49960      * is the runtime equivalent of setting the 'editable' config option at config time.
49961      * @param {Boolean} value True to allow the user to directly edit the field text
49962      */
49963     setEditable : function(value){
49964          
49965     },
49966
49967     // private
49968     onBeforeLoad : function(){
49969         
49970         Roo.log("Select before load");
49971         return;
49972     
49973         this.innerList.update(this.loadingText ?
49974                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49975         //this.restrictHeight();
49976         this.selectedIndex = -1;
49977     },
49978
49979     // private
49980     onLoad : function(){
49981
49982     
49983         var dom = this.el.dom;
49984         dom.innerHTML = '';
49985          var od = dom.ownerDocument;
49986          
49987         if (this.emptyText) {
49988             var op = od.createElement('option');
49989             op.setAttribute('value', '');
49990             op.innerHTML = String.format('{0}', this.emptyText);
49991             dom.appendChild(op);
49992         }
49993         if(this.store.getCount() > 0){
49994            
49995             var vf = this.valueField;
49996             var df = this.displayField;
49997             this.store.data.each(function(r) {
49998                 // which colmsn to use... testing - cdoe / title..
49999                 var op = od.createElement('option');
50000                 op.setAttribute('value', r.data[vf]);
50001                 op.innerHTML = String.format('{0}', r.data[df]);
50002                 dom.appendChild(op);
50003             });
50004             if (typeof(this.defaultValue != 'undefined')) {
50005                 this.setValue(this.defaultValue);
50006             }
50007             
50008              
50009         }else{
50010             //this.onEmptyResults();
50011         }
50012         //this.el.focus();
50013     },
50014     // private
50015     onLoadException : function()
50016     {
50017         dom.innerHTML = '';
50018             
50019         Roo.log("Select on load exception");
50020         return;
50021     
50022         this.collapse();
50023         Roo.log(this.store.reader.jsonData);
50024         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50025             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50026         }
50027         
50028         
50029     },
50030     // private
50031     onTypeAhead : function(){
50032          
50033     },
50034
50035     // private
50036     onSelect : function(record, index){
50037         Roo.log('on select?');
50038         return;
50039         if(this.fireEvent('beforeselect', this, record, index) !== false){
50040             this.setFromData(index > -1 ? record.data : false);
50041             this.collapse();
50042             this.fireEvent('select', this, record, index);
50043         }
50044     },
50045
50046     /**
50047      * Returns the currently selected field value or empty string if no value is set.
50048      * @return {String} value The selected value
50049      */
50050     getValue : function(){
50051         var dom = this.el.dom;
50052         this.value = dom.options[dom.selectedIndex].value;
50053         return this.value;
50054         
50055     },
50056
50057     /**
50058      * Clears any text/value currently set in the field
50059      */
50060     clearValue : function(){
50061         this.value = '';
50062         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50063         
50064     },
50065
50066     /**
50067      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50068      * will be displayed in the field.  If the value does not match the data value of an existing item,
50069      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50070      * Otherwise the field will be blank (although the value will still be set).
50071      * @param {String} value The value to match
50072      */
50073     setValue : function(v){
50074         var d = this.el.dom;
50075         for (var i =0; i < d.options.length;i++) {
50076             if (v == d.options[i].value) {
50077                 d.selectedIndex = i;
50078                 this.value = v;
50079                 return;
50080             }
50081         }
50082         this.clearValue();
50083     },
50084     /**
50085      * @property {Object} the last set data for the element
50086      */
50087     
50088     lastData : false,
50089     /**
50090      * Sets the value of the field based on a object which is related to the record format for the store.
50091      * @param {Object} value the value to set as. or false on reset?
50092      */
50093     setFromData : function(o){
50094         Roo.log('setfrom data?');
50095          
50096         
50097         
50098     },
50099     // private
50100     reset : function(){
50101         this.clearValue();
50102     },
50103     // private
50104     findRecord : function(prop, value){
50105         
50106         return false;
50107     
50108         var record;
50109         if(this.store.getCount() > 0){
50110             this.store.each(function(r){
50111                 if(r.data[prop] == value){
50112                     record = r;
50113                     return false;
50114                 }
50115                 return true;
50116             });
50117         }
50118         return record;
50119     },
50120     
50121     getName: function()
50122     {
50123         // returns hidden if it's set..
50124         if (!this.rendered) {return ''};
50125         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50126         
50127     },
50128      
50129
50130     
50131
50132     // private
50133     onEmptyResults : function(){
50134         Roo.log('empty results');
50135         //this.collapse();
50136     },
50137
50138     /**
50139      * Returns true if the dropdown list is expanded, else false.
50140      */
50141     isExpanded : function(){
50142         return false;
50143     },
50144
50145     /**
50146      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50147      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50148      * @param {String} value The data value of the item to select
50149      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50150      * selected item if it is not currently in view (defaults to true)
50151      * @return {Boolean} True if the value matched an item in the list, else false
50152      */
50153     selectByValue : function(v, scrollIntoView){
50154         Roo.log('select By Value');
50155         return false;
50156     
50157         if(v !== undefined && v !== null){
50158             var r = this.findRecord(this.valueField || this.displayField, v);
50159             if(r){
50160                 this.select(this.store.indexOf(r), scrollIntoView);
50161                 return true;
50162             }
50163         }
50164         return false;
50165     },
50166
50167     /**
50168      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50169      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50170      * @param {Number} index The zero-based index of the list item to select
50171      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50172      * selected item if it is not currently in view (defaults to true)
50173      */
50174     select : function(index, scrollIntoView){
50175         Roo.log('select ');
50176         return  ;
50177         
50178         this.selectedIndex = index;
50179         this.view.select(index);
50180         if(scrollIntoView !== false){
50181             var el = this.view.getNode(index);
50182             if(el){
50183                 this.innerList.scrollChildIntoView(el, false);
50184             }
50185         }
50186     },
50187
50188       
50189
50190     // private
50191     validateBlur : function(){
50192         
50193         return;
50194         
50195     },
50196
50197     // private
50198     initQuery : function(){
50199         this.doQuery(this.getRawValue());
50200     },
50201
50202     // private
50203     doForce : function(){
50204         if(this.el.dom.value.length > 0){
50205             this.el.dom.value =
50206                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50207              
50208         }
50209     },
50210
50211     /**
50212      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50213      * query allowing the query action to be canceled if needed.
50214      * @param {String} query The SQL query to execute
50215      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50216      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50217      * saved in the current store (defaults to false)
50218      */
50219     doQuery : function(q, forceAll){
50220         
50221         Roo.log('doQuery?');
50222         if(q === undefined || q === null){
50223             q = '';
50224         }
50225         var qe = {
50226             query: q,
50227             forceAll: forceAll,
50228             combo: this,
50229             cancel:false
50230         };
50231         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50232             return false;
50233         }
50234         q = qe.query;
50235         forceAll = qe.forceAll;
50236         if(forceAll === true || (q.length >= this.minChars)){
50237             if(this.lastQuery != q || this.alwaysQuery){
50238                 this.lastQuery = q;
50239                 if(this.mode == 'local'){
50240                     this.selectedIndex = -1;
50241                     if(forceAll){
50242                         this.store.clearFilter();
50243                     }else{
50244                         this.store.filter(this.displayField, q);
50245                     }
50246                     this.onLoad();
50247                 }else{
50248                     this.store.baseParams[this.queryParam] = q;
50249                     this.store.load({
50250                         params: this.getParams(q)
50251                     });
50252                     this.expand();
50253                 }
50254             }else{
50255                 this.selectedIndex = -1;
50256                 this.onLoad();   
50257             }
50258         }
50259     },
50260
50261     // private
50262     getParams : function(q){
50263         var p = {};
50264         //p[this.queryParam] = q;
50265         if(this.pageSize){
50266             p.start = 0;
50267             p.limit = this.pageSize;
50268         }
50269         return p;
50270     },
50271
50272     /**
50273      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50274      */
50275     collapse : function(){
50276         
50277     },
50278
50279     // private
50280     collapseIf : function(e){
50281         
50282     },
50283
50284     /**
50285      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50286      */
50287     expand : function(){
50288         
50289     } ,
50290
50291     // private
50292      
50293
50294     /** 
50295     * @cfg {Boolean} grow 
50296     * @hide 
50297     */
50298     /** 
50299     * @cfg {Number} growMin 
50300     * @hide 
50301     */
50302     /** 
50303     * @cfg {Number} growMax 
50304     * @hide 
50305     */
50306     /**
50307      * @hide
50308      * @method autoSize
50309      */
50310     
50311     setWidth : function()
50312     {
50313         
50314     },
50315     getResizeEl : function(){
50316         return this.el;
50317     }
50318 });//<script type="text/javasscript">
50319  
50320
50321 /**
50322  * @class Roo.DDView
50323  * A DnD enabled version of Roo.View.
50324  * @param {Element/String} container The Element in which to create the View.
50325  * @param {String} tpl The template string used to create the markup for each element of the View
50326  * @param {Object} config The configuration properties. These include all the config options of
50327  * {@link Roo.View} plus some specific to this class.<br>
50328  * <p>
50329  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50330  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50331  * <p>
50332  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50333 .x-view-drag-insert-above {
50334         border-top:1px dotted #3366cc;
50335 }
50336 .x-view-drag-insert-below {
50337         border-bottom:1px dotted #3366cc;
50338 }
50339 </code></pre>
50340  * 
50341  */
50342  
50343 Roo.DDView = function(container, tpl, config) {
50344     Roo.DDView.superclass.constructor.apply(this, arguments);
50345     this.getEl().setStyle("outline", "0px none");
50346     this.getEl().unselectable();
50347     if (this.dragGroup) {
50348                 this.setDraggable(this.dragGroup.split(","));
50349     }
50350     if (this.dropGroup) {
50351                 this.setDroppable(this.dropGroup.split(","));
50352     }
50353     if (this.deletable) {
50354         this.setDeletable();
50355     }
50356     this.isDirtyFlag = false;
50357         this.addEvents({
50358                 "drop" : true
50359         });
50360 };
50361
50362 Roo.extend(Roo.DDView, Roo.View, {
50363 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50364 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50365 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50366 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50367
50368         isFormField: true,
50369
50370         reset: Roo.emptyFn,
50371         
50372         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50373
50374         validate: function() {
50375                 return true;
50376         },
50377         
50378         destroy: function() {
50379                 this.purgeListeners();
50380                 this.getEl.removeAllListeners();
50381                 this.getEl().remove();
50382                 if (this.dragZone) {
50383                         if (this.dragZone.destroy) {
50384                                 this.dragZone.destroy();
50385                         }
50386                 }
50387                 if (this.dropZone) {
50388                         if (this.dropZone.destroy) {
50389                                 this.dropZone.destroy();
50390                         }
50391                 }
50392         },
50393
50394 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50395         getName: function() {
50396                 return this.name;
50397         },
50398
50399 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50400         setValue: function(v) {
50401                 if (!this.store) {
50402                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50403                 }
50404                 var data = {};
50405                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50406                 this.store.proxy = new Roo.data.MemoryProxy(data);
50407                 this.store.load();
50408         },
50409
50410 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50411         getValue: function() {
50412                 var result = '(';
50413                 this.store.each(function(rec) {
50414                         result += rec.id + ',';
50415                 });
50416                 return result.substr(0, result.length - 1) + ')';
50417         },
50418         
50419         getIds: function() {
50420                 var i = 0, result = new Array(this.store.getCount());
50421                 this.store.each(function(rec) {
50422                         result[i++] = rec.id;
50423                 });
50424                 return result;
50425         },
50426         
50427         isDirty: function() {
50428                 return this.isDirtyFlag;
50429         },
50430
50431 /**
50432  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50433  *      whole Element becomes the target, and this causes the drop gesture to append.
50434  */
50435     getTargetFromEvent : function(e) {
50436                 var target = e.getTarget();
50437                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50438                 target = target.parentNode;
50439                 }
50440                 if (!target) {
50441                         target = this.el.dom.lastChild || this.el.dom;
50442                 }
50443                 return target;
50444     },
50445
50446 /**
50447  *      Create the drag data which consists of an object which has the property "ddel" as
50448  *      the drag proxy element. 
50449  */
50450     getDragData : function(e) {
50451         var target = this.findItemFromChild(e.getTarget());
50452                 if(target) {
50453                         this.handleSelection(e);
50454                         var selNodes = this.getSelectedNodes();
50455             var dragData = {
50456                 source: this,
50457                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50458                 nodes: selNodes,
50459                 records: []
50460                         };
50461                         var selectedIndices = this.getSelectedIndexes();
50462                         for (var i = 0; i < selectedIndices.length; i++) {
50463                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50464                         }
50465                         if (selNodes.length == 1) {
50466                                 dragData.ddel = target.cloneNode(true); // the div element
50467                         } else {
50468                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50469                                 div.className = 'multi-proxy';
50470                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50471                                         div.appendChild(selNodes[i].cloneNode(true));
50472                                 }
50473                                 dragData.ddel = div;
50474                         }
50475             //console.log(dragData)
50476             //console.log(dragData.ddel.innerHTML)
50477                         return dragData;
50478                 }
50479         //console.log('nodragData')
50480                 return false;
50481     },
50482     
50483 /**     Specify to which ddGroup items in this DDView may be dragged. */
50484     setDraggable: function(ddGroup) {
50485         if (ddGroup instanceof Array) {
50486                 Roo.each(ddGroup, this.setDraggable, this);
50487                 return;
50488         }
50489         if (this.dragZone) {
50490                 this.dragZone.addToGroup(ddGroup);
50491         } else {
50492                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50493                                 containerScroll: true,
50494                                 ddGroup: ddGroup 
50495
50496                         });
50497 //                      Draggability implies selection. DragZone's mousedown selects the element.
50498                         if (!this.multiSelect) { this.singleSelect = true; }
50499
50500 //                      Wire the DragZone's handlers up to methods in *this*
50501                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50502                 }
50503     },
50504
50505 /**     Specify from which ddGroup this DDView accepts drops. */
50506     setDroppable: function(ddGroup) {
50507         if (ddGroup instanceof Array) {
50508                 Roo.each(ddGroup, this.setDroppable, this);
50509                 return;
50510         }
50511         if (this.dropZone) {
50512                 this.dropZone.addToGroup(ddGroup);
50513         } else {
50514                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50515                                 containerScroll: true,
50516                                 ddGroup: ddGroup
50517                         });
50518
50519 //                      Wire the DropZone's handlers up to methods in *this*
50520                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50521                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50522                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50523                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50524                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50525                 }
50526     },
50527
50528 /**     Decide whether to drop above or below a View node. */
50529     getDropPoint : function(e, n, dd){
50530         if (n == this.el.dom) { return "above"; }
50531                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50532                 var c = t + (b - t) / 2;
50533                 var y = Roo.lib.Event.getPageY(e);
50534                 if(y <= c) {
50535                         return "above";
50536                 }else{
50537                         return "below";
50538                 }
50539     },
50540
50541     onNodeEnter : function(n, dd, e, data){
50542                 return false;
50543     },
50544     
50545     onNodeOver : function(n, dd, e, data){
50546                 var pt = this.getDropPoint(e, n, dd);
50547                 // set the insert point style on the target node
50548                 var dragElClass = this.dropNotAllowed;
50549                 if (pt) {
50550                         var targetElClass;
50551                         if (pt == "above"){
50552                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50553                                 targetElClass = "x-view-drag-insert-above";
50554                         } else {
50555                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50556                                 targetElClass = "x-view-drag-insert-below";
50557                         }
50558                         if (this.lastInsertClass != targetElClass){
50559                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50560                                 this.lastInsertClass = targetElClass;
50561                         }
50562                 }
50563                 return dragElClass;
50564         },
50565
50566     onNodeOut : function(n, dd, e, data){
50567                 this.removeDropIndicators(n);
50568     },
50569
50570     onNodeDrop : function(n, dd, e, data){
50571         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50572                 return false;
50573         }
50574         var pt = this.getDropPoint(e, n, dd);
50575                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50576                 if (pt == "below") { insertAt++; }
50577                 for (var i = 0; i < data.records.length; i++) {
50578                         var r = data.records[i];
50579                         var dup = this.store.getById(r.id);
50580                         if (dup && (dd != this.dragZone)) {
50581                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50582                         } else {
50583                                 if (data.copy) {
50584                                         this.store.insert(insertAt++, r.copy());
50585                                 } else {
50586                                         data.source.isDirtyFlag = true;
50587                                         r.store.remove(r);
50588                                         this.store.insert(insertAt++, r);
50589                                 }
50590                                 this.isDirtyFlag = true;
50591                         }
50592                 }
50593                 this.dragZone.cachedTarget = null;
50594                 return true;
50595     },
50596
50597     removeDropIndicators : function(n){
50598                 if(n){
50599                         Roo.fly(n).removeClass([
50600                                 "x-view-drag-insert-above",
50601                                 "x-view-drag-insert-below"]);
50602                         this.lastInsertClass = "_noclass";
50603                 }
50604     },
50605
50606 /**
50607  *      Utility method. Add a delete option to the DDView's context menu.
50608  *      @param {String} imageUrl The URL of the "delete" icon image.
50609  */
50610         setDeletable: function(imageUrl) {
50611                 if (!this.singleSelect && !this.multiSelect) {
50612                         this.singleSelect = true;
50613                 }
50614                 var c = this.getContextMenu();
50615                 this.contextMenu.on("itemclick", function(item) {
50616                         switch (item.id) {
50617                                 case "delete":
50618                                         this.remove(this.getSelectedIndexes());
50619                                         break;
50620                         }
50621                 }, this);
50622                 this.contextMenu.add({
50623                         icon: imageUrl,
50624                         id: "delete",
50625                         text: 'Delete'
50626                 });
50627         },
50628         
50629 /**     Return the context menu for this DDView. */
50630         getContextMenu: function() {
50631                 if (!this.contextMenu) {
50632 //                      Create the View's context menu
50633                         this.contextMenu = new Roo.menu.Menu({
50634                                 id: this.id + "-contextmenu"
50635                         });
50636                         this.el.on("contextmenu", this.showContextMenu, this);
50637                 }
50638                 return this.contextMenu;
50639         },
50640         
50641         disableContextMenu: function() {
50642                 if (this.contextMenu) {
50643                         this.el.un("contextmenu", this.showContextMenu, this);
50644                 }
50645         },
50646
50647         showContextMenu: function(e, item) {
50648         item = this.findItemFromChild(e.getTarget());
50649                 if (item) {
50650                         e.stopEvent();
50651                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50652                         this.contextMenu.showAt(e.getXY());
50653             }
50654     },
50655
50656 /**
50657  *      Remove {@link Roo.data.Record}s at the specified indices.
50658  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50659  */
50660     remove: function(selectedIndices) {
50661                 selectedIndices = [].concat(selectedIndices);
50662                 for (var i = 0; i < selectedIndices.length; i++) {
50663                         var rec = this.store.getAt(selectedIndices[i]);
50664                         this.store.remove(rec);
50665                 }
50666     },
50667
50668 /**
50669  *      Double click fires the event, but also, if this is draggable, and there is only one other
50670  *      related DropZone, it transfers the selected node.
50671  */
50672     onDblClick : function(e){
50673         var item = this.findItemFromChild(e.getTarget());
50674         if(item){
50675             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50676                 return false;
50677             }
50678             if (this.dragGroup) {
50679                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50680                     while (targets.indexOf(this.dropZone) > -1) {
50681                             targets.remove(this.dropZone);
50682                                 }
50683                     if (targets.length == 1) {
50684                                         this.dragZone.cachedTarget = null;
50685                         var el = Roo.get(targets[0].getEl());
50686                         var box = el.getBox(true);
50687                         targets[0].onNodeDrop(el.dom, {
50688                                 target: el.dom,
50689                                 xy: [box.x, box.y + box.height - 1]
50690                         }, null, this.getDragData(e));
50691                     }
50692                 }
50693         }
50694     },
50695     
50696     handleSelection: function(e) {
50697                 this.dragZone.cachedTarget = null;
50698         var item = this.findItemFromChild(e.getTarget());
50699         if (!item) {
50700                 this.clearSelections(true);
50701                 return;
50702         }
50703                 if (item && (this.multiSelect || this.singleSelect)){
50704                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50705                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50706                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50707                                 this.unselect(item);
50708                         } else {
50709                                 this.select(item, this.multiSelect && e.ctrlKey);
50710                                 this.lastSelection = item;
50711                         }
50712                 }
50713     },
50714
50715     onItemClick : function(item, index, e){
50716                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50717                         return false;
50718                 }
50719                 return true;
50720     },
50721
50722     unselect : function(nodeInfo, suppressEvent){
50723                 var node = this.getNode(nodeInfo);
50724                 if(node && this.isSelected(node)){
50725                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50726                                 Roo.fly(node).removeClass(this.selectedClass);
50727                                 this.selections.remove(node);
50728                                 if(!suppressEvent){
50729                                         this.fireEvent("selectionchange", this, this.selections);
50730                                 }
50731                         }
50732                 }
50733     }
50734 });
50735 /*
50736  * Based on:
50737  * Ext JS Library 1.1.1
50738  * Copyright(c) 2006-2007, Ext JS, LLC.
50739  *
50740  * Originally Released Under LGPL - original licence link has changed is not relivant.
50741  *
50742  * Fork - LGPL
50743  * <script type="text/javascript">
50744  */
50745  
50746 /**
50747  * @class Roo.LayoutManager
50748  * @extends Roo.util.Observable
50749  * Base class for layout managers.
50750  */
50751 Roo.LayoutManager = function(container, config){
50752     Roo.LayoutManager.superclass.constructor.call(this);
50753     this.el = Roo.get(container);
50754     // ie scrollbar fix
50755     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50756         document.body.scroll = "no";
50757     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50758         this.el.position('relative');
50759     }
50760     this.id = this.el.id;
50761     this.el.addClass("x-layout-container");
50762     /** false to disable window resize monitoring @type Boolean */
50763     this.monitorWindowResize = true;
50764     this.regions = {};
50765     this.addEvents({
50766         /**
50767          * @event layout
50768          * Fires when a layout is performed. 
50769          * @param {Roo.LayoutManager} this
50770          */
50771         "layout" : true,
50772         /**
50773          * @event regionresized
50774          * Fires when the user resizes a region. 
50775          * @param {Roo.LayoutRegion} region The resized region
50776          * @param {Number} newSize The new size (width for east/west, height for north/south)
50777          */
50778         "regionresized" : true,
50779         /**
50780          * @event regioncollapsed
50781          * Fires when a region is collapsed. 
50782          * @param {Roo.LayoutRegion} region The collapsed region
50783          */
50784         "regioncollapsed" : true,
50785         /**
50786          * @event regionexpanded
50787          * Fires when a region is expanded.  
50788          * @param {Roo.LayoutRegion} region The expanded region
50789          */
50790         "regionexpanded" : true
50791     });
50792     this.updating = false;
50793     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50794 };
50795
50796 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50797     /**
50798      * Returns true if this layout is currently being updated
50799      * @return {Boolean}
50800      */
50801     isUpdating : function(){
50802         return this.updating; 
50803     },
50804     
50805     /**
50806      * Suspend the LayoutManager from doing auto-layouts while
50807      * making multiple add or remove calls
50808      */
50809     beginUpdate : function(){
50810         this.updating = true;    
50811     },
50812     
50813     /**
50814      * Restore auto-layouts and optionally disable the manager from performing a layout
50815      * @param {Boolean} noLayout true to disable a layout update 
50816      */
50817     endUpdate : function(noLayout){
50818         this.updating = false;
50819         if(!noLayout){
50820             this.layout();
50821         }    
50822     },
50823     
50824     layout: function(){
50825         
50826     },
50827     
50828     onRegionResized : function(region, newSize){
50829         this.fireEvent("regionresized", region, newSize);
50830         this.layout();
50831     },
50832     
50833     onRegionCollapsed : function(region){
50834         this.fireEvent("regioncollapsed", region);
50835     },
50836     
50837     onRegionExpanded : function(region){
50838         this.fireEvent("regionexpanded", region);
50839     },
50840         
50841     /**
50842      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50843      * performs box-model adjustments.
50844      * @return {Object} The size as an object {width: (the width), height: (the height)}
50845      */
50846     getViewSize : function(){
50847         var size;
50848         if(this.el.dom != document.body){
50849             size = this.el.getSize();
50850         }else{
50851             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50852         }
50853         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50854         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50855         return size;
50856     },
50857     
50858     /**
50859      * Returns the Element this layout is bound to.
50860      * @return {Roo.Element}
50861      */
50862     getEl : function(){
50863         return this.el;
50864     },
50865     
50866     /**
50867      * Returns the specified region.
50868      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50869      * @return {Roo.LayoutRegion}
50870      */
50871     getRegion : function(target){
50872         return this.regions[target.toLowerCase()];
50873     },
50874     
50875     onWindowResize : function(){
50876         if(this.monitorWindowResize){
50877             this.layout();
50878         }
50879     }
50880 });/*
50881  * Based on:
50882  * Ext JS Library 1.1.1
50883  * Copyright(c) 2006-2007, Ext JS, LLC.
50884  *
50885  * Originally Released Under LGPL - original licence link has changed is not relivant.
50886  *
50887  * Fork - LGPL
50888  * <script type="text/javascript">
50889  */
50890 /**
50891  * @class Roo.BorderLayout
50892  * @extends Roo.LayoutManager
50893  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50894  * please see: <br><br>
50895  * <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>
50896  * <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>
50897  * Example:
50898  <pre><code>
50899  var layout = new Roo.BorderLayout(document.body, {
50900     north: {
50901         initialSize: 25,
50902         titlebar: false
50903     },
50904     west: {
50905         split:true,
50906         initialSize: 200,
50907         minSize: 175,
50908         maxSize: 400,
50909         titlebar: true,
50910         collapsible: true
50911     },
50912     east: {
50913         split:true,
50914         initialSize: 202,
50915         minSize: 175,
50916         maxSize: 400,
50917         titlebar: true,
50918         collapsible: true
50919     },
50920     south: {
50921         split:true,
50922         initialSize: 100,
50923         minSize: 100,
50924         maxSize: 200,
50925         titlebar: true,
50926         collapsible: true
50927     },
50928     center: {
50929         titlebar: true,
50930         autoScroll:true,
50931         resizeTabs: true,
50932         minTabWidth: 50,
50933         preferredTabWidth: 150
50934     }
50935 });
50936
50937 // shorthand
50938 var CP = Roo.ContentPanel;
50939
50940 layout.beginUpdate();
50941 layout.add("north", new CP("north", "North"));
50942 layout.add("south", new CP("south", {title: "South", closable: true}));
50943 layout.add("west", new CP("west", {title: "West"}));
50944 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50945 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50946 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50947 layout.getRegion("center").showPanel("center1");
50948 layout.endUpdate();
50949 </code></pre>
50950
50951 <b>The container the layout is rendered into can be either the body element or any other element.
50952 If it is not the body element, the container needs to either be an absolute positioned element,
50953 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50954 the container size if it is not the body element.</b>
50955
50956 * @constructor
50957 * Create a new BorderLayout
50958 * @param {String/HTMLElement/Element} container The container this layout is bound to
50959 * @param {Object} config Configuration options
50960  */
50961 Roo.BorderLayout = function(container, config){
50962     config = config || {};
50963     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50964     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50965     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50966         var target = this.factory.validRegions[i];
50967         if(config[target]){
50968             this.addRegion(target, config[target]);
50969         }
50970     }
50971 };
50972
50973 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50974     /**
50975      * Creates and adds a new region if it doesn't already exist.
50976      * @param {String} target The target region key (north, south, east, west or center).
50977      * @param {Object} config The regions config object
50978      * @return {BorderLayoutRegion} The new region
50979      */
50980     addRegion : function(target, config){
50981         if(!this.regions[target]){
50982             var r = this.factory.create(target, this, config);
50983             this.bindRegion(target, r);
50984         }
50985         return this.regions[target];
50986     },
50987
50988     // private (kinda)
50989     bindRegion : function(name, r){
50990         this.regions[name] = r;
50991         r.on("visibilitychange", this.layout, this);
50992         r.on("paneladded", this.layout, this);
50993         r.on("panelremoved", this.layout, this);
50994         r.on("invalidated", this.layout, this);
50995         r.on("resized", this.onRegionResized, this);
50996         r.on("collapsed", this.onRegionCollapsed, this);
50997         r.on("expanded", this.onRegionExpanded, this);
50998     },
50999
51000     /**
51001      * Performs a layout update.
51002      */
51003     layout : function(){
51004         if(this.updating) {
51005             return;
51006         }
51007         var size = this.getViewSize();
51008         var w = size.width;
51009         var h = size.height;
51010         var centerW = w;
51011         var centerH = h;
51012         var centerY = 0;
51013         var centerX = 0;
51014         //var x = 0, y = 0;
51015
51016         var rs = this.regions;
51017         var north = rs["north"];
51018         var south = rs["south"]; 
51019         var west = rs["west"];
51020         var east = rs["east"];
51021         var center = rs["center"];
51022         //if(this.hideOnLayout){ // not supported anymore
51023             //c.el.setStyle("display", "none");
51024         //}
51025         if(north && north.isVisible()){
51026             var b = north.getBox();
51027             var m = north.getMargins();
51028             b.width = w - (m.left+m.right);
51029             b.x = m.left;
51030             b.y = m.top;
51031             centerY = b.height + b.y + m.bottom;
51032             centerH -= centerY;
51033             north.updateBox(this.safeBox(b));
51034         }
51035         if(south && south.isVisible()){
51036             var b = south.getBox();
51037             var m = south.getMargins();
51038             b.width = w - (m.left+m.right);
51039             b.x = m.left;
51040             var totalHeight = (b.height + m.top + m.bottom);
51041             b.y = h - totalHeight + m.top;
51042             centerH -= totalHeight;
51043             south.updateBox(this.safeBox(b));
51044         }
51045         if(west && west.isVisible()){
51046             var b = west.getBox();
51047             var m = west.getMargins();
51048             b.height = centerH - (m.top+m.bottom);
51049             b.x = m.left;
51050             b.y = centerY + m.top;
51051             var totalWidth = (b.width + m.left + m.right);
51052             centerX += totalWidth;
51053             centerW -= totalWidth;
51054             west.updateBox(this.safeBox(b));
51055         }
51056         if(east && east.isVisible()){
51057             var b = east.getBox();
51058             var m = east.getMargins();
51059             b.height = centerH - (m.top+m.bottom);
51060             var totalWidth = (b.width + m.left + m.right);
51061             b.x = w - totalWidth + m.left;
51062             b.y = centerY + m.top;
51063             centerW -= totalWidth;
51064             east.updateBox(this.safeBox(b));
51065         }
51066         if(center){
51067             var m = center.getMargins();
51068             var centerBox = {
51069                 x: centerX + m.left,
51070                 y: centerY + m.top,
51071                 width: centerW - (m.left+m.right),
51072                 height: centerH - (m.top+m.bottom)
51073             };
51074             //if(this.hideOnLayout){
51075                 //center.el.setStyle("display", "block");
51076             //}
51077             center.updateBox(this.safeBox(centerBox));
51078         }
51079         this.el.repaint();
51080         this.fireEvent("layout", this);
51081     },
51082
51083     // private
51084     safeBox : function(box){
51085         box.width = Math.max(0, box.width);
51086         box.height = Math.max(0, box.height);
51087         return box;
51088     },
51089
51090     /**
51091      * Adds a ContentPanel (or subclass) to this layout.
51092      * @param {String} target The target region key (north, south, east, west or center).
51093      * @param {Roo.ContentPanel} panel The panel to add
51094      * @return {Roo.ContentPanel} The added panel
51095      */
51096     add : function(target, panel){
51097          
51098         target = target.toLowerCase();
51099         return this.regions[target].add(panel);
51100     },
51101
51102     /**
51103      * Remove a ContentPanel (or subclass) to this layout.
51104      * @param {String} target The target region key (north, south, east, west or center).
51105      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51106      * @return {Roo.ContentPanel} The removed panel
51107      */
51108     remove : function(target, panel){
51109         target = target.toLowerCase();
51110         return this.regions[target].remove(panel);
51111     },
51112
51113     /**
51114      * Searches all regions for a panel with the specified id
51115      * @param {String} panelId
51116      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51117      */
51118     findPanel : function(panelId){
51119         var rs = this.regions;
51120         for(var target in rs){
51121             if(typeof rs[target] != "function"){
51122                 var p = rs[target].getPanel(panelId);
51123                 if(p){
51124                     return p;
51125                 }
51126             }
51127         }
51128         return null;
51129     },
51130
51131     /**
51132      * Searches all regions for a panel with the specified id and activates (shows) it.
51133      * @param {String/ContentPanel} panelId The panels id or the panel itself
51134      * @return {Roo.ContentPanel} The shown panel or null
51135      */
51136     showPanel : function(panelId) {
51137       var rs = this.regions;
51138       for(var target in rs){
51139          var r = rs[target];
51140          if(typeof r != "function"){
51141             if(r.hasPanel(panelId)){
51142                return r.showPanel(panelId);
51143             }
51144          }
51145       }
51146       return null;
51147    },
51148
51149    /**
51150      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51151      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51152      */
51153     restoreState : function(provider){
51154         if(!provider){
51155             provider = Roo.state.Manager;
51156         }
51157         var sm = new Roo.LayoutStateManager();
51158         sm.init(this, provider);
51159     },
51160
51161     /**
51162      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51163      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51164      * a valid ContentPanel config object.  Example:
51165      * <pre><code>
51166 // Create the main layout
51167 var layout = new Roo.BorderLayout('main-ct', {
51168     west: {
51169         split:true,
51170         minSize: 175,
51171         titlebar: true
51172     },
51173     center: {
51174         title:'Components'
51175     }
51176 }, 'main-ct');
51177
51178 // Create and add multiple ContentPanels at once via configs
51179 layout.batchAdd({
51180    west: {
51181        id: 'source-files',
51182        autoCreate:true,
51183        title:'Ext Source Files',
51184        autoScroll:true,
51185        fitToFrame:true
51186    },
51187    center : {
51188        el: cview,
51189        autoScroll:true,
51190        fitToFrame:true,
51191        toolbar: tb,
51192        resizeEl:'cbody'
51193    }
51194 });
51195 </code></pre>
51196      * @param {Object} regions An object containing ContentPanel configs by region name
51197      */
51198     batchAdd : function(regions){
51199         this.beginUpdate();
51200         for(var rname in regions){
51201             var lr = this.regions[rname];
51202             if(lr){
51203                 this.addTypedPanels(lr, regions[rname]);
51204             }
51205         }
51206         this.endUpdate();
51207     },
51208
51209     // private
51210     addTypedPanels : function(lr, ps){
51211         if(typeof ps == 'string'){
51212             lr.add(new Roo.ContentPanel(ps));
51213         }
51214         else if(ps instanceof Array){
51215             for(var i =0, len = ps.length; i < len; i++){
51216                 this.addTypedPanels(lr, ps[i]);
51217             }
51218         }
51219         else if(!ps.events){ // raw config?
51220             var el = ps.el;
51221             delete ps.el; // prevent conflict
51222             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51223         }
51224         else {  // panel object assumed!
51225             lr.add(ps);
51226         }
51227     },
51228     /**
51229      * Adds a xtype elements to the layout.
51230      * <pre><code>
51231
51232 layout.addxtype({
51233        xtype : 'ContentPanel',
51234        region: 'west',
51235        items: [ .... ]
51236    }
51237 );
51238
51239 layout.addxtype({
51240         xtype : 'NestedLayoutPanel',
51241         region: 'west',
51242         layout: {
51243            center: { },
51244            west: { }   
51245         },
51246         items : [ ... list of content panels or nested layout panels.. ]
51247    }
51248 );
51249 </code></pre>
51250      * @param {Object} cfg Xtype definition of item to add.
51251      */
51252     addxtype : function(cfg)
51253     {
51254         // basically accepts a pannel...
51255         // can accept a layout region..!?!?
51256         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51257         
51258         if (!cfg.xtype.match(/Panel$/)) {
51259             return false;
51260         }
51261         var ret = false;
51262         
51263         if (typeof(cfg.region) == 'undefined') {
51264             Roo.log("Failed to add Panel, region was not set");
51265             Roo.log(cfg);
51266             return false;
51267         }
51268         var region = cfg.region;
51269         delete cfg.region;
51270         
51271           
51272         var xitems = [];
51273         if (cfg.items) {
51274             xitems = cfg.items;
51275             delete cfg.items;
51276         }
51277         var nb = false;
51278         
51279         switch(cfg.xtype) 
51280         {
51281             case 'ContentPanel':  // ContentPanel (el, cfg)
51282             case 'ScrollPanel':  // ContentPanel (el, cfg)
51283             case 'ViewPanel': 
51284                 if(cfg.autoCreate) {
51285                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51286                 } else {
51287                     var el = this.el.createChild();
51288                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51289                 }
51290                 
51291                 this.add(region, ret);
51292                 break;
51293             
51294             
51295             case 'TreePanel': // our new panel!
51296                 cfg.el = this.el.createChild();
51297                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51298                 this.add(region, ret);
51299                 break;
51300             
51301             case 'NestedLayoutPanel': 
51302                 // create a new Layout (which is  a Border Layout...
51303                 var el = this.el.createChild();
51304                 var clayout = cfg.layout;
51305                 delete cfg.layout;
51306                 clayout.items   = clayout.items  || [];
51307                 // replace this exitems with the clayout ones..
51308                 xitems = clayout.items;
51309                  
51310                 
51311                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51312                     cfg.background = false;
51313                 }
51314                 var layout = new Roo.BorderLayout(el, clayout);
51315                 
51316                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51317                 //console.log('adding nested layout panel '  + cfg.toSource());
51318                 this.add(region, ret);
51319                 nb = {}; /// find first...
51320                 break;
51321                 
51322             case 'GridPanel': 
51323             
51324                 // needs grid and region
51325                 
51326                 //var el = this.getRegion(region).el.createChild();
51327                 var el = this.el.createChild();
51328                 // create the grid first...
51329                 
51330                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51331                 delete cfg.grid;
51332                 if (region == 'center' && this.active ) {
51333                     cfg.background = false;
51334                 }
51335                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51336                 
51337                 this.add(region, ret);
51338                 if (cfg.background) {
51339                     ret.on('activate', function(gp) {
51340                         if (!gp.grid.rendered) {
51341                             gp.grid.render();
51342                         }
51343                     });
51344                 } else {
51345                     grid.render();
51346                 }
51347                 break;
51348            
51349            
51350            
51351                 
51352                 
51353                 
51354             default:
51355                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51356                     
51357                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51358                     this.add(region, ret);
51359                 } else {
51360                 
51361                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51362                     return null;
51363                 }
51364                 
51365              // GridPanel (grid, cfg)
51366             
51367         }
51368         this.beginUpdate();
51369         // add children..
51370         var region = '';
51371         var abn = {};
51372         Roo.each(xitems, function(i)  {
51373             region = nb && i.region ? i.region : false;
51374             
51375             var add = ret.addxtype(i);
51376            
51377             if (region) {
51378                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51379                 if (!i.background) {
51380                     abn[region] = nb[region] ;
51381                 }
51382             }
51383             
51384         });
51385         this.endUpdate();
51386
51387         // make the last non-background panel active..
51388         //if (nb) { Roo.log(abn); }
51389         if (nb) {
51390             
51391             for(var r in abn) {
51392                 region = this.getRegion(r);
51393                 if (region) {
51394                     // tried using nb[r], but it does not work..
51395                      
51396                     region.showPanel(abn[r]);
51397                    
51398                 }
51399             }
51400         }
51401         return ret;
51402         
51403     }
51404 });
51405
51406 /**
51407  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51408  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51409  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51410  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51411  * <pre><code>
51412 // shorthand
51413 var CP = Roo.ContentPanel;
51414
51415 var layout = Roo.BorderLayout.create({
51416     north: {
51417         initialSize: 25,
51418         titlebar: false,
51419         panels: [new CP("north", "North")]
51420     },
51421     west: {
51422         split:true,
51423         initialSize: 200,
51424         minSize: 175,
51425         maxSize: 400,
51426         titlebar: true,
51427         collapsible: true,
51428         panels: [new CP("west", {title: "West"})]
51429     },
51430     east: {
51431         split:true,
51432         initialSize: 202,
51433         minSize: 175,
51434         maxSize: 400,
51435         titlebar: true,
51436         collapsible: true,
51437         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51438     },
51439     south: {
51440         split:true,
51441         initialSize: 100,
51442         minSize: 100,
51443         maxSize: 200,
51444         titlebar: true,
51445         collapsible: true,
51446         panels: [new CP("south", {title: "South", closable: true})]
51447     },
51448     center: {
51449         titlebar: true,
51450         autoScroll:true,
51451         resizeTabs: true,
51452         minTabWidth: 50,
51453         preferredTabWidth: 150,
51454         panels: [
51455             new CP("center1", {title: "Close Me", closable: true}),
51456             new CP("center2", {title: "Center Panel", closable: false})
51457         ]
51458     }
51459 }, document.body);
51460
51461 layout.getRegion("center").showPanel("center1");
51462 </code></pre>
51463  * @param config
51464  * @param targetEl
51465  */
51466 Roo.BorderLayout.create = function(config, targetEl){
51467     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51468     layout.beginUpdate();
51469     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51470     for(var j = 0, jlen = regions.length; j < jlen; j++){
51471         var lr = regions[j];
51472         if(layout.regions[lr] && config[lr].panels){
51473             var r = layout.regions[lr];
51474             var ps = config[lr].panels;
51475             layout.addTypedPanels(r, ps);
51476         }
51477     }
51478     layout.endUpdate();
51479     return layout;
51480 };
51481
51482 // private
51483 Roo.BorderLayout.RegionFactory = {
51484     // private
51485     validRegions : ["north","south","east","west","center"],
51486
51487     // private
51488     create : function(target, mgr, config){
51489         target = target.toLowerCase();
51490         if(config.lightweight || config.basic){
51491             return new Roo.BasicLayoutRegion(mgr, config, target);
51492         }
51493         switch(target){
51494             case "north":
51495                 return new Roo.NorthLayoutRegion(mgr, config);
51496             case "south":
51497                 return new Roo.SouthLayoutRegion(mgr, config);
51498             case "east":
51499                 return new Roo.EastLayoutRegion(mgr, config);
51500             case "west":
51501                 return new Roo.WestLayoutRegion(mgr, config);
51502             case "center":
51503                 return new Roo.CenterLayoutRegion(mgr, config);
51504         }
51505         throw 'Layout region "'+target+'" not supported.';
51506     }
51507 };/*
51508  * Based on:
51509  * Ext JS Library 1.1.1
51510  * Copyright(c) 2006-2007, Ext JS, LLC.
51511  *
51512  * Originally Released Under LGPL - original licence link has changed is not relivant.
51513  *
51514  * Fork - LGPL
51515  * <script type="text/javascript">
51516  */
51517  
51518 /**
51519  * @class Roo.BasicLayoutRegion
51520  * @extends Roo.util.Observable
51521  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51522  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51523  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51524  */
51525 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51526     this.mgr = mgr;
51527     this.position  = pos;
51528     this.events = {
51529         /**
51530          * @scope Roo.BasicLayoutRegion
51531          */
51532         
51533         /**
51534          * @event beforeremove
51535          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51536          * @param {Roo.LayoutRegion} this
51537          * @param {Roo.ContentPanel} panel The panel
51538          * @param {Object} e The cancel event object
51539          */
51540         "beforeremove" : true,
51541         /**
51542          * @event invalidated
51543          * Fires when the layout for this region is changed.
51544          * @param {Roo.LayoutRegion} this
51545          */
51546         "invalidated" : true,
51547         /**
51548          * @event visibilitychange
51549          * Fires when this region is shown or hidden 
51550          * @param {Roo.LayoutRegion} this
51551          * @param {Boolean} visibility true or false
51552          */
51553         "visibilitychange" : true,
51554         /**
51555          * @event paneladded
51556          * Fires when a panel is added. 
51557          * @param {Roo.LayoutRegion} this
51558          * @param {Roo.ContentPanel} panel The panel
51559          */
51560         "paneladded" : true,
51561         /**
51562          * @event panelremoved
51563          * Fires when a panel is removed. 
51564          * @param {Roo.LayoutRegion} this
51565          * @param {Roo.ContentPanel} panel The panel
51566          */
51567         "panelremoved" : true,
51568         /**
51569          * @event beforecollapse
51570          * Fires when this region before collapse.
51571          * @param {Roo.LayoutRegion} this
51572          */
51573         "beforecollapse" : true,
51574         /**
51575          * @event collapsed
51576          * Fires when this region is collapsed.
51577          * @param {Roo.LayoutRegion} this
51578          */
51579         "collapsed" : true,
51580         /**
51581          * @event expanded
51582          * Fires when this region is expanded.
51583          * @param {Roo.LayoutRegion} this
51584          */
51585         "expanded" : true,
51586         /**
51587          * @event slideshow
51588          * Fires when this region is slid into view.
51589          * @param {Roo.LayoutRegion} this
51590          */
51591         "slideshow" : true,
51592         /**
51593          * @event slidehide
51594          * Fires when this region slides out of view. 
51595          * @param {Roo.LayoutRegion} this
51596          */
51597         "slidehide" : true,
51598         /**
51599          * @event panelactivated
51600          * Fires when a panel is activated. 
51601          * @param {Roo.LayoutRegion} this
51602          * @param {Roo.ContentPanel} panel The activated panel
51603          */
51604         "panelactivated" : true,
51605         /**
51606          * @event resized
51607          * Fires when the user resizes this region. 
51608          * @param {Roo.LayoutRegion} this
51609          * @param {Number} newSize The new size (width for east/west, height for north/south)
51610          */
51611         "resized" : true
51612     };
51613     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51614     this.panels = new Roo.util.MixedCollection();
51615     this.panels.getKey = this.getPanelId.createDelegate(this);
51616     this.box = null;
51617     this.activePanel = null;
51618     // ensure listeners are added...
51619     
51620     if (config.listeners || config.events) {
51621         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51622             listeners : config.listeners || {},
51623             events : config.events || {}
51624         });
51625     }
51626     
51627     if(skipConfig !== true){
51628         this.applyConfig(config);
51629     }
51630 };
51631
51632 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51633     getPanelId : function(p){
51634         return p.getId();
51635     },
51636     
51637     applyConfig : function(config){
51638         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51639         this.config = config;
51640         
51641     },
51642     
51643     /**
51644      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51645      * the width, for horizontal (north, south) the height.
51646      * @param {Number} newSize The new width or height
51647      */
51648     resizeTo : function(newSize){
51649         var el = this.el ? this.el :
51650                  (this.activePanel ? this.activePanel.getEl() : null);
51651         if(el){
51652             switch(this.position){
51653                 case "east":
51654                 case "west":
51655                     el.setWidth(newSize);
51656                     this.fireEvent("resized", this, newSize);
51657                 break;
51658                 case "north":
51659                 case "south":
51660                     el.setHeight(newSize);
51661                     this.fireEvent("resized", this, newSize);
51662                 break;                
51663             }
51664         }
51665     },
51666     
51667     getBox : function(){
51668         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51669     },
51670     
51671     getMargins : function(){
51672         return this.margins;
51673     },
51674     
51675     updateBox : function(box){
51676         this.box = box;
51677         var el = this.activePanel.getEl();
51678         el.dom.style.left = box.x + "px";
51679         el.dom.style.top = box.y + "px";
51680         this.activePanel.setSize(box.width, box.height);
51681     },
51682     
51683     /**
51684      * Returns the container element for this region.
51685      * @return {Roo.Element}
51686      */
51687     getEl : function(){
51688         return this.activePanel;
51689     },
51690     
51691     /**
51692      * Returns true if this region is currently visible.
51693      * @return {Boolean}
51694      */
51695     isVisible : function(){
51696         return this.activePanel ? true : false;
51697     },
51698     
51699     setActivePanel : function(panel){
51700         panel = this.getPanel(panel);
51701         if(this.activePanel && this.activePanel != panel){
51702             this.activePanel.setActiveState(false);
51703             this.activePanel.getEl().setLeftTop(-10000,-10000);
51704         }
51705         this.activePanel = panel;
51706         panel.setActiveState(true);
51707         if(this.box){
51708             panel.setSize(this.box.width, this.box.height);
51709         }
51710         this.fireEvent("panelactivated", this, panel);
51711         this.fireEvent("invalidated");
51712     },
51713     
51714     /**
51715      * Show the specified panel.
51716      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51717      * @return {Roo.ContentPanel} The shown panel or null
51718      */
51719     showPanel : function(panel){
51720         if(panel = this.getPanel(panel)){
51721             this.setActivePanel(panel);
51722         }
51723         return panel;
51724     },
51725     
51726     /**
51727      * Get the active panel for this region.
51728      * @return {Roo.ContentPanel} The active panel or null
51729      */
51730     getActivePanel : function(){
51731         return this.activePanel;
51732     },
51733     
51734     /**
51735      * Add the passed ContentPanel(s)
51736      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51737      * @return {Roo.ContentPanel} The panel added (if only one was added)
51738      */
51739     add : function(panel){
51740         if(arguments.length > 1){
51741             for(var i = 0, len = arguments.length; i < len; i++) {
51742                 this.add(arguments[i]);
51743             }
51744             return null;
51745         }
51746         if(this.hasPanel(panel)){
51747             this.showPanel(panel);
51748             return panel;
51749         }
51750         var el = panel.getEl();
51751         if(el.dom.parentNode != this.mgr.el.dom){
51752             this.mgr.el.dom.appendChild(el.dom);
51753         }
51754         if(panel.setRegion){
51755             panel.setRegion(this);
51756         }
51757         this.panels.add(panel);
51758         el.setStyle("position", "absolute");
51759         if(!panel.background){
51760             this.setActivePanel(panel);
51761             if(this.config.initialSize && this.panels.getCount()==1){
51762                 this.resizeTo(this.config.initialSize);
51763             }
51764         }
51765         this.fireEvent("paneladded", this, panel);
51766         return panel;
51767     },
51768     
51769     /**
51770      * Returns true if the panel is in this region.
51771      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51772      * @return {Boolean}
51773      */
51774     hasPanel : function(panel){
51775         if(typeof panel == "object"){ // must be panel obj
51776             panel = panel.getId();
51777         }
51778         return this.getPanel(panel) ? true : false;
51779     },
51780     
51781     /**
51782      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51783      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51784      * @param {Boolean} preservePanel Overrides the config preservePanel option
51785      * @return {Roo.ContentPanel} The panel that was removed
51786      */
51787     remove : function(panel, preservePanel){
51788         panel = this.getPanel(panel);
51789         if(!panel){
51790             return null;
51791         }
51792         var e = {};
51793         this.fireEvent("beforeremove", this, panel, e);
51794         if(e.cancel === true){
51795             return null;
51796         }
51797         var panelId = panel.getId();
51798         this.panels.removeKey(panelId);
51799         return panel;
51800     },
51801     
51802     /**
51803      * Returns the panel specified or null if it's not in this region.
51804      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51805      * @return {Roo.ContentPanel}
51806      */
51807     getPanel : function(id){
51808         if(typeof id == "object"){ // must be panel obj
51809             return id;
51810         }
51811         return this.panels.get(id);
51812     },
51813     
51814     /**
51815      * Returns this regions position (north/south/east/west/center).
51816      * @return {String} 
51817      */
51818     getPosition: function(){
51819         return this.position;    
51820     }
51821 });/*
51822  * Based on:
51823  * Ext JS Library 1.1.1
51824  * Copyright(c) 2006-2007, Ext JS, LLC.
51825  *
51826  * Originally Released Under LGPL - original licence link has changed is not relivant.
51827  *
51828  * Fork - LGPL
51829  * <script type="text/javascript">
51830  */
51831  
51832 /**
51833  * @class Roo.LayoutRegion
51834  * @extends Roo.BasicLayoutRegion
51835  * This class represents a region in a layout manager.
51836  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51837  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51838  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51839  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51840  * @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})
51841  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51842  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51843  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51844  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51845  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51846  * @cfg {String}    title           The title for the region (overrides panel titles)
51847  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51848  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51849  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51850  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51851  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51852  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51853  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51854  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51855  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51856  * @cfg {Boolean}   showPin         True to show a pin button
51857  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51858  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51859  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51860  * @cfg {Number}    width           For East/West panels
51861  * @cfg {Number}    height          For North/South panels
51862  * @cfg {Boolean}   split           To show the splitter
51863  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51864  */
51865 Roo.LayoutRegion = function(mgr, config, pos){
51866     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51867     var dh = Roo.DomHelper;
51868     /** This region's container element 
51869     * @type Roo.Element */
51870     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51871     /** This region's title element 
51872     * @type Roo.Element */
51873
51874     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51875         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51876         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51877     ]}, true);
51878     this.titleEl.enableDisplayMode();
51879     /** This region's title text element 
51880     * @type HTMLElement */
51881     this.titleTextEl = this.titleEl.dom.firstChild;
51882     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51883     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51884     this.closeBtn.enableDisplayMode();
51885     this.closeBtn.on("click", this.closeClicked, this);
51886     this.closeBtn.hide();
51887
51888     this.createBody(config);
51889     this.visible = true;
51890     this.collapsed = false;
51891
51892     if(config.hideWhenEmpty){
51893         this.hide();
51894         this.on("paneladded", this.validateVisibility, this);
51895         this.on("panelremoved", this.validateVisibility, this);
51896     }
51897     this.applyConfig(config);
51898 };
51899
51900 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51901
51902     createBody : function(){
51903         /** This region's body element 
51904         * @type Roo.Element */
51905         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51906     },
51907
51908     applyConfig : function(c){
51909         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51910             var dh = Roo.DomHelper;
51911             if(c.titlebar !== false){
51912                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51913                 this.collapseBtn.on("click", this.collapse, this);
51914                 this.collapseBtn.enableDisplayMode();
51915
51916                 if(c.showPin === true || this.showPin){
51917                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51918                     this.stickBtn.enableDisplayMode();
51919                     this.stickBtn.on("click", this.expand, this);
51920                     this.stickBtn.hide();
51921                 }
51922             }
51923             /** This region's collapsed element
51924             * @type Roo.Element */
51925             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51926                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51927             ]}, true);
51928             if(c.floatable !== false){
51929                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51930                this.collapsedEl.on("click", this.collapseClick, this);
51931             }
51932
51933             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51934                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51935                    id: "message", unselectable: "on", style:{"float":"left"}});
51936                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51937              }
51938             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51939             this.expandBtn.on("click", this.expand, this);
51940         }
51941         if(this.collapseBtn){
51942             this.collapseBtn.setVisible(c.collapsible == true);
51943         }
51944         this.cmargins = c.cmargins || this.cmargins ||
51945                          (this.position == "west" || this.position == "east" ?
51946                              {top: 0, left: 2, right:2, bottom: 0} :
51947                              {top: 2, left: 0, right:0, bottom: 2});
51948         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51949         this.bottomTabs = c.tabPosition != "top";
51950         this.autoScroll = c.autoScroll || false;
51951         if(this.autoScroll){
51952             this.bodyEl.setStyle("overflow", "auto");
51953         }else{
51954             this.bodyEl.setStyle("overflow", "hidden");
51955         }
51956         //if(c.titlebar !== false){
51957             if((!c.titlebar && !c.title) || c.titlebar === false){
51958                 this.titleEl.hide();
51959             }else{
51960                 this.titleEl.show();
51961                 if(c.title){
51962                     this.titleTextEl.innerHTML = c.title;
51963                 }
51964             }
51965         //}
51966         this.duration = c.duration || .30;
51967         this.slideDuration = c.slideDuration || .45;
51968         this.config = c;
51969         if(c.collapsed){
51970             this.collapse(true);
51971         }
51972         if(c.hidden){
51973             this.hide();
51974         }
51975     },
51976     /**
51977      * Returns true if this region is currently visible.
51978      * @return {Boolean}
51979      */
51980     isVisible : function(){
51981         return this.visible;
51982     },
51983
51984     /**
51985      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51986      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51987      */
51988     setCollapsedTitle : function(title){
51989         title = title || "&#160;";
51990         if(this.collapsedTitleTextEl){
51991             this.collapsedTitleTextEl.innerHTML = title;
51992         }
51993     },
51994
51995     getBox : function(){
51996         var b;
51997         if(!this.collapsed){
51998             b = this.el.getBox(false, true);
51999         }else{
52000             b = this.collapsedEl.getBox(false, true);
52001         }
52002         return b;
52003     },
52004
52005     getMargins : function(){
52006         return this.collapsed ? this.cmargins : this.margins;
52007     },
52008
52009     highlight : function(){
52010         this.el.addClass("x-layout-panel-dragover");
52011     },
52012
52013     unhighlight : function(){
52014         this.el.removeClass("x-layout-panel-dragover");
52015     },
52016
52017     updateBox : function(box){
52018         this.box = box;
52019         if(!this.collapsed){
52020             this.el.dom.style.left = box.x + "px";
52021             this.el.dom.style.top = box.y + "px";
52022             this.updateBody(box.width, box.height);
52023         }else{
52024             this.collapsedEl.dom.style.left = box.x + "px";
52025             this.collapsedEl.dom.style.top = box.y + "px";
52026             this.collapsedEl.setSize(box.width, box.height);
52027         }
52028         if(this.tabs){
52029             this.tabs.autoSizeTabs();
52030         }
52031     },
52032
52033     updateBody : function(w, h){
52034         if(w !== null){
52035             this.el.setWidth(w);
52036             w -= this.el.getBorderWidth("rl");
52037             if(this.config.adjustments){
52038                 w += this.config.adjustments[0];
52039             }
52040         }
52041         if(h !== null){
52042             this.el.setHeight(h);
52043             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52044             h -= this.el.getBorderWidth("tb");
52045             if(this.config.adjustments){
52046                 h += this.config.adjustments[1];
52047             }
52048             this.bodyEl.setHeight(h);
52049             if(this.tabs){
52050                 h = this.tabs.syncHeight(h);
52051             }
52052         }
52053         if(this.panelSize){
52054             w = w !== null ? w : this.panelSize.width;
52055             h = h !== null ? h : this.panelSize.height;
52056         }
52057         if(this.activePanel){
52058             var el = this.activePanel.getEl();
52059             w = w !== null ? w : el.getWidth();
52060             h = h !== null ? h : el.getHeight();
52061             this.panelSize = {width: w, height: h};
52062             this.activePanel.setSize(w, h);
52063         }
52064         if(Roo.isIE && this.tabs){
52065             this.tabs.el.repaint();
52066         }
52067     },
52068
52069     /**
52070      * Returns the container element for this region.
52071      * @return {Roo.Element}
52072      */
52073     getEl : function(){
52074         return this.el;
52075     },
52076
52077     /**
52078      * Hides this region.
52079      */
52080     hide : function(){
52081         if(!this.collapsed){
52082             this.el.dom.style.left = "-2000px";
52083             this.el.hide();
52084         }else{
52085             this.collapsedEl.dom.style.left = "-2000px";
52086             this.collapsedEl.hide();
52087         }
52088         this.visible = false;
52089         this.fireEvent("visibilitychange", this, false);
52090     },
52091
52092     /**
52093      * Shows this region if it was previously hidden.
52094      */
52095     show : function(){
52096         if(!this.collapsed){
52097             this.el.show();
52098         }else{
52099             this.collapsedEl.show();
52100         }
52101         this.visible = true;
52102         this.fireEvent("visibilitychange", this, true);
52103     },
52104
52105     closeClicked : function(){
52106         if(this.activePanel){
52107             this.remove(this.activePanel);
52108         }
52109     },
52110
52111     collapseClick : function(e){
52112         if(this.isSlid){
52113            e.stopPropagation();
52114            this.slideIn();
52115         }else{
52116            e.stopPropagation();
52117            this.slideOut();
52118         }
52119     },
52120
52121     /**
52122      * Collapses this region.
52123      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52124      */
52125     collapse : function(skipAnim, skipCheck = false){
52126         if(this.collapsed) {
52127             return;
52128         }
52129         
52130         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52131             
52132             this.collapsed = true;
52133             if(this.split){
52134                 this.split.el.hide();
52135             }
52136             if(this.config.animate && skipAnim !== true){
52137                 this.fireEvent("invalidated", this);
52138                 this.animateCollapse();
52139             }else{
52140                 this.el.setLocation(-20000,-20000);
52141                 this.el.hide();
52142                 this.collapsedEl.show();
52143                 this.fireEvent("collapsed", this);
52144                 this.fireEvent("invalidated", this);
52145             }
52146         }
52147         
52148     },
52149
52150     animateCollapse : function(){
52151         // overridden
52152     },
52153
52154     /**
52155      * Expands this region if it was previously collapsed.
52156      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52157      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52158      */
52159     expand : function(e, skipAnim){
52160         if(e) {
52161             e.stopPropagation();
52162         }
52163         if(!this.collapsed || this.el.hasActiveFx()) {
52164             return;
52165         }
52166         if(this.isSlid){
52167             this.afterSlideIn();
52168             skipAnim = true;
52169         }
52170         this.collapsed = false;
52171         if(this.config.animate && skipAnim !== true){
52172             this.animateExpand();
52173         }else{
52174             this.el.show();
52175             if(this.split){
52176                 this.split.el.show();
52177             }
52178             this.collapsedEl.setLocation(-2000,-2000);
52179             this.collapsedEl.hide();
52180             this.fireEvent("invalidated", this);
52181             this.fireEvent("expanded", this);
52182         }
52183     },
52184
52185     animateExpand : function(){
52186         // overridden
52187     },
52188
52189     initTabs : function()
52190     {
52191         this.bodyEl.setStyle("overflow", "hidden");
52192         var ts = new Roo.TabPanel(
52193                 this.bodyEl.dom,
52194                 {
52195                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52196                     disableTooltips: this.config.disableTabTips,
52197                     toolbar : this.config.toolbar
52198                 }
52199         );
52200         if(this.config.hideTabs){
52201             ts.stripWrap.setDisplayed(false);
52202         }
52203         this.tabs = ts;
52204         ts.resizeTabs = this.config.resizeTabs === true;
52205         ts.minTabWidth = this.config.minTabWidth || 40;
52206         ts.maxTabWidth = this.config.maxTabWidth || 250;
52207         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52208         ts.monitorResize = false;
52209         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52210         ts.bodyEl.addClass('x-layout-tabs-body');
52211         this.panels.each(this.initPanelAsTab, this);
52212     },
52213
52214     initPanelAsTab : function(panel){
52215         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52216                     this.config.closeOnTab && panel.isClosable());
52217         if(panel.tabTip !== undefined){
52218             ti.setTooltip(panel.tabTip);
52219         }
52220         ti.on("activate", function(){
52221               this.setActivePanel(panel);
52222         }, this);
52223         if(this.config.closeOnTab){
52224             ti.on("beforeclose", function(t, e){
52225                 e.cancel = true;
52226                 this.remove(panel);
52227             }, this);
52228         }
52229         return ti;
52230     },
52231
52232     updatePanelTitle : function(panel, title){
52233         if(this.activePanel == panel){
52234             this.updateTitle(title);
52235         }
52236         if(this.tabs){
52237             var ti = this.tabs.getTab(panel.getEl().id);
52238             ti.setText(title);
52239             if(panel.tabTip !== undefined){
52240                 ti.setTooltip(panel.tabTip);
52241             }
52242         }
52243     },
52244
52245     updateTitle : function(title){
52246         if(this.titleTextEl && !this.config.title){
52247             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52248         }
52249     },
52250
52251     setActivePanel : function(panel){
52252         panel = this.getPanel(panel);
52253         if(this.activePanel && this.activePanel != panel){
52254             this.activePanel.setActiveState(false);
52255         }
52256         this.activePanel = panel;
52257         panel.setActiveState(true);
52258         if(this.panelSize){
52259             panel.setSize(this.panelSize.width, this.panelSize.height);
52260         }
52261         if(this.closeBtn){
52262             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52263         }
52264         this.updateTitle(panel.getTitle());
52265         if(this.tabs){
52266             this.fireEvent("invalidated", this);
52267         }
52268         this.fireEvent("panelactivated", this, panel);
52269     },
52270
52271     /**
52272      * Shows the specified panel.
52273      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52274      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52275      */
52276     showPanel : function(panel)
52277     {
52278         panel = this.getPanel(panel);
52279         if(panel){
52280             if(this.tabs){
52281                 var tab = this.tabs.getTab(panel.getEl().id);
52282                 if(tab.isHidden()){
52283                     this.tabs.unhideTab(tab.id);
52284                 }
52285                 tab.activate();
52286             }else{
52287                 this.setActivePanel(panel);
52288             }
52289         }
52290         return panel;
52291     },
52292
52293     /**
52294      * Get the active panel for this region.
52295      * @return {Roo.ContentPanel} The active panel or null
52296      */
52297     getActivePanel : function(){
52298         return this.activePanel;
52299     },
52300
52301     validateVisibility : function(){
52302         if(this.panels.getCount() < 1){
52303             this.updateTitle("&#160;");
52304             this.closeBtn.hide();
52305             this.hide();
52306         }else{
52307             if(!this.isVisible()){
52308                 this.show();
52309             }
52310         }
52311     },
52312
52313     /**
52314      * Adds the passed ContentPanel(s) to this region.
52315      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52316      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52317      */
52318     add : function(panel){
52319         if(arguments.length > 1){
52320             for(var i = 0, len = arguments.length; i < len; i++) {
52321                 this.add(arguments[i]);
52322             }
52323             return null;
52324         }
52325         if(this.hasPanel(panel)){
52326             this.showPanel(panel);
52327             return panel;
52328         }
52329         panel.setRegion(this);
52330         this.panels.add(panel);
52331         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52332             this.bodyEl.dom.appendChild(panel.getEl().dom);
52333             if(panel.background !== true){
52334                 this.setActivePanel(panel);
52335             }
52336             this.fireEvent("paneladded", this, panel);
52337             return panel;
52338         }
52339         if(!this.tabs){
52340             this.initTabs();
52341         }else{
52342             this.initPanelAsTab(panel);
52343         }
52344         if(panel.background !== true){
52345             this.tabs.activate(panel.getEl().id);
52346         }
52347         this.fireEvent("paneladded", this, panel);
52348         return panel;
52349     },
52350
52351     /**
52352      * Hides the tab for the specified panel.
52353      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52354      */
52355     hidePanel : function(panel){
52356         if(this.tabs && (panel = this.getPanel(panel))){
52357             this.tabs.hideTab(panel.getEl().id);
52358         }
52359     },
52360
52361     /**
52362      * Unhides the tab for a previously hidden panel.
52363      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52364      */
52365     unhidePanel : function(panel){
52366         if(this.tabs && (panel = this.getPanel(panel))){
52367             this.tabs.unhideTab(panel.getEl().id);
52368         }
52369     },
52370
52371     clearPanels : function(){
52372         while(this.panels.getCount() > 0){
52373              this.remove(this.panels.first());
52374         }
52375     },
52376
52377     /**
52378      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52379      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52380      * @param {Boolean} preservePanel Overrides the config preservePanel option
52381      * @return {Roo.ContentPanel} The panel that was removed
52382      */
52383     remove : function(panel, preservePanel){
52384         panel = this.getPanel(panel);
52385         if(!panel){
52386             return null;
52387         }
52388         var e = {};
52389         this.fireEvent("beforeremove", this, panel, e);
52390         if(e.cancel === true){
52391             return null;
52392         }
52393         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52394         var panelId = panel.getId();
52395         this.panels.removeKey(panelId);
52396         if(preservePanel){
52397             document.body.appendChild(panel.getEl().dom);
52398         }
52399         if(this.tabs){
52400             this.tabs.removeTab(panel.getEl().id);
52401         }else if (!preservePanel){
52402             this.bodyEl.dom.removeChild(panel.getEl().dom);
52403         }
52404         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52405             var p = this.panels.first();
52406             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52407             tempEl.appendChild(p.getEl().dom);
52408             this.bodyEl.update("");
52409             this.bodyEl.dom.appendChild(p.getEl().dom);
52410             tempEl = null;
52411             this.updateTitle(p.getTitle());
52412             this.tabs = null;
52413             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52414             this.setActivePanel(p);
52415         }
52416         panel.setRegion(null);
52417         if(this.activePanel == panel){
52418             this.activePanel = null;
52419         }
52420         if(this.config.autoDestroy !== false && preservePanel !== true){
52421             try{panel.destroy();}catch(e){}
52422         }
52423         this.fireEvent("panelremoved", this, panel);
52424         return panel;
52425     },
52426
52427     /**
52428      * Returns the TabPanel component used by this region
52429      * @return {Roo.TabPanel}
52430      */
52431     getTabs : function(){
52432         return this.tabs;
52433     },
52434
52435     createTool : function(parentEl, className){
52436         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52437             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52438         btn.addClassOnOver("x-layout-tools-button-over");
52439         return btn;
52440     }
52441 });/*
52442  * Based on:
52443  * Ext JS Library 1.1.1
52444  * Copyright(c) 2006-2007, Ext JS, LLC.
52445  *
52446  * Originally Released Under LGPL - original licence link has changed is not relivant.
52447  *
52448  * Fork - LGPL
52449  * <script type="text/javascript">
52450  */
52451  
52452
52453
52454 /**
52455  * @class Roo.SplitLayoutRegion
52456  * @extends Roo.LayoutRegion
52457  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52458  */
52459 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52460     this.cursor = cursor;
52461     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52462 };
52463
52464 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52465     splitTip : "Drag to resize.",
52466     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52467     useSplitTips : false,
52468
52469     applyConfig : function(config){
52470         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52471         if(config.split){
52472             if(!this.split){
52473                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52474                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52475                 /** The SplitBar for this region 
52476                 * @type Roo.SplitBar */
52477                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52478                 this.split.on("moved", this.onSplitMove, this);
52479                 this.split.useShim = config.useShim === true;
52480                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52481                 if(this.useSplitTips){
52482                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52483                 }
52484                 if(config.collapsible){
52485                     this.split.el.on("dblclick", this.collapse,  this);
52486                 }
52487             }
52488             if(typeof config.minSize != "undefined"){
52489                 this.split.minSize = config.minSize;
52490             }
52491             if(typeof config.maxSize != "undefined"){
52492                 this.split.maxSize = config.maxSize;
52493             }
52494             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52495                 this.hideSplitter();
52496             }
52497         }
52498     },
52499
52500     getHMaxSize : function(){
52501          var cmax = this.config.maxSize || 10000;
52502          var center = this.mgr.getRegion("center");
52503          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52504     },
52505
52506     getVMaxSize : function(){
52507          var cmax = this.config.maxSize || 10000;
52508          var center = this.mgr.getRegion("center");
52509          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52510     },
52511
52512     onSplitMove : function(split, newSize){
52513         this.fireEvent("resized", this, newSize);
52514     },
52515     
52516     /** 
52517      * Returns the {@link Roo.SplitBar} for this region.
52518      * @return {Roo.SplitBar}
52519      */
52520     getSplitBar : function(){
52521         return this.split;
52522     },
52523     
52524     hide : function(){
52525         this.hideSplitter();
52526         Roo.SplitLayoutRegion.superclass.hide.call(this);
52527     },
52528
52529     hideSplitter : function(){
52530         if(this.split){
52531             this.split.el.setLocation(-2000,-2000);
52532             this.split.el.hide();
52533         }
52534     },
52535
52536     show : function(){
52537         if(this.split){
52538             this.split.el.show();
52539         }
52540         Roo.SplitLayoutRegion.superclass.show.call(this);
52541     },
52542     
52543     beforeSlide: function(){
52544         if(Roo.isGecko){// firefox overflow auto bug workaround
52545             this.bodyEl.clip();
52546             if(this.tabs) {
52547                 this.tabs.bodyEl.clip();
52548             }
52549             if(this.activePanel){
52550                 this.activePanel.getEl().clip();
52551                 
52552                 if(this.activePanel.beforeSlide){
52553                     this.activePanel.beforeSlide();
52554                 }
52555             }
52556         }
52557     },
52558     
52559     afterSlide : function(){
52560         if(Roo.isGecko){// firefox overflow auto bug workaround
52561             this.bodyEl.unclip();
52562             if(this.tabs) {
52563                 this.tabs.bodyEl.unclip();
52564             }
52565             if(this.activePanel){
52566                 this.activePanel.getEl().unclip();
52567                 if(this.activePanel.afterSlide){
52568                     this.activePanel.afterSlide();
52569                 }
52570             }
52571         }
52572     },
52573
52574     initAutoHide : function(){
52575         if(this.autoHide !== false){
52576             if(!this.autoHideHd){
52577                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52578                 this.autoHideHd = {
52579                     "mouseout": function(e){
52580                         if(!e.within(this.el, true)){
52581                             st.delay(500);
52582                         }
52583                     },
52584                     "mouseover" : function(e){
52585                         st.cancel();
52586                     },
52587                     scope : this
52588                 };
52589             }
52590             this.el.on(this.autoHideHd);
52591         }
52592     },
52593
52594     clearAutoHide : function(){
52595         if(this.autoHide !== false){
52596             this.el.un("mouseout", this.autoHideHd.mouseout);
52597             this.el.un("mouseover", this.autoHideHd.mouseover);
52598         }
52599     },
52600
52601     clearMonitor : function(){
52602         Roo.get(document).un("click", this.slideInIf, this);
52603     },
52604
52605     // these names are backwards but not changed for compat
52606     slideOut : function(){
52607         if(this.isSlid || this.el.hasActiveFx()){
52608             return;
52609         }
52610         this.isSlid = true;
52611         if(this.collapseBtn){
52612             this.collapseBtn.hide();
52613         }
52614         this.closeBtnState = this.closeBtn.getStyle('display');
52615         this.closeBtn.hide();
52616         if(this.stickBtn){
52617             this.stickBtn.show();
52618         }
52619         this.el.show();
52620         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52621         this.beforeSlide();
52622         this.el.setStyle("z-index", 10001);
52623         this.el.slideIn(this.getSlideAnchor(), {
52624             callback: function(){
52625                 this.afterSlide();
52626                 this.initAutoHide();
52627                 Roo.get(document).on("click", this.slideInIf, this);
52628                 this.fireEvent("slideshow", this);
52629             },
52630             scope: this,
52631             block: true
52632         });
52633     },
52634
52635     afterSlideIn : function(){
52636         this.clearAutoHide();
52637         this.isSlid = false;
52638         this.clearMonitor();
52639         this.el.setStyle("z-index", "");
52640         if(this.collapseBtn){
52641             this.collapseBtn.show();
52642         }
52643         this.closeBtn.setStyle('display', this.closeBtnState);
52644         if(this.stickBtn){
52645             this.stickBtn.hide();
52646         }
52647         this.fireEvent("slidehide", this);
52648     },
52649
52650     slideIn : function(cb){
52651         if(!this.isSlid || this.el.hasActiveFx()){
52652             Roo.callback(cb);
52653             return;
52654         }
52655         this.isSlid = false;
52656         this.beforeSlide();
52657         this.el.slideOut(this.getSlideAnchor(), {
52658             callback: function(){
52659                 this.el.setLeftTop(-10000, -10000);
52660                 this.afterSlide();
52661                 this.afterSlideIn();
52662                 Roo.callback(cb);
52663             },
52664             scope: this,
52665             block: true
52666         });
52667     },
52668     
52669     slideInIf : function(e){
52670         if(!e.within(this.el)){
52671             this.slideIn();
52672         }
52673     },
52674
52675     animateCollapse : function(){
52676         this.beforeSlide();
52677         this.el.setStyle("z-index", 20000);
52678         var anchor = this.getSlideAnchor();
52679         this.el.slideOut(anchor, {
52680             callback : function(){
52681                 this.el.setStyle("z-index", "");
52682                 this.collapsedEl.slideIn(anchor, {duration:.3});
52683                 this.afterSlide();
52684                 this.el.setLocation(-10000,-10000);
52685                 this.el.hide();
52686                 this.fireEvent("collapsed", this);
52687             },
52688             scope: this,
52689             block: true
52690         });
52691     },
52692
52693     animateExpand : function(){
52694         this.beforeSlide();
52695         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52696         this.el.setStyle("z-index", 20000);
52697         this.collapsedEl.hide({
52698             duration:.1
52699         });
52700         this.el.slideIn(this.getSlideAnchor(), {
52701             callback : function(){
52702                 this.el.setStyle("z-index", "");
52703                 this.afterSlide();
52704                 if(this.split){
52705                     this.split.el.show();
52706                 }
52707                 this.fireEvent("invalidated", this);
52708                 this.fireEvent("expanded", this);
52709             },
52710             scope: this,
52711             block: true
52712         });
52713     },
52714
52715     anchors : {
52716         "west" : "left",
52717         "east" : "right",
52718         "north" : "top",
52719         "south" : "bottom"
52720     },
52721
52722     sanchors : {
52723         "west" : "l",
52724         "east" : "r",
52725         "north" : "t",
52726         "south" : "b"
52727     },
52728
52729     canchors : {
52730         "west" : "tl-tr",
52731         "east" : "tr-tl",
52732         "north" : "tl-bl",
52733         "south" : "bl-tl"
52734     },
52735
52736     getAnchor : function(){
52737         return this.anchors[this.position];
52738     },
52739
52740     getCollapseAnchor : function(){
52741         return this.canchors[this.position];
52742     },
52743
52744     getSlideAnchor : function(){
52745         return this.sanchors[this.position];
52746     },
52747
52748     getAlignAdj : function(){
52749         var cm = this.cmargins;
52750         switch(this.position){
52751             case "west":
52752                 return [0, 0];
52753             break;
52754             case "east":
52755                 return [0, 0];
52756             break;
52757             case "north":
52758                 return [0, 0];
52759             break;
52760             case "south":
52761                 return [0, 0];
52762             break;
52763         }
52764     },
52765
52766     getExpandAdj : function(){
52767         var c = this.collapsedEl, cm = this.cmargins;
52768         switch(this.position){
52769             case "west":
52770                 return [-(cm.right+c.getWidth()+cm.left), 0];
52771             break;
52772             case "east":
52773                 return [cm.right+c.getWidth()+cm.left, 0];
52774             break;
52775             case "north":
52776                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52777             break;
52778             case "south":
52779                 return [0, cm.top+cm.bottom+c.getHeight()];
52780             break;
52781         }
52782     }
52783 });/*
52784  * Based on:
52785  * Ext JS Library 1.1.1
52786  * Copyright(c) 2006-2007, Ext JS, LLC.
52787  *
52788  * Originally Released Under LGPL - original licence link has changed is not relivant.
52789  *
52790  * Fork - LGPL
52791  * <script type="text/javascript">
52792  */
52793 /*
52794  * These classes are private internal classes
52795  */
52796 Roo.CenterLayoutRegion = function(mgr, config){
52797     Roo.LayoutRegion.call(this, mgr, config, "center");
52798     this.visible = true;
52799     this.minWidth = config.minWidth || 20;
52800     this.minHeight = config.minHeight || 20;
52801 };
52802
52803 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52804     hide : function(){
52805         // center panel can't be hidden
52806     },
52807     
52808     show : function(){
52809         // center panel can't be hidden
52810     },
52811     
52812     getMinWidth: function(){
52813         return this.minWidth;
52814     },
52815     
52816     getMinHeight: function(){
52817         return this.minHeight;
52818     }
52819 });
52820
52821
52822 Roo.NorthLayoutRegion = function(mgr, config){
52823     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52824     if(this.split){
52825         this.split.placement = Roo.SplitBar.TOP;
52826         this.split.orientation = Roo.SplitBar.VERTICAL;
52827         this.split.el.addClass("x-layout-split-v");
52828     }
52829     var size = config.initialSize || config.height;
52830     if(typeof size != "undefined"){
52831         this.el.setHeight(size);
52832     }
52833 };
52834 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52835     orientation: Roo.SplitBar.VERTICAL,
52836     getBox : function(){
52837         if(this.collapsed){
52838             return this.collapsedEl.getBox();
52839         }
52840         var box = this.el.getBox();
52841         if(this.split){
52842             box.height += this.split.el.getHeight();
52843         }
52844         return box;
52845     },
52846     
52847     updateBox : function(box){
52848         if(this.split && !this.collapsed){
52849             box.height -= this.split.el.getHeight();
52850             this.split.el.setLeft(box.x);
52851             this.split.el.setTop(box.y+box.height);
52852             this.split.el.setWidth(box.width);
52853         }
52854         if(this.collapsed){
52855             this.updateBody(box.width, null);
52856         }
52857         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52858     }
52859 });
52860
52861 Roo.SouthLayoutRegion = function(mgr, config){
52862     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52863     if(this.split){
52864         this.split.placement = Roo.SplitBar.BOTTOM;
52865         this.split.orientation = Roo.SplitBar.VERTICAL;
52866         this.split.el.addClass("x-layout-split-v");
52867     }
52868     var size = config.initialSize || config.height;
52869     if(typeof size != "undefined"){
52870         this.el.setHeight(size);
52871     }
52872 };
52873 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52874     orientation: Roo.SplitBar.VERTICAL,
52875     getBox : function(){
52876         if(this.collapsed){
52877             return this.collapsedEl.getBox();
52878         }
52879         var box = this.el.getBox();
52880         if(this.split){
52881             var sh = this.split.el.getHeight();
52882             box.height += sh;
52883             box.y -= sh;
52884         }
52885         return box;
52886     },
52887     
52888     updateBox : function(box){
52889         if(this.split && !this.collapsed){
52890             var sh = this.split.el.getHeight();
52891             box.height -= sh;
52892             box.y += sh;
52893             this.split.el.setLeft(box.x);
52894             this.split.el.setTop(box.y-sh);
52895             this.split.el.setWidth(box.width);
52896         }
52897         if(this.collapsed){
52898             this.updateBody(box.width, null);
52899         }
52900         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52901     }
52902 });
52903
52904 Roo.EastLayoutRegion = function(mgr, config){
52905     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52906     if(this.split){
52907         this.split.placement = Roo.SplitBar.RIGHT;
52908         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52909         this.split.el.addClass("x-layout-split-h");
52910     }
52911     var size = config.initialSize || config.width;
52912     if(typeof size != "undefined"){
52913         this.el.setWidth(size);
52914     }
52915 };
52916 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52917     orientation: Roo.SplitBar.HORIZONTAL,
52918     getBox : function(){
52919         if(this.collapsed){
52920             return this.collapsedEl.getBox();
52921         }
52922         var box = this.el.getBox();
52923         if(this.split){
52924             var sw = this.split.el.getWidth();
52925             box.width += sw;
52926             box.x -= sw;
52927         }
52928         return box;
52929     },
52930
52931     updateBox : function(box){
52932         if(this.split && !this.collapsed){
52933             var sw = this.split.el.getWidth();
52934             box.width -= sw;
52935             this.split.el.setLeft(box.x);
52936             this.split.el.setTop(box.y);
52937             this.split.el.setHeight(box.height);
52938             box.x += sw;
52939         }
52940         if(this.collapsed){
52941             this.updateBody(null, box.height);
52942         }
52943         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52944     }
52945 });
52946
52947 Roo.WestLayoutRegion = function(mgr, config){
52948     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52949     if(this.split){
52950         this.split.placement = Roo.SplitBar.LEFT;
52951         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52952         this.split.el.addClass("x-layout-split-h");
52953     }
52954     var size = config.initialSize || config.width;
52955     if(typeof size != "undefined"){
52956         this.el.setWidth(size);
52957     }
52958 };
52959 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52960     orientation: Roo.SplitBar.HORIZONTAL,
52961     getBox : function(){
52962         if(this.collapsed){
52963             return this.collapsedEl.getBox();
52964         }
52965         var box = this.el.getBox();
52966         if(this.split){
52967             box.width += this.split.el.getWidth();
52968         }
52969         return box;
52970     },
52971     
52972     updateBox : function(box){
52973         if(this.split && !this.collapsed){
52974             var sw = this.split.el.getWidth();
52975             box.width -= sw;
52976             this.split.el.setLeft(box.x+box.width);
52977             this.split.el.setTop(box.y);
52978             this.split.el.setHeight(box.height);
52979         }
52980         if(this.collapsed){
52981             this.updateBody(null, box.height);
52982         }
52983         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52984     }
52985 });
52986 /*
52987  * Based on:
52988  * Ext JS Library 1.1.1
52989  * Copyright(c) 2006-2007, Ext JS, LLC.
52990  *
52991  * Originally Released Under LGPL - original licence link has changed is not relivant.
52992  *
52993  * Fork - LGPL
52994  * <script type="text/javascript">
52995  */
52996  
52997  
52998 /*
52999  * Private internal class for reading and applying state
53000  */
53001 Roo.LayoutStateManager = function(layout){
53002      // default empty state
53003      this.state = {
53004         north: {},
53005         south: {},
53006         east: {},
53007         west: {}       
53008     };
53009 };
53010
53011 Roo.LayoutStateManager.prototype = {
53012     init : function(layout, provider){
53013         this.provider = provider;
53014         var state = provider.get(layout.id+"-layout-state");
53015         if(state){
53016             var wasUpdating = layout.isUpdating();
53017             if(!wasUpdating){
53018                 layout.beginUpdate();
53019             }
53020             for(var key in state){
53021                 if(typeof state[key] != "function"){
53022                     var rstate = state[key];
53023                     var r = layout.getRegion(key);
53024                     if(r && rstate){
53025                         if(rstate.size){
53026                             r.resizeTo(rstate.size);
53027                         }
53028                         if(rstate.collapsed == true){
53029                             r.collapse(true);
53030                         }else{
53031                             r.expand(null, true);
53032                         }
53033                     }
53034                 }
53035             }
53036             if(!wasUpdating){
53037                 layout.endUpdate();
53038             }
53039             this.state = state; 
53040         }
53041         this.layout = layout;
53042         layout.on("regionresized", this.onRegionResized, this);
53043         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53044         layout.on("regionexpanded", this.onRegionExpanded, this);
53045     },
53046     
53047     storeState : function(){
53048         this.provider.set(this.layout.id+"-layout-state", this.state);
53049     },
53050     
53051     onRegionResized : function(region, newSize){
53052         this.state[region.getPosition()].size = newSize;
53053         this.storeState();
53054     },
53055     
53056     onRegionCollapsed : function(region){
53057         this.state[region.getPosition()].collapsed = true;
53058         this.storeState();
53059     },
53060     
53061     onRegionExpanded : function(region){
53062         this.state[region.getPosition()].collapsed = false;
53063         this.storeState();
53064     }
53065 };/*
53066  * Based on:
53067  * Ext JS Library 1.1.1
53068  * Copyright(c) 2006-2007, Ext JS, LLC.
53069  *
53070  * Originally Released Under LGPL - original licence link has changed is not relivant.
53071  *
53072  * Fork - LGPL
53073  * <script type="text/javascript">
53074  */
53075 /**
53076  * @class Roo.ContentPanel
53077  * @extends Roo.util.Observable
53078  * A basic ContentPanel element.
53079  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53080  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53081  * @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
53082  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53083  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53084  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53085  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53086  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53087  * @cfg {String} title          The title for this panel
53088  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53089  * @cfg {String} url            Calls {@link #setUrl} with this value
53090  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53091  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53092  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53093  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53094
53095  * @constructor
53096  * Create a new ContentPanel.
53097  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53098  * @param {String/Object} config A string to set only the title or a config object
53099  * @param {String} content (optional) Set the HTML content for this panel
53100  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53101  */
53102 Roo.ContentPanel = function(el, config, content){
53103     
53104      
53105     /*
53106     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53107         config = el;
53108         el = Roo.id();
53109     }
53110     if (config && config.parentLayout) { 
53111         el = config.parentLayout.el.createChild(); 
53112     }
53113     */
53114     if(el.autoCreate){ // xtype is available if this is called from factory
53115         config = el;
53116         el = Roo.id();
53117     }
53118     this.el = Roo.get(el);
53119     if(!this.el && config && config.autoCreate){
53120         if(typeof config.autoCreate == "object"){
53121             if(!config.autoCreate.id){
53122                 config.autoCreate.id = config.id||el;
53123             }
53124             this.el = Roo.DomHelper.append(document.body,
53125                         config.autoCreate, true);
53126         }else{
53127             this.el = Roo.DomHelper.append(document.body,
53128                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53129         }
53130     }
53131     this.closable = false;
53132     this.loaded = false;
53133     this.active = false;
53134     if(typeof config == "string"){
53135         this.title = config;
53136     }else{
53137         Roo.apply(this, config);
53138     }
53139     
53140     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53141         this.wrapEl = this.el.wrap();
53142         this.toolbar.container = this.el.insertSibling(false, 'before');
53143         this.toolbar = new Roo.Toolbar(this.toolbar);
53144     }
53145     
53146     // xtype created footer. - not sure if will work as we normally have to render first..
53147     if (this.footer && !this.footer.el && this.footer.xtype) {
53148         if (!this.wrapEl) {
53149             this.wrapEl = this.el.wrap();
53150         }
53151     
53152         this.footer.container = this.wrapEl.createChild();
53153          
53154         this.footer = Roo.factory(this.footer, Roo);
53155         
53156     }
53157     
53158     if(this.resizeEl){
53159         this.resizeEl = Roo.get(this.resizeEl, true);
53160     }else{
53161         this.resizeEl = this.el;
53162     }
53163     // handle view.xtype
53164     
53165  
53166     
53167     
53168     this.addEvents({
53169         /**
53170          * @event activate
53171          * Fires when this panel is activated. 
53172          * @param {Roo.ContentPanel} this
53173          */
53174         "activate" : true,
53175         /**
53176          * @event deactivate
53177          * Fires when this panel is activated. 
53178          * @param {Roo.ContentPanel} this
53179          */
53180         "deactivate" : true,
53181
53182         /**
53183          * @event resize
53184          * Fires when this panel is resized if fitToFrame is true.
53185          * @param {Roo.ContentPanel} this
53186          * @param {Number} width The width after any component adjustments
53187          * @param {Number} height The height after any component adjustments
53188          */
53189         "resize" : true,
53190         
53191          /**
53192          * @event render
53193          * Fires when this tab is created
53194          * @param {Roo.ContentPanel} this
53195          */
53196         "render" : true
53197         
53198         
53199         
53200     });
53201     
53202
53203     
53204     
53205     if(this.autoScroll){
53206         this.resizeEl.setStyle("overflow", "auto");
53207     } else {
53208         // fix randome scrolling
53209         this.el.on('scroll', function() {
53210             Roo.log('fix random scolling');
53211             this.scrollTo('top',0); 
53212         });
53213     }
53214     content = content || this.content;
53215     if(content){
53216         this.setContent(content);
53217     }
53218     if(config && config.url){
53219         this.setUrl(this.url, this.params, this.loadOnce);
53220     }
53221     
53222     
53223     
53224     Roo.ContentPanel.superclass.constructor.call(this);
53225     
53226     if (this.view && typeof(this.view.xtype) != 'undefined') {
53227         this.view.el = this.el.appendChild(document.createElement("div"));
53228         this.view = Roo.factory(this.view); 
53229         this.view.render  &&  this.view.render(false, '');  
53230     }
53231     
53232     
53233     this.fireEvent('render', this);
53234 };
53235
53236 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53237     tabTip:'',
53238     setRegion : function(region){
53239         this.region = region;
53240         if(region){
53241            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53242         }else{
53243            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53244         } 
53245     },
53246     
53247     /**
53248      * Returns the toolbar for this Panel if one was configured. 
53249      * @return {Roo.Toolbar} 
53250      */
53251     getToolbar : function(){
53252         return this.toolbar;
53253     },
53254     
53255     setActiveState : function(active){
53256         this.active = active;
53257         if(!active){
53258             this.fireEvent("deactivate", this);
53259         }else{
53260             this.fireEvent("activate", this);
53261         }
53262     },
53263     /**
53264      * Updates this panel's element
53265      * @param {String} content The new content
53266      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53267     */
53268     setContent : function(content, loadScripts){
53269         this.el.update(content, loadScripts);
53270     },
53271
53272     ignoreResize : function(w, h){
53273         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53274             return true;
53275         }else{
53276             this.lastSize = {width: w, height: h};
53277             return false;
53278         }
53279     },
53280     /**
53281      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53282      * @return {Roo.UpdateManager} The UpdateManager
53283      */
53284     getUpdateManager : function(){
53285         return this.el.getUpdateManager();
53286     },
53287      /**
53288      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53289      * @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:
53290 <pre><code>
53291 panel.load({
53292     url: "your-url.php",
53293     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53294     callback: yourFunction,
53295     scope: yourObject, //(optional scope)
53296     discardUrl: false,
53297     nocache: false,
53298     text: "Loading...",
53299     timeout: 30,
53300     scripts: false
53301 });
53302 </code></pre>
53303      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53304      * 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.
53305      * @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}
53306      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53307      * @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.
53308      * @return {Roo.ContentPanel} this
53309      */
53310     load : function(){
53311         var um = this.el.getUpdateManager();
53312         um.update.apply(um, arguments);
53313         return this;
53314     },
53315
53316
53317     /**
53318      * 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.
53319      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53320      * @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)
53321      * @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)
53322      * @return {Roo.UpdateManager} The UpdateManager
53323      */
53324     setUrl : function(url, params, loadOnce){
53325         if(this.refreshDelegate){
53326             this.removeListener("activate", this.refreshDelegate);
53327         }
53328         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53329         this.on("activate", this.refreshDelegate);
53330         return this.el.getUpdateManager();
53331     },
53332     
53333     _handleRefresh : function(url, params, loadOnce){
53334         if(!loadOnce || !this.loaded){
53335             var updater = this.el.getUpdateManager();
53336             updater.update(url, params, this._setLoaded.createDelegate(this));
53337         }
53338     },
53339     
53340     _setLoaded : function(){
53341         this.loaded = true;
53342     }, 
53343     
53344     /**
53345      * Returns this panel's id
53346      * @return {String} 
53347      */
53348     getId : function(){
53349         return this.el.id;
53350     },
53351     
53352     /** 
53353      * Returns this panel's element - used by regiosn to add.
53354      * @return {Roo.Element} 
53355      */
53356     getEl : function(){
53357         return this.wrapEl || this.el;
53358     },
53359     
53360     adjustForComponents : function(width, height)
53361     {
53362         //Roo.log('adjustForComponents ');
53363         if(this.resizeEl != this.el){
53364             width -= this.el.getFrameWidth('lr');
53365             height -= this.el.getFrameWidth('tb');
53366         }
53367         if(this.toolbar){
53368             var te = this.toolbar.getEl();
53369             height -= te.getHeight();
53370             te.setWidth(width);
53371         }
53372         if(this.footer){
53373             var te = this.footer.getEl();
53374             Roo.log("footer:" + te.getHeight());
53375             
53376             height -= te.getHeight();
53377             te.setWidth(width);
53378         }
53379         
53380         
53381         if(this.adjustments){
53382             width += this.adjustments[0];
53383             height += this.adjustments[1];
53384         }
53385         return {"width": width, "height": height};
53386     },
53387     
53388     setSize : function(width, height){
53389         if(this.fitToFrame && !this.ignoreResize(width, height)){
53390             if(this.fitContainer && this.resizeEl != this.el){
53391                 this.el.setSize(width, height);
53392             }
53393             var size = this.adjustForComponents(width, height);
53394             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53395             this.fireEvent('resize', this, size.width, size.height);
53396         }
53397     },
53398     
53399     /**
53400      * Returns this panel's title
53401      * @return {String} 
53402      */
53403     getTitle : function(){
53404         return this.title;
53405     },
53406     
53407     /**
53408      * Set this panel's title
53409      * @param {String} title
53410      */
53411     setTitle : function(title){
53412         this.title = title;
53413         if(this.region){
53414             this.region.updatePanelTitle(this, title);
53415         }
53416     },
53417     
53418     /**
53419      * Returns true is this panel was configured to be closable
53420      * @return {Boolean} 
53421      */
53422     isClosable : function(){
53423         return this.closable;
53424     },
53425     
53426     beforeSlide : function(){
53427         this.el.clip();
53428         this.resizeEl.clip();
53429     },
53430     
53431     afterSlide : function(){
53432         this.el.unclip();
53433         this.resizeEl.unclip();
53434     },
53435     
53436     /**
53437      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53438      *   Will fail silently if the {@link #setUrl} method has not been called.
53439      *   This does not activate the panel, just updates its content.
53440      */
53441     refresh : function(){
53442         if(this.refreshDelegate){
53443            this.loaded = false;
53444            this.refreshDelegate();
53445         }
53446     },
53447     
53448     /**
53449      * Destroys this panel
53450      */
53451     destroy : function(){
53452         this.el.removeAllListeners();
53453         var tempEl = document.createElement("span");
53454         tempEl.appendChild(this.el.dom);
53455         tempEl.innerHTML = "";
53456         this.el.remove();
53457         this.el = null;
53458     },
53459     
53460     /**
53461      * form - if the content panel contains a form - this is a reference to it.
53462      * @type {Roo.form.Form}
53463      */
53464     form : false,
53465     /**
53466      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53467      *    This contains a reference to it.
53468      * @type {Roo.View}
53469      */
53470     view : false,
53471     
53472       /**
53473      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53474      * <pre><code>
53475
53476 layout.addxtype({
53477        xtype : 'Form',
53478        items: [ .... ]
53479    }
53480 );
53481
53482 </code></pre>
53483      * @param {Object} cfg Xtype definition of item to add.
53484      */
53485     
53486     addxtype : function(cfg) {
53487         // add form..
53488         if (cfg.xtype.match(/^Form$/)) {
53489             
53490             var el;
53491             //if (this.footer) {
53492             //    el = this.footer.container.insertSibling(false, 'before');
53493             //} else {
53494                 el = this.el.createChild();
53495             //}
53496
53497             this.form = new  Roo.form.Form(cfg);
53498             
53499             
53500             if ( this.form.allItems.length) {
53501                 this.form.render(el.dom);
53502             }
53503             return this.form;
53504         }
53505         // should only have one of theses..
53506         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53507             // views.. should not be just added - used named prop 'view''
53508             
53509             cfg.el = this.el.appendChild(document.createElement("div"));
53510             // factory?
53511             
53512             var ret = new Roo.factory(cfg);
53513              
53514              ret.render && ret.render(false, ''); // render blank..
53515             this.view = ret;
53516             return ret;
53517         }
53518         return false;
53519     }
53520 });
53521
53522 /**
53523  * @class Roo.GridPanel
53524  * @extends Roo.ContentPanel
53525  * @constructor
53526  * Create a new GridPanel.
53527  * @param {Roo.grid.Grid} grid The grid for this panel
53528  * @param {String/Object} config A string to set only the panel's title, or a config object
53529  */
53530 Roo.GridPanel = function(grid, config){
53531     
53532   
53533     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53534         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53535         
53536     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53537     
53538     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53539     
53540     if(this.toolbar){
53541         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53542     }
53543     // xtype created footer. - not sure if will work as we normally have to render first..
53544     if (this.footer && !this.footer.el && this.footer.xtype) {
53545         
53546         this.footer.container = this.grid.getView().getFooterPanel(true);
53547         this.footer.dataSource = this.grid.dataSource;
53548         this.footer = Roo.factory(this.footer, Roo);
53549         
53550     }
53551     
53552     grid.monitorWindowResize = false; // turn off autosizing
53553     grid.autoHeight = false;
53554     grid.autoWidth = false;
53555     this.grid = grid;
53556     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53557 };
53558
53559 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53560     getId : function(){
53561         return this.grid.id;
53562     },
53563     
53564     /**
53565      * Returns the grid for this panel
53566      * @return {Roo.grid.Grid} 
53567      */
53568     getGrid : function(){
53569         return this.grid;    
53570     },
53571     
53572     setSize : function(width, height){
53573         if(!this.ignoreResize(width, height)){
53574             var grid = this.grid;
53575             var size = this.adjustForComponents(width, height);
53576             grid.getGridEl().setSize(size.width, size.height);
53577             grid.autoSize();
53578         }
53579     },
53580     
53581     beforeSlide : function(){
53582         this.grid.getView().scroller.clip();
53583     },
53584     
53585     afterSlide : function(){
53586         this.grid.getView().scroller.unclip();
53587     },
53588     
53589     destroy : function(){
53590         this.grid.destroy();
53591         delete this.grid;
53592         Roo.GridPanel.superclass.destroy.call(this); 
53593     }
53594 });
53595
53596
53597 /**
53598  * @class Roo.NestedLayoutPanel
53599  * @extends Roo.ContentPanel
53600  * @constructor
53601  * Create a new NestedLayoutPanel.
53602  * 
53603  * 
53604  * @param {Roo.BorderLayout} layout The layout for this panel
53605  * @param {String/Object} config A string to set only the title or a config object
53606  */
53607 Roo.NestedLayoutPanel = function(layout, config)
53608 {
53609     // construct with only one argument..
53610     /* FIXME - implement nicer consturctors
53611     if (layout.layout) {
53612         config = layout;
53613         layout = config.layout;
53614         delete config.layout;
53615     }
53616     if (layout.xtype && !layout.getEl) {
53617         // then layout needs constructing..
53618         layout = Roo.factory(layout, Roo);
53619     }
53620     */
53621     
53622     
53623     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53624     
53625     layout.monitorWindowResize = false; // turn off autosizing
53626     this.layout = layout;
53627     this.layout.getEl().addClass("x-layout-nested-layout");
53628     
53629     
53630     
53631     
53632 };
53633
53634 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53635
53636     setSize : function(width, height){
53637         if(!this.ignoreResize(width, height)){
53638             var size = this.adjustForComponents(width, height);
53639             var el = this.layout.getEl();
53640             el.setSize(size.width, size.height);
53641             var touch = el.dom.offsetWidth;
53642             this.layout.layout();
53643             // ie requires a double layout on the first pass
53644             if(Roo.isIE && !this.initialized){
53645                 this.initialized = true;
53646                 this.layout.layout();
53647             }
53648         }
53649     },
53650     
53651     // activate all subpanels if not currently active..
53652     
53653     setActiveState : function(active){
53654         this.active = active;
53655         if(!active){
53656             this.fireEvent("deactivate", this);
53657             return;
53658         }
53659         
53660         this.fireEvent("activate", this);
53661         // not sure if this should happen before or after..
53662         if (!this.layout) {
53663             return; // should not happen..
53664         }
53665         var reg = false;
53666         for (var r in this.layout.regions) {
53667             reg = this.layout.getRegion(r);
53668             if (reg.getActivePanel()) {
53669                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53670                 reg.setActivePanel(reg.getActivePanel());
53671                 continue;
53672             }
53673             if (!reg.panels.length) {
53674                 continue;
53675             }
53676             reg.showPanel(reg.getPanel(0));
53677         }
53678         
53679         
53680         
53681         
53682     },
53683     
53684     /**
53685      * Returns the nested BorderLayout for this panel
53686      * @return {Roo.BorderLayout} 
53687      */
53688     getLayout : function(){
53689         return this.layout;
53690     },
53691     
53692      /**
53693      * Adds a xtype elements to the layout of the nested panel
53694      * <pre><code>
53695
53696 panel.addxtype({
53697        xtype : 'ContentPanel',
53698        region: 'west',
53699        items: [ .... ]
53700    }
53701 );
53702
53703 panel.addxtype({
53704         xtype : 'NestedLayoutPanel',
53705         region: 'west',
53706         layout: {
53707            center: { },
53708            west: { }   
53709         },
53710         items : [ ... list of content panels or nested layout panels.. ]
53711    }
53712 );
53713 </code></pre>
53714      * @param {Object} cfg Xtype definition of item to add.
53715      */
53716     addxtype : function(cfg) {
53717         return this.layout.addxtype(cfg);
53718     
53719     }
53720 });
53721
53722 Roo.ScrollPanel = function(el, config, content){
53723     config = config || {};
53724     config.fitToFrame = true;
53725     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53726     
53727     this.el.dom.style.overflow = "hidden";
53728     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53729     this.el.removeClass("x-layout-inactive-content");
53730     this.el.on("mousewheel", this.onWheel, this);
53731
53732     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53733     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53734     up.unselectable(); down.unselectable();
53735     up.on("click", this.scrollUp, this);
53736     down.on("click", this.scrollDown, this);
53737     up.addClassOnOver("x-scroller-btn-over");
53738     down.addClassOnOver("x-scroller-btn-over");
53739     up.addClassOnClick("x-scroller-btn-click");
53740     down.addClassOnClick("x-scroller-btn-click");
53741     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53742
53743     this.resizeEl = this.el;
53744     this.el = wrap; this.up = up; this.down = down;
53745 };
53746
53747 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53748     increment : 100,
53749     wheelIncrement : 5,
53750     scrollUp : function(){
53751         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53752     },
53753
53754     scrollDown : function(){
53755         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53756     },
53757
53758     afterScroll : function(){
53759         var el = this.resizeEl;
53760         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53761         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53762         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53763     },
53764
53765     setSize : function(){
53766         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53767         this.afterScroll();
53768     },
53769
53770     onWheel : function(e){
53771         var d = e.getWheelDelta();
53772         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53773         this.afterScroll();
53774         e.stopEvent();
53775     },
53776
53777     setContent : function(content, loadScripts){
53778         this.resizeEl.update(content, loadScripts);
53779     }
53780
53781 });
53782
53783
53784
53785
53786
53787
53788
53789
53790
53791 /**
53792  * @class Roo.TreePanel
53793  * @extends Roo.ContentPanel
53794  * @constructor
53795  * Create a new TreePanel. - defaults to fit/scoll contents.
53796  * @param {String/Object} config A string to set only the panel's title, or a config object
53797  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53798  */
53799 Roo.TreePanel = function(config){
53800     var el = config.el;
53801     var tree = config.tree;
53802     delete config.tree; 
53803     delete config.el; // hopefull!
53804     
53805     // wrapper for IE7 strict & safari scroll issue
53806     
53807     var treeEl = el.createChild();
53808     config.resizeEl = treeEl;
53809     
53810     
53811     
53812     Roo.TreePanel.superclass.constructor.call(this, el, config);
53813  
53814  
53815     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53816     //console.log(tree);
53817     this.on('activate', function()
53818     {
53819         if (this.tree.rendered) {
53820             return;
53821         }
53822         //console.log('render tree');
53823         this.tree.render();
53824     });
53825     // this should not be needed.. - it's actually the 'el' that resizes?
53826     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53827     
53828     //this.on('resize',  function (cp, w, h) {
53829     //        this.tree.innerCt.setWidth(w);
53830     //        this.tree.innerCt.setHeight(h);
53831     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53832     //});
53833
53834         
53835     
53836 };
53837
53838 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53839     fitToFrame : true,
53840     autoScroll : true
53841 });
53842
53843
53844
53845
53846
53847
53848
53849
53850
53851
53852
53853 /*
53854  * Based on:
53855  * Ext JS Library 1.1.1
53856  * Copyright(c) 2006-2007, Ext JS, LLC.
53857  *
53858  * Originally Released Under LGPL - original licence link has changed is not relivant.
53859  *
53860  * Fork - LGPL
53861  * <script type="text/javascript">
53862  */
53863  
53864
53865 /**
53866  * @class Roo.ReaderLayout
53867  * @extends Roo.BorderLayout
53868  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53869  * center region containing two nested regions (a top one for a list view and one for item preview below),
53870  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53871  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53872  * expedites the setup of the overall layout and regions for this common application style.
53873  * Example:
53874  <pre><code>
53875 var reader = new Roo.ReaderLayout();
53876 var CP = Roo.ContentPanel;  // shortcut for adding
53877
53878 reader.beginUpdate();
53879 reader.add("north", new CP("north", "North"));
53880 reader.add("west", new CP("west", {title: "West"}));
53881 reader.add("east", new CP("east", {title: "East"}));
53882
53883 reader.regions.listView.add(new CP("listView", "List"));
53884 reader.regions.preview.add(new CP("preview", "Preview"));
53885 reader.endUpdate();
53886 </code></pre>
53887 * @constructor
53888 * Create a new ReaderLayout
53889 * @param {Object} config Configuration options
53890 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53891 * document.body if omitted)
53892 */
53893 Roo.ReaderLayout = function(config, renderTo){
53894     var c = config || {size:{}};
53895     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53896         north: c.north !== false ? Roo.apply({
53897             split:false,
53898             initialSize: 32,
53899             titlebar: false
53900         }, c.north) : false,
53901         west: c.west !== false ? Roo.apply({
53902             split:true,
53903             initialSize: 200,
53904             minSize: 175,
53905             maxSize: 400,
53906             titlebar: true,
53907             collapsible: true,
53908             animate: true,
53909             margins:{left:5,right:0,bottom:5,top:5},
53910             cmargins:{left:5,right:5,bottom:5,top:5}
53911         }, c.west) : false,
53912         east: c.east !== false ? Roo.apply({
53913             split:true,
53914             initialSize: 200,
53915             minSize: 175,
53916             maxSize: 400,
53917             titlebar: true,
53918             collapsible: true,
53919             animate: true,
53920             margins:{left:0,right:5,bottom:5,top:5},
53921             cmargins:{left:5,right:5,bottom:5,top:5}
53922         }, c.east) : false,
53923         center: Roo.apply({
53924             tabPosition: 'top',
53925             autoScroll:false,
53926             closeOnTab: true,
53927             titlebar:false,
53928             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53929         }, c.center)
53930     });
53931
53932     this.el.addClass('x-reader');
53933
53934     this.beginUpdate();
53935
53936     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53937         south: c.preview !== false ? Roo.apply({
53938             split:true,
53939             initialSize: 200,
53940             minSize: 100,
53941             autoScroll:true,
53942             collapsible:true,
53943             titlebar: true,
53944             cmargins:{top:5,left:0, right:0, bottom:0}
53945         }, c.preview) : false,
53946         center: Roo.apply({
53947             autoScroll:false,
53948             titlebar:false,
53949             minHeight:200
53950         }, c.listView)
53951     });
53952     this.add('center', new Roo.NestedLayoutPanel(inner,
53953             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53954
53955     this.endUpdate();
53956
53957     this.regions.preview = inner.getRegion('south');
53958     this.regions.listView = inner.getRegion('center');
53959 };
53960
53961 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53962  * Based on:
53963  * Ext JS Library 1.1.1
53964  * Copyright(c) 2006-2007, Ext JS, LLC.
53965  *
53966  * Originally Released Under LGPL - original licence link has changed is not relivant.
53967  *
53968  * Fork - LGPL
53969  * <script type="text/javascript">
53970  */
53971  
53972 /**
53973  * @class Roo.grid.Grid
53974  * @extends Roo.util.Observable
53975  * This class represents the primary interface of a component based grid control.
53976  * <br><br>Usage:<pre><code>
53977  var grid = new Roo.grid.Grid("my-container-id", {
53978      ds: myDataStore,
53979      cm: myColModel,
53980      selModel: mySelectionModel,
53981      autoSizeColumns: true,
53982      monitorWindowResize: false,
53983      trackMouseOver: true
53984  });
53985  // set any options
53986  grid.render();
53987  * </code></pre>
53988  * <b>Common Problems:</b><br/>
53989  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53990  * element will correct this<br/>
53991  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53992  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53993  * are unpredictable.<br/>
53994  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53995  * grid to calculate dimensions/offsets.<br/>
53996   * @constructor
53997  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53998  * The container MUST have some type of size defined for the grid to fill. The container will be
53999  * automatically set to position relative if it isn't already.
54000  * @param {Object} config A config object that sets properties on this grid.
54001  */
54002 Roo.grid.Grid = function(container, config){
54003         // initialize the container
54004         this.container = Roo.get(container);
54005         this.container.update("");
54006         this.container.setStyle("overflow", "hidden");
54007     this.container.addClass('x-grid-container');
54008
54009     this.id = this.container.id;
54010
54011     Roo.apply(this, config);
54012     // check and correct shorthanded configs
54013     if(this.ds){
54014         this.dataSource = this.ds;
54015         delete this.ds;
54016     }
54017     if(this.cm){
54018         this.colModel = this.cm;
54019         delete this.cm;
54020     }
54021     if(this.sm){
54022         this.selModel = this.sm;
54023         delete this.sm;
54024     }
54025
54026     if (this.selModel) {
54027         this.selModel = Roo.factory(this.selModel, Roo.grid);
54028         this.sm = this.selModel;
54029         this.sm.xmodule = this.xmodule || false;
54030     }
54031     if (typeof(this.colModel.config) == 'undefined') {
54032         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54033         this.cm = this.colModel;
54034         this.cm.xmodule = this.xmodule || false;
54035     }
54036     if (this.dataSource) {
54037         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54038         this.ds = this.dataSource;
54039         this.ds.xmodule = this.xmodule || false;
54040          
54041     }
54042     
54043     
54044     
54045     if(this.width){
54046         this.container.setWidth(this.width);
54047     }
54048
54049     if(this.height){
54050         this.container.setHeight(this.height);
54051     }
54052     /** @private */
54053         this.addEvents({
54054         // raw events
54055         /**
54056          * @event click
54057          * The raw click event for the entire grid.
54058          * @param {Roo.EventObject} e
54059          */
54060         "click" : true,
54061         /**
54062          * @event dblclick
54063          * The raw dblclick event for the entire grid.
54064          * @param {Roo.EventObject} e
54065          */
54066         "dblclick" : true,
54067         /**
54068          * @event contextmenu
54069          * The raw contextmenu event for the entire grid.
54070          * @param {Roo.EventObject} e
54071          */
54072         "contextmenu" : true,
54073         /**
54074          * @event mousedown
54075          * The raw mousedown event for the entire grid.
54076          * @param {Roo.EventObject} e
54077          */
54078         "mousedown" : true,
54079         /**
54080          * @event mouseup
54081          * The raw mouseup event for the entire grid.
54082          * @param {Roo.EventObject} e
54083          */
54084         "mouseup" : true,
54085         /**
54086          * @event mouseover
54087          * The raw mouseover event for the entire grid.
54088          * @param {Roo.EventObject} e
54089          */
54090         "mouseover" : true,
54091         /**
54092          * @event mouseout
54093          * The raw mouseout event for the entire grid.
54094          * @param {Roo.EventObject} e
54095          */
54096         "mouseout" : true,
54097         /**
54098          * @event keypress
54099          * The raw keypress event for the entire grid.
54100          * @param {Roo.EventObject} e
54101          */
54102         "keypress" : true,
54103         /**
54104          * @event keydown
54105          * The raw keydown event for the entire grid.
54106          * @param {Roo.EventObject} e
54107          */
54108         "keydown" : true,
54109
54110         // custom events
54111
54112         /**
54113          * @event cellclick
54114          * Fires when a cell is clicked
54115          * @param {Grid} this
54116          * @param {Number} rowIndex
54117          * @param {Number} columnIndex
54118          * @param {Roo.EventObject} e
54119          */
54120         "cellclick" : true,
54121         /**
54122          * @event celldblclick
54123          * Fires when a cell is double clicked
54124          * @param {Grid} this
54125          * @param {Number} rowIndex
54126          * @param {Number} columnIndex
54127          * @param {Roo.EventObject} e
54128          */
54129         "celldblclick" : true,
54130         /**
54131          * @event rowclick
54132          * Fires when a row is clicked
54133          * @param {Grid} this
54134          * @param {Number} rowIndex
54135          * @param {Roo.EventObject} e
54136          */
54137         "rowclick" : true,
54138         /**
54139          * @event rowdblclick
54140          * Fires when a row is double clicked
54141          * @param {Grid} this
54142          * @param {Number} rowIndex
54143          * @param {Roo.EventObject} e
54144          */
54145         "rowdblclick" : true,
54146         /**
54147          * @event headerclick
54148          * Fires when a header is clicked
54149          * @param {Grid} this
54150          * @param {Number} columnIndex
54151          * @param {Roo.EventObject} e
54152          */
54153         "headerclick" : true,
54154         /**
54155          * @event headerdblclick
54156          * Fires when a header cell is double clicked
54157          * @param {Grid} this
54158          * @param {Number} columnIndex
54159          * @param {Roo.EventObject} e
54160          */
54161         "headerdblclick" : true,
54162         /**
54163          * @event rowcontextmenu
54164          * Fires when a row is right clicked
54165          * @param {Grid} this
54166          * @param {Number} rowIndex
54167          * @param {Roo.EventObject} e
54168          */
54169         "rowcontextmenu" : true,
54170         /**
54171          * @event cellcontextmenu
54172          * Fires when a cell is right clicked
54173          * @param {Grid} this
54174          * @param {Number} rowIndex
54175          * @param {Number} cellIndex
54176          * @param {Roo.EventObject} e
54177          */
54178          "cellcontextmenu" : true,
54179         /**
54180          * @event headercontextmenu
54181          * Fires when a header is right clicked
54182          * @param {Grid} this
54183          * @param {Number} columnIndex
54184          * @param {Roo.EventObject} e
54185          */
54186         "headercontextmenu" : true,
54187         /**
54188          * @event bodyscroll
54189          * Fires when the body element is scrolled
54190          * @param {Number} scrollLeft
54191          * @param {Number} scrollTop
54192          */
54193         "bodyscroll" : true,
54194         /**
54195          * @event columnresize
54196          * Fires when the user resizes a column
54197          * @param {Number} columnIndex
54198          * @param {Number} newSize
54199          */
54200         "columnresize" : true,
54201         /**
54202          * @event columnmove
54203          * Fires when the user moves a column
54204          * @param {Number} oldIndex
54205          * @param {Number} newIndex
54206          */
54207         "columnmove" : true,
54208         /**
54209          * @event startdrag
54210          * Fires when row(s) start being dragged
54211          * @param {Grid} this
54212          * @param {Roo.GridDD} dd The drag drop object
54213          * @param {event} e The raw browser event
54214          */
54215         "startdrag" : true,
54216         /**
54217          * @event enddrag
54218          * Fires when a drag operation is complete
54219          * @param {Grid} this
54220          * @param {Roo.GridDD} dd The drag drop object
54221          * @param {event} e The raw browser event
54222          */
54223         "enddrag" : true,
54224         /**
54225          * @event dragdrop
54226          * Fires when dragged row(s) are dropped on a valid DD target
54227          * @param {Grid} this
54228          * @param {Roo.GridDD} dd The drag drop object
54229          * @param {String} targetId The target drag drop object
54230          * @param {event} e The raw browser event
54231          */
54232         "dragdrop" : true,
54233         /**
54234          * @event dragover
54235          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54236          * @param {Grid} this
54237          * @param {Roo.GridDD} dd The drag drop object
54238          * @param {String} targetId The target drag drop object
54239          * @param {event} e The raw browser event
54240          */
54241         "dragover" : true,
54242         /**
54243          * @event dragenter
54244          *  Fires when the dragged row(s) first cross another DD target while being dragged
54245          * @param {Grid} this
54246          * @param {Roo.GridDD} dd The drag drop object
54247          * @param {String} targetId The target drag drop object
54248          * @param {event} e The raw browser event
54249          */
54250         "dragenter" : true,
54251         /**
54252          * @event dragout
54253          * Fires when the dragged row(s) leave another DD target while being dragged
54254          * @param {Grid} this
54255          * @param {Roo.GridDD} dd The drag drop object
54256          * @param {String} targetId The target drag drop object
54257          * @param {event} e The raw browser event
54258          */
54259         "dragout" : true,
54260         /**
54261          * @event rowclass
54262          * Fires when a row is rendered, so you can change add a style to it.
54263          * @param {GridView} gridview   The grid view
54264          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54265          */
54266         'rowclass' : true,
54267
54268         /**
54269          * @event render
54270          * Fires when the grid is rendered
54271          * @param {Grid} grid
54272          */
54273         'render' : true
54274     });
54275
54276     Roo.grid.Grid.superclass.constructor.call(this);
54277 };
54278 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54279     
54280     /**
54281      * @cfg {String} ddGroup - drag drop group.
54282      */
54283
54284     /**
54285      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54286      */
54287     minColumnWidth : 25,
54288
54289     /**
54290      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54291      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54292      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54293      */
54294     autoSizeColumns : false,
54295
54296     /**
54297      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54298      */
54299     autoSizeHeaders : true,
54300
54301     /**
54302      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54303      */
54304     monitorWindowResize : true,
54305
54306     /**
54307      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54308      * rows measured to get a columns size. Default is 0 (all rows).
54309      */
54310     maxRowsToMeasure : 0,
54311
54312     /**
54313      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54314      */
54315     trackMouseOver : true,
54316
54317     /**
54318     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54319     */
54320     
54321     /**
54322     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54323     */
54324     enableDragDrop : false,
54325     
54326     /**
54327     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54328     */
54329     enableColumnMove : true,
54330     
54331     /**
54332     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54333     */
54334     enableColumnHide : true,
54335     
54336     /**
54337     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54338     */
54339     enableRowHeightSync : false,
54340     
54341     /**
54342     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54343     */
54344     stripeRows : true,
54345     
54346     /**
54347     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54348     */
54349     autoHeight : false,
54350
54351     /**
54352      * @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.
54353      */
54354     autoExpandColumn : false,
54355
54356     /**
54357     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54358     * Default is 50.
54359     */
54360     autoExpandMin : 50,
54361
54362     /**
54363     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54364     */
54365     autoExpandMax : 1000,
54366
54367     /**
54368     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54369     */
54370     view : null,
54371
54372     /**
54373     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54374     */
54375     loadMask : false,
54376     /**
54377     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54378     */
54379     dropTarget: false,
54380     
54381    
54382     
54383     // private
54384     rendered : false,
54385
54386     /**
54387     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54388     * of a fixed width. Default is false.
54389     */
54390     /**
54391     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54392     */
54393     /**
54394      * Called once after all setup has been completed and the grid is ready to be rendered.
54395      * @return {Roo.grid.Grid} this
54396      */
54397     render : function()
54398     {
54399         var c = this.container;
54400         // try to detect autoHeight/width mode
54401         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54402             this.autoHeight = true;
54403         }
54404         var view = this.getView();
54405         view.init(this);
54406
54407         c.on("click", this.onClick, this);
54408         c.on("dblclick", this.onDblClick, this);
54409         c.on("contextmenu", this.onContextMenu, this);
54410         c.on("keydown", this.onKeyDown, this);
54411         if (Roo.isTouch) {
54412             c.on("touchstart", this.onTouchStart, this);
54413         }
54414
54415         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54416
54417         this.getSelectionModel().init(this);
54418
54419         view.render();
54420
54421         if(this.loadMask){
54422             this.loadMask = new Roo.LoadMask(this.container,
54423                     Roo.apply({store:this.dataSource}, this.loadMask));
54424         }
54425         
54426         
54427         if (this.toolbar && this.toolbar.xtype) {
54428             this.toolbar.container = this.getView().getHeaderPanel(true);
54429             this.toolbar = new Roo.Toolbar(this.toolbar);
54430         }
54431         if (this.footer && this.footer.xtype) {
54432             this.footer.dataSource = this.getDataSource();
54433             this.footer.container = this.getView().getFooterPanel(true);
54434             this.footer = Roo.factory(this.footer, Roo);
54435         }
54436         if (this.dropTarget && this.dropTarget.xtype) {
54437             delete this.dropTarget.xtype;
54438             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54439         }
54440         
54441         
54442         this.rendered = true;
54443         this.fireEvent('render', this);
54444         return this;
54445     },
54446
54447         /**
54448          * Reconfigures the grid to use a different Store and Column Model.
54449          * The View will be bound to the new objects and refreshed.
54450          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54451          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54452          */
54453     reconfigure : function(dataSource, colModel){
54454         if(this.loadMask){
54455             this.loadMask.destroy();
54456             this.loadMask = new Roo.LoadMask(this.container,
54457                     Roo.apply({store:dataSource}, this.loadMask));
54458         }
54459         this.view.bind(dataSource, colModel);
54460         this.dataSource = dataSource;
54461         this.colModel = colModel;
54462         this.view.refresh(true);
54463     },
54464
54465     // private
54466     onKeyDown : function(e){
54467         this.fireEvent("keydown", e);
54468     },
54469
54470     /**
54471      * Destroy this grid.
54472      * @param {Boolean} removeEl True to remove the element
54473      */
54474     destroy : function(removeEl, keepListeners){
54475         if(this.loadMask){
54476             this.loadMask.destroy();
54477         }
54478         var c = this.container;
54479         c.removeAllListeners();
54480         this.view.destroy();
54481         this.colModel.purgeListeners();
54482         if(!keepListeners){
54483             this.purgeListeners();
54484         }
54485         c.update("");
54486         if(removeEl === true){
54487             c.remove();
54488         }
54489     },
54490
54491     // private
54492     processEvent : function(name, e){
54493         // does this fire select???
54494         //Roo.log('grid:processEvent '  + name);
54495         
54496         if (name != 'touchstart' ) {
54497             this.fireEvent(name, e);    
54498         }
54499         
54500         var t = e.getTarget();
54501         var v = this.view;
54502         var header = v.findHeaderIndex(t);
54503         if(header !== false){
54504             var ename = name == 'touchstart' ? 'click' : name;
54505              
54506             this.fireEvent("header" + ename, this, header, e);
54507         }else{
54508             var row = v.findRowIndex(t);
54509             var cell = v.findCellIndex(t);
54510             if (name == 'touchstart') {
54511                 // first touch is always a click.
54512                 // hopefull this happens after selection is updated.?
54513                 name = false;
54514                 
54515                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54516                     var cs = this.selModel.getSelectedCell();
54517                     if (row == cs[0] && cell == cs[1]){
54518                         name = 'dblclick';
54519                     }
54520                 }
54521                 if (typeof(this.selModel.getSelections) != 'undefined') {
54522                     var cs = this.selModel.getSelections();
54523                     var ds = this.dataSource;
54524                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54525                         name = 'dblclick';
54526                     }
54527                 }
54528                 if (!name) {
54529                     return;
54530                 }
54531             }
54532             
54533             
54534             if(row !== false){
54535                 this.fireEvent("row" + name, this, row, e);
54536                 if(cell !== false){
54537                     this.fireEvent("cell" + name, this, row, cell, e);
54538                 }
54539             }
54540         }
54541     },
54542
54543     // private
54544     onClick : function(e){
54545         this.processEvent("click", e);
54546     },
54547    // private
54548     onTouchStart : function(e){
54549         this.processEvent("touchstart", e);
54550     },
54551
54552     // private
54553     onContextMenu : function(e, t){
54554         this.processEvent("contextmenu", e);
54555     },
54556
54557     // private
54558     onDblClick : function(e){
54559         this.processEvent("dblclick", e);
54560     },
54561
54562     // private
54563     walkCells : function(row, col, step, fn, scope){
54564         var cm = this.colModel, clen = cm.getColumnCount();
54565         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54566         if(step < 0){
54567             if(col < 0){
54568                 row--;
54569                 first = false;
54570             }
54571             while(row >= 0){
54572                 if(!first){
54573                     col = clen-1;
54574                 }
54575                 first = false;
54576                 while(col >= 0){
54577                     if(fn.call(scope || this, row, col, cm) === true){
54578                         return [row, col];
54579                     }
54580                     col--;
54581                 }
54582                 row--;
54583             }
54584         } else {
54585             if(col >= clen){
54586                 row++;
54587                 first = false;
54588             }
54589             while(row < rlen){
54590                 if(!first){
54591                     col = 0;
54592                 }
54593                 first = false;
54594                 while(col < clen){
54595                     if(fn.call(scope || this, row, col, cm) === true){
54596                         return [row, col];
54597                     }
54598                     col++;
54599                 }
54600                 row++;
54601             }
54602         }
54603         return null;
54604     },
54605
54606     // private
54607     getSelections : function(){
54608         return this.selModel.getSelections();
54609     },
54610
54611     /**
54612      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54613      * but if manual update is required this method will initiate it.
54614      */
54615     autoSize : function(){
54616         if(this.rendered){
54617             this.view.layout();
54618             if(this.view.adjustForScroll){
54619                 this.view.adjustForScroll();
54620             }
54621         }
54622     },
54623
54624     /**
54625      * Returns the grid's underlying element.
54626      * @return {Element} The element
54627      */
54628     getGridEl : function(){
54629         return this.container;
54630     },
54631
54632     // private for compatibility, overridden by editor grid
54633     stopEditing : function(){},
54634
54635     /**
54636      * Returns the grid's SelectionModel.
54637      * @return {SelectionModel}
54638      */
54639     getSelectionModel : function(){
54640         if(!this.selModel){
54641             this.selModel = new Roo.grid.RowSelectionModel();
54642         }
54643         return this.selModel;
54644     },
54645
54646     /**
54647      * Returns the grid's DataSource.
54648      * @return {DataSource}
54649      */
54650     getDataSource : function(){
54651         return this.dataSource;
54652     },
54653
54654     /**
54655      * Returns the grid's ColumnModel.
54656      * @return {ColumnModel}
54657      */
54658     getColumnModel : function(){
54659         return this.colModel;
54660     },
54661
54662     /**
54663      * Returns the grid's GridView object.
54664      * @return {GridView}
54665      */
54666     getView : function(){
54667         if(!this.view){
54668             this.view = new Roo.grid.GridView(this.viewConfig);
54669         }
54670         return this.view;
54671     },
54672     /**
54673      * Called to get grid's drag proxy text, by default returns this.ddText.
54674      * @return {String}
54675      */
54676     getDragDropText : function(){
54677         var count = this.selModel.getCount();
54678         return String.format(this.ddText, count, count == 1 ? '' : 's');
54679     }
54680 });
54681 /**
54682  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54683  * %0 is replaced with the number of selected rows.
54684  * @type String
54685  */
54686 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54687  * Based on:
54688  * Ext JS Library 1.1.1
54689  * Copyright(c) 2006-2007, Ext JS, LLC.
54690  *
54691  * Originally Released Under LGPL - original licence link has changed is not relivant.
54692  *
54693  * Fork - LGPL
54694  * <script type="text/javascript">
54695  */
54696  
54697 Roo.grid.AbstractGridView = function(){
54698         this.grid = null;
54699         
54700         this.events = {
54701             "beforerowremoved" : true,
54702             "beforerowsinserted" : true,
54703             "beforerefresh" : true,
54704             "rowremoved" : true,
54705             "rowsinserted" : true,
54706             "rowupdated" : true,
54707             "refresh" : true
54708         };
54709     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54710 };
54711
54712 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54713     rowClass : "x-grid-row",
54714     cellClass : "x-grid-cell",
54715     tdClass : "x-grid-td",
54716     hdClass : "x-grid-hd",
54717     splitClass : "x-grid-hd-split",
54718     
54719     init: function(grid){
54720         this.grid = grid;
54721                 var cid = this.grid.getGridEl().id;
54722         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54723         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54724         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54725         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54726         },
54727         
54728     getColumnRenderers : function(){
54729         var renderers = [];
54730         var cm = this.grid.colModel;
54731         var colCount = cm.getColumnCount();
54732         for(var i = 0; i < colCount; i++){
54733             renderers[i] = cm.getRenderer(i);
54734         }
54735         return renderers;
54736     },
54737     
54738     getColumnIds : function(){
54739         var ids = [];
54740         var cm = this.grid.colModel;
54741         var colCount = cm.getColumnCount();
54742         for(var i = 0; i < colCount; i++){
54743             ids[i] = cm.getColumnId(i);
54744         }
54745         return ids;
54746     },
54747     
54748     getDataIndexes : function(){
54749         if(!this.indexMap){
54750             this.indexMap = this.buildIndexMap();
54751         }
54752         return this.indexMap.colToData;
54753     },
54754     
54755     getColumnIndexByDataIndex : function(dataIndex){
54756         if(!this.indexMap){
54757             this.indexMap = this.buildIndexMap();
54758         }
54759         return this.indexMap.dataToCol[dataIndex];
54760     },
54761     
54762     /**
54763      * Set a css style for a column dynamically. 
54764      * @param {Number} colIndex The index of the column
54765      * @param {String} name The css property name
54766      * @param {String} value The css value
54767      */
54768     setCSSStyle : function(colIndex, name, value){
54769         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54770         Roo.util.CSS.updateRule(selector, name, value);
54771     },
54772     
54773     generateRules : function(cm){
54774         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54775         Roo.util.CSS.removeStyleSheet(rulesId);
54776         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54777             var cid = cm.getColumnId(i);
54778             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54779                          this.tdSelector, cid, " {\n}\n",
54780                          this.hdSelector, cid, " {\n}\n",
54781                          this.splitSelector, cid, " {\n}\n");
54782         }
54783         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54784     }
54785 });/*
54786  * Based on:
54787  * Ext JS Library 1.1.1
54788  * Copyright(c) 2006-2007, Ext JS, LLC.
54789  *
54790  * Originally Released Under LGPL - original licence link has changed is not relivant.
54791  *
54792  * Fork - LGPL
54793  * <script type="text/javascript">
54794  */
54795
54796 // private
54797 // This is a support class used internally by the Grid components
54798 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54799     this.grid = grid;
54800     this.view = grid.getView();
54801     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54802     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54803     if(hd2){
54804         this.setHandleElId(Roo.id(hd));
54805         this.setOuterHandleElId(Roo.id(hd2));
54806     }
54807     this.scroll = false;
54808 };
54809 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54810     maxDragWidth: 120,
54811     getDragData : function(e){
54812         var t = Roo.lib.Event.getTarget(e);
54813         var h = this.view.findHeaderCell(t);
54814         if(h){
54815             return {ddel: h.firstChild, header:h};
54816         }
54817         return false;
54818     },
54819
54820     onInitDrag : function(e){
54821         this.view.headersDisabled = true;
54822         var clone = this.dragData.ddel.cloneNode(true);
54823         clone.id = Roo.id();
54824         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54825         this.proxy.update(clone);
54826         return true;
54827     },
54828
54829     afterValidDrop : function(){
54830         var v = this.view;
54831         setTimeout(function(){
54832             v.headersDisabled = false;
54833         }, 50);
54834     },
54835
54836     afterInvalidDrop : function(){
54837         var v = this.view;
54838         setTimeout(function(){
54839             v.headersDisabled = false;
54840         }, 50);
54841     }
54842 });
54843 /*
54844  * Based on:
54845  * Ext JS Library 1.1.1
54846  * Copyright(c) 2006-2007, Ext JS, LLC.
54847  *
54848  * Originally Released Under LGPL - original licence link has changed is not relivant.
54849  *
54850  * Fork - LGPL
54851  * <script type="text/javascript">
54852  */
54853 // private
54854 // This is a support class used internally by the Grid components
54855 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54856     this.grid = grid;
54857     this.view = grid.getView();
54858     // split the proxies so they don't interfere with mouse events
54859     this.proxyTop = Roo.DomHelper.append(document.body, {
54860         cls:"col-move-top", html:"&#160;"
54861     }, true);
54862     this.proxyBottom = Roo.DomHelper.append(document.body, {
54863         cls:"col-move-bottom", html:"&#160;"
54864     }, true);
54865     this.proxyTop.hide = this.proxyBottom.hide = function(){
54866         this.setLeftTop(-100,-100);
54867         this.setStyle("visibility", "hidden");
54868     };
54869     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54870     // temporarily disabled
54871     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54872     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54873 };
54874 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54875     proxyOffsets : [-4, -9],
54876     fly: Roo.Element.fly,
54877
54878     getTargetFromEvent : function(e){
54879         var t = Roo.lib.Event.getTarget(e);
54880         var cindex = this.view.findCellIndex(t);
54881         if(cindex !== false){
54882             return this.view.getHeaderCell(cindex);
54883         }
54884         return null;
54885     },
54886
54887     nextVisible : function(h){
54888         var v = this.view, cm = this.grid.colModel;
54889         h = h.nextSibling;
54890         while(h){
54891             if(!cm.isHidden(v.getCellIndex(h))){
54892                 return h;
54893             }
54894             h = h.nextSibling;
54895         }
54896         return null;
54897     },
54898
54899     prevVisible : function(h){
54900         var v = this.view, cm = this.grid.colModel;
54901         h = h.prevSibling;
54902         while(h){
54903             if(!cm.isHidden(v.getCellIndex(h))){
54904                 return h;
54905             }
54906             h = h.prevSibling;
54907         }
54908         return null;
54909     },
54910
54911     positionIndicator : function(h, n, e){
54912         var x = Roo.lib.Event.getPageX(e);
54913         var r = Roo.lib.Dom.getRegion(n.firstChild);
54914         var px, pt, py = r.top + this.proxyOffsets[1];
54915         if((r.right - x) <= (r.right-r.left)/2){
54916             px = r.right+this.view.borderWidth;
54917             pt = "after";
54918         }else{
54919             px = r.left;
54920             pt = "before";
54921         }
54922         var oldIndex = this.view.getCellIndex(h);
54923         var newIndex = this.view.getCellIndex(n);
54924
54925         if(this.grid.colModel.isFixed(newIndex)){
54926             return false;
54927         }
54928
54929         var locked = this.grid.colModel.isLocked(newIndex);
54930
54931         if(pt == "after"){
54932             newIndex++;
54933         }
54934         if(oldIndex < newIndex){
54935             newIndex--;
54936         }
54937         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54938             return false;
54939         }
54940         px +=  this.proxyOffsets[0];
54941         this.proxyTop.setLeftTop(px, py);
54942         this.proxyTop.show();
54943         if(!this.bottomOffset){
54944             this.bottomOffset = this.view.mainHd.getHeight();
54945         }
54946         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54947         this.proxyBottom.show();
54948         return pt;
54949     },
54950
54951     onNodeEnter : function(n, dd, e, data){
54952         if(data.header != n){
54953             this.positionIndicator(data.header, n, e);
54954         }
54955     },
54956
54957     onNodeOver : function(n, dd, e, data){
54958         var result = false;
54959         if(data.header != n){
54960             result = this.positionIndicator(data.header, n, e);
54961         }
54962         if(!result){
54963             this.proxyTop.hide();
54964             this.proxyBottom.hide();
54965         }
54966         return result ? this.dropAllowed : this.dropNotAllowed;
54967     },
54968
54969     onNodeOut : function(n, dd, e, data){
54970         this.proxyTop.hide();
54971         this.proxyBottom.hide();
54972     },
54973
54974     onNodeDrop : function(n, dd, e, data){
54975         var h = data.header;
54976         if(h != n){
54977             var cm = this.grid.colModel;
54978             var x = Roo.lib.Event.getPageX(e);
54979             var r = Roo.lib.Dom.getRegion(n.firstChild);
54980             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54981             var oldIndex = this.view.getCellIndex(h);
54982             var newIndex = this.view.getCellIndex(n);
54983             var locked = cm.isLocked(newIndex);
54984             if(pt == "after"){
54985                 newIndex++;
54986             }
54987             if(oldIndex < newIndex){
54988                 newIndex--;
54989             }
54990             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54991                 return false;
54992             }
54993             cm.setLocked(oldIndex, locked, true);
54994             cm.moveColumn(oldIndex, newIndex);
54995             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54996             return true;
54997         }
54998         return false;
54999     }
55000 });
55001 /*
55002  * Based on:
55003  * Ext JS Library 1.1.1
55004  * Copyright(c) 2006-2007, Ext JS, LLC.
55005  *
55006  * Originally Released Under LGPL - original licence link has changed is not relivant.
55007  *
55008  * Fork - LGPL
55009  * <script type="text/javascript">
55010  */
55011   
55012 /**
55013  * @class Roo.grid.GridView
55014  * @extends Roo.util.Observable
55015  *
55016  * @constructor
55017  * @param {Object} config
55018  */
55019 Roo.grid.GridView = function(config){
55020     Roo.grid.GridView.superclass.constructor.call(this);
55021     this.el = null;
55022
55023     Roo.apply(this, config);
55024 };
55025
55026 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55027
55028     unselectable :  'unselectable="on"',
55029     unselectableCls :  'x-unselectable',
55030     
55031     
55032     rowClass : "x-grid-row",
55033
55034     cellClass : "x-grid-col",
55035
55036     tdClass : "x-grid-td",
55037
55038     hdClass : "x-grid-hd",
55039
55040     splitClass : "x-grid-split",
55041
55042     sortClasses : ["sort-asc", "sort-desc"],
55043
55044     enableMoveAnim : false,
55045
55046     hlColor: "C3DAF9",
55047
55048     dh : Roo.DomHelper,
55049
55050     fly : Roo.Element.fly,
55051
55052     css : Roo.util.CSS,
55053
55054     borderWidth: 1,
55055
55056     splitOffset: 3,
55057
55058     scrollIncrement : 22,
55059
55060     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55061
55062     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55063
55064     bind : function(ds, cm){
55065         if(this.ds){
55066             this.ds.un("load", this.onLoad, this);
55067             this.ds.un("datachanged", this.onDataChange, this);
55068             this.ds.un("add", this.onAdd, this);
55069             this.ds.un("remove", this.onRemove, this);
55070             this.ds.un("update", this.onUpdate, this);
55071             this.ds.un("clear", this.onClear, this);
55072         }
55073         if(ds){
55074             ds.on("load", this.onLoad, this);
55075             ds.on("datachanged", this.onDataChange, this);
55076             ds.on("add", this.onAdd, this);
55077             ds.on("remove", this.onRemove, this);
55078             ds.on("update", this.onUpdate, this);
55079             ds.on("clear", this.onClear, this);
55080         }
55081         this.ds = ds;
55082
55083         if(this.cm){
55084             this.cm.un("widthchange", this.onColWidthChange, this);
55085             this.cm.un("headerchange", this.onHeaderChange, this);
55086             this.cm.un("hiddenchange", this.onHiddenChange, this);
55087             this.cm.un("columnmoved", this.onColumnMove, this);
55088             this.cm.un("columnlockchange", this.onColumnLock, this);
55089         }
55090         if(cm){
55091             this.generateRules(cm);
55092             cm.on("widthchange", this.onColWidthChange, this);
55093             cm.on("headerchange", this.onHeaderChange, this);
55094             cm.on("hiddenchange", this.onHiddenChange, this);
55095             cm.on("columnmoved", this.onColumnMove, this);
55096             cm.on("columnlockchange", this.onColumnLock, this);
55097         }
55098         this.cm = cm;
55099     },
55100
55101     init: function(grid){
55102         Roo.grid.GridView.superclass.init.call(this, grid);
55103
55104         this.bind(grid.dataSource, grid.colModel);
55105
55106         grid.on("headerclick", this.handleHeaderClick, this);
55107
55108         if(grid.trackMouseOver){
55109             grid.on("mouseover", this.onRowOver, this);
55110             grid.on("mouseout", this.onRowOut, this);
55111         }
55112         grid.cancelTextSelection = function(){};
55113         this.gridId = grid.id;
55114
55115         var tpls = this.templates || {};
55116
55117         if(!tpls.master){
55118             tpls.master = new Roo.Template(
55119                '<div class="x-grid" hidefocus="true">',
55120                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55121                   '<div class="x-grid-topbar"></div>',
55122                   '<div class="x-grid-scroller"><div></div></div>',
55123                   '<div class="x-grid-locked">',
55124                       '<div class="x-grid-header">{lockedHeader}</div>',
55125                       '<div class="x-grid-body">{lockedBody}</div>',
55126                   "</div>",
55127                   '<div class="x-grid-viewport">',
55128                       '<div class="x-grid-header">{header}</div>',
55129                       '<div class="x-grid-body">{body}</div>',
55130                   "</div>",
55131                   '<div class="x-grid-bottombar"></div>',
55132                  
55133                   '<div class="x-grid-resize-proxy">&#160;</div>',
55134                "</div>"
55135             );
55136             tpls.master.disableformats = true;
55137         }
55138
55139         if(!tpls.header){
55140             tpls.header = new Roo.Template(
55141                '<table border="0" cellspacing="0" cellpadding="0">',
55142                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55143                "</table>{splits}"
55144             );
55145             tpls.header.disableformats = true;
55146         }
55147         tpls.header.compile();
55148
55149         if(!tpls.hcell){
55150             tpls.hcell = new Roo.Template(
55151                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55152                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55153                 "</div></td>"
55154              );
55155              tpls.hcell.disableFormats = true;
55156         }
55157         tpls.hcell.compile();
55158
55159         if(!tpls.hsplit){
55160             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55161                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55162             tpls.hsplit.disableFormats = true;
55163         }
55164         tpls.hsplit.compile();
55165
55166         if(!tpls.body){
55167             tpls.body = new Roo.Template(
55168                '<table border="0" cellspacing="0" cellpadding="0">',
55169                "<tbody>{rows}</tbody>",
55170                "</table>"
55171             );
55172             tpls.body.disableFormats = true;
55173         }
55174         tpls.body.compile();
55175
55176         if(!tpls.row){
55177             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55178             tpls.row.disableFormats = true;
55179         }
55180         tpls.row.compile();
55181
55182         if(!tpls.cell){
55183             tpls.cell = new Roo.Template(
55184                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55185                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55186                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55187                 "</td>"
55188             );
55189             tpls.cell.disableFormats = true;
55190         }
55191         tpls.cell.compile();
55192
55193         this.templates = tpls;
55194     },
55195
55196     // remap these for backwards compat
55197     onColWidthChange : function(){
55198         this.updateColumns.apply(this, arguments);
55199     },
55200     onHeaderChange : function(){
55201         this.updateHeaders.apply(this, arguments);
55202     }, 
55203     onHiddenChange : function(){
55204         this.handleHiddenChange.apply(this, arguments);
55205     },
55206     onColumnMove : function(){
55207         this.handleColumnMove.apply(this, arguments);
55208     },
55209     onColumnLock : function(){
55210         this.handleLockChange.apply(this, arguments);
55211     },
55212
55213     onDataChange : function(){
55214         this.refresh();
55215         this.updateHeaderSortState();
55216     },
55217
55218     onClear : function(){
55219         this.refresh();
55220     },
55221
55222     onUpdate : function(ds, record){
55223         this.refreshRow(record);
55224     },
55225
55226     refreshRow : function(record){
55227         var ds = this.ds, index;
55228         if(typeof record == 'number'){
55229             index = record;
55230             record = ds.getAt(index);
55231         }else{
55232             index = ds.indexOf(record);
55233         }
55234         this.insertRows(ds, index, index, true);
55235         this.onRemove(ds, record, index+1, true);
55236         this.syncRowHeights(index, index);
55237         this.layout();
55238         this.fireEvent("rowupdated", this, index, record);
55239     },
55240
55241     onAdd : function(ds, records, index){
55242         this.insertRows(ds, index, index + (records.length-1));
55243     },
55244
55245     onRemove : function(ds, record, index, isUpdate){
55246         if(isUpdate !== true){
55247             this.fireEvent("beforerowremoved", this, index, record);
55248         }
55249         var bt = this.getBodyTable(), lt = this.getLockedTable();
55250         if(bt.rows[index]){
55251             bt.firstChild.removeChild(bt.rows[index]);
55252         }
55253         if(lt.rows[index]){
55254             lt.firstChild.removeChild(lt.rows[index]);
55255         }
55256         if(isUpdate !== true){
55257             this.stripeRows(index);
55258             this.syncRowHeights(index, index);
55259             this.layout();
55260             this.fireEvent("rowremoved", this, index, record);
55261         }
55262     },
55263
55264     onLoad : function(){
55265         this.scrollToTop();
55266     },
55267
55268     /**
55269      * Scrolls the grid to the top
55270      */
55271     scrollToTop : function(){
55272         if(this.scroller){
55273             this.scroller.dom.scrollTop = 0;
55274             this.syncScroll();
55275         }
55276     },
55277
55278     /**
55279      * Gets a panel in the header of the grid that can be used for toolbars etc.
55280      * After modifying the contents of this panel a call to grid.autoSize() may be
55281      * required to register any changes in size.
55282      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55283      * @return Roo.Element
55284      */
55285     getHeaderPanel : function(doShow){
55286         if(doShow){
55287             this.headerPanel.show();
55288         }
55289         return this.headerPanel;
55290     },
55291
55292     /**
55293      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55294      * After modifying the contents of this panel a call to grid.autoSize() may be
55295      * required to register any changes in size.
55296      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55297      * @return Roo.Element
55298      */
55299     getFooterPanel : function(doShow){
55300         if(doShow){
55301             this.footerPanel.show();
55302         }
55303         return this.footerPanel;
55304     },
55305
55306     initElements : function(){
55307         var E = Roo.Element;
55308         var el = this.grid.getGridEl().dom.firstChild;
55309         var cs = el.childNodes;
55310
55311         this.el = new E(el);
55312         
55313          this.focusEl = new E(el.firstChild);
55314         this.focusEl.swallowEvent("click", true);
55315         
55316         this.headerPanel = new E(cs[1]);
55317         this.headerPanel.enableDisplayMode("block");
55318
55319         this.scroller = new E(cs[2]);
55320         this.scrollSizer = new E(this.scroller.dom.firstChild);
55321
55322         this.lockedWrap = new E(cs[3]);
55323         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55324         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55325
55326         this.mainWrap = new E(cs[4]);
55327         this.mainHd = new E(this.mainWrap.dom.firstChild);
55328         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55329
55330         this.footerPanel = new E(cs[5]);
55331         this.footerPanel.enableDisplayMode("block");
55332
55333         this.resizeProxy = new E(cs[6]);
55334
55335         this.headerSelector = String.format(
55336            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55337            this.lockedHd.id, this.mainHd.id
55338         );
55339
55340         this.splitterSelector = String.format(
55341            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55342            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55343         );
55344     },
55345     idToCssName : function(s)
55346     {
55347         return s.replace(/[^a-z0-9]+/ig, '-');
55348     },
55349
55350     getHeaderCell : function(index){
55351         return Roo.DomQuery.select(this.headerSelector)[index];
55352     },
55353
55354     getHeaderCellMeasure : function(index){
55355         return this.getHeaderCell(index).firstChild;
55356     },
55357
55358     getHeaderCellText : function(index){
55359         return this.getHeaderCell(index).firstChild.firstChild;
55360     },
55361
55362     getLockedTable : function(){
55363         return this.lockedBody.dom.firstChild;
55364     },
55365
55366     getBodyTable : function(){
55367         return this.mainBody.dom.firstChild;
55368     },
55369
55370     getLockedRow : function(index){
55371         return this.getLockedTable().rows[index];
55372     },
55373
55374     getRow : function(index){
55375         return this.getBodyTable().rows[index];
55376     },
55377
55378     getRowComposite : function(index){
55379         if(!this.rowEl){
55380             this.rowEl = new Roo.CompositeElementLite();
55381         }
55382         var els = [], lrow, mrow;
55383         if(lrow = this.getLockedRow(index)){
55384             els.push(lrow);
55385         }
55386         if(mrow = this.getRow(index)){
55387             els.push(mrow);
55388         }
55389         this.rowEl.elements = els;
55390         return this.rowEl;
55391     },
55392     /**
55393      * Gets the 'td' of the cell
55394      * 
55395      * @param {Integer} rowIndex row to select
55396      * @param {Integer} colIndex column to select
55397      * 
55398      * @return {Object} 
55399      */
55400     getCell : function(rowIndex, colIndex){
55401         var locked = this.cm.getLockedCount();
55402         var source;
55403         if(colIndex < locked){
55404             source = this.lockedBody.dom.firstChild;
55405         }else{
55406             source = this.mainBody.dom.firstChild;
55407             colIndex -= locked;
55408         }
55409         return source.rows[rowIndex].childNodes[colIndex];
55410     },
55411
55412     getCellText : function(rowIndex, colIndex){
55413         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55414     },
55415
55416     getCellBox : function(cell){
55417         var b = this.fly(cell).getBox();
55418         if(Roo.isOpera){ // opera fails to report the Y
55419             b.y = cell.offsetTop + this.mainBody.getY();
55420         }
55421         return b;
55422     },
55423
55424     getCellIndex : function(cell){
55425         var id = String(cell.className).match(this.cellRE);
55426         if(id){
55427             return parseInt(id[1], 10);
55428         }
55429         return 0;
55430     },
55431
55432     findHeaderIndex : function(n){
55433         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55434         return r ? this.getCellIndex(r) : false;
55435     },
55436
55437     findHeaderCell : function(n){
55438         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55439         return r ? r : false;
55440     },
55441
55442     findRowIndex : function(n){
55443         if(!n){
55444             return false;
55445         }
55446         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55447         return r ? r.rowIndex : false;
55448     },
55449
55450     findCellIndex : function(node){
55451         var stop = this.el.dom;
55452         while(node && node != stop){
55453             if(this.findRE.test(node.className)){
55454                 return this.getCellIndex(node);
55455             }
55456             node = node.parentNode;
55457         }
55458         return false;
55459     },
55460
55461     getColumnId : function(index){
55462         return this.cm.getColumnId(index);
55463     },
55464
55465     getSplitters : function()
55466     {
55467         if(this.splitterSelector){
55468            return Roo.DomQuery.select(this.splitterSelector);
55469         }else{
55470             return null;
55471       }
55472     },
55473
55474     getSplitter : function(index){
55475         return this.getSplitters()[index];
55476     },
55477
55478     onRowOver : function(e, t){
55479         var row;
55480         if((row = this.findRowIndex(t)) !== false){
55481             this.getRowComposite(row).addClass("x-grid-row-over");
55482         }
55483     },
55484
55485     onRowOut : function(e, t){
55486         var row;
55487         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55488             this.getRowComposite(row).removeClass("x-grid-row-over");
55489         }
55490     },
55491
55492     renderHeaders : function(){
55493         var cm = this.cm;
55494         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55495         var cb = [], lb = [], sb = [], lsb = [], p = {};
55496         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55497             p.cellId = "x-grid-hd-0-" + i;
55498             p.splitId = "x-grid-csplit-0-" + i;
55499             p.id = cm.getColumnId(i);
55500             p.value = cm.getColumnHeader(i) || "";
55501             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55502             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55503             if(!cm.isLocked(i)){
55504                 cb[cb.length] = ct.apply(p);
55505                 sb[sb.length] = st.apply(p);
55506             }else{
55507                 lb[lb.length] = ct.apply(p);
55508                 lsb[lsb.length] = st.apply(p);
55509             }
55510         }
55511         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55512                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55513     },
55514
55515     updateHeaders : function(){
55516         var html = this.renderHeaders();
55517         this.lockedHd.update(html[0]);
55518         this.mainHd.update(html[1]);
55519     },
55520
55521     /**
55522      * Focuses the specified row.
55523      * @param {Number} row The row index
55524      */
55525     focusRow : function(row)
55526     {
55527         //Roo.log('GridView.focusRow');
55528         var x = this.scroller.dom.scrollLeft;
55529         this.focusCell(row, 0, false);
55530         this.scroller.dom.scrollLeft = x;
55531     },
55532
55533     /**
55534      * Focuses the specified cell.
55535      * @param {Number} row The row index
55536      * @param {Number} col The column index
55537      * @param {Boolean} hscroll false to disable horizontal scrolling
55538      */
55539     focusCell : function(row, col, hscroll)
55540     {
55541         //Roo.log('GridView.focusCell');
55542         var el = this.ensureVisible(row, col, hscroll);
55543         this.focusEl.alignTo(el, "tl-tl");
55544         if(Roo.isGecko){
55545             this.focusEl.focus();
55546         }else{
55547             this.focusEl.focus.defer(1, this.focusEl);
55548         }
55549     },
55550
55551     /**
55552      * Scrolls the specified cell into view
55553      * @param {Number} row The row index
55554      * @param {Number} col The column index
55555      * @param {Boolean} hscroll false to disable horizontal scrolling
55556      */
55557     ensureVisible : function(row, col, hscroll)
55558     {
55559         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55560         //return null; //disable for testing.
55561         if(typeof row != "number"){
55562             row = row.rowIndex;
55563         }
55564         if(row < 0 && row >= this.ds.getCount()){
55565             return  null;
55566         }
55567         col = (col !== undefined ? col : 0);
55568         var cm = this.grid.colModel;
55569         while(cm.isHidden(col)){
55570             col++;
55571         }
55572
55573         var el = this.getCell(row, col);
55574         if(!el){
55575             return null;
55576         }
55577         var c = this.scroller.dom;
55578
55579         var ctop = parseInt(el.offsetTop, 10);
55580         var cleft = parseInt(el.offsetLeft, 10);
55581         var cbot = ctop + el.offsetHeight;
55582         var cright = cleft + el.offsetWidth;
55583         
55584         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55585         var stop = parseInt(c.scrollTop, 10);
55586         var sleft = parseInt(c.scrollLeft, 10);
55587         var sbot = stop + ch;
55588         var sright = sleft + c.clientWidth;
55589         /*
55590         Roo.log('GridView.ensureVisible:' +
55591                 ' ctop:' + ctop +
55592                 ' c.clientHeight:' + c.clientHeight +
55593                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55594                 ' stop:' + stop +
55595                 ' cbot:' + cbot +
55596                 ' sbot:' + sbot +
55597                 ' ch:' + ch  
55598                 );
55599         */
55600         if(ctop < stop){
55601              c.scrollTop = ctop;
55602             //Roo.log("set scrolltop to ctop DISABLE?");
55603         }else if(cbot > sbot){
55604             //Roo.log("set scrolltop to cbot-ch");
55605             c.scrollTop = cbot-ch;
55606         }
55607         
55608         if(hscroll !== false){
55609             if(cleft < sleft){
55610                 c.scrollLeft = cleft;
55611             }else if(cright > sright){
55612                 c.scrollLeft = cright-c.clientWidth;
55613             }
55614         }
55615          
55616         return el;
55617     },
55618
55619     updateColumns : function(){
55620         this.grid.stopEditing();
55621         var cm = this.grid.colModel, colIds = this.getColumnIds();
55622         //var totalWidth = cm.getTotalWidth();
55623         var pos = 0;
55624         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55625             //if(cm.isHidden(i)) continue;
55626             var w = cm.getColumnWidth(i);
55627             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55628             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55629         }
55630         this.updateSplitters();
55631     },
55632
55633     generateRules : function(cm){
55634         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55635         Roo.util.CSS.removeStyleSheet(rulesId);
55636         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55637             var cid = cm.getColumnId(i);
55638             var align = '';
55639             if(cm.config[i].align){
55640                 align = 'text-align:'+cm.config[i].align+';';
55641             }
55642             var hidden = '';
55643             if(cm.isHidden(i)){
55644                 hidden = 'display:none;';
55645             }
55646             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55647             ruleBuf.push(
55648                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55649                     this.hdSelector, cid, " {\n", align, width, "}\n",
55650                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55651                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55652         }
55653         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55654     },
55655
55656     updateSplitters : function(){
55657         var cm = this.cm, s = this.getSplitters();
55658         if(s){ // splitters not created yet
55659             var pos = 0, locked = true;
55660             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55661                 if(cm.isHidden(i)) {
55662                     continue;
55663                 }
55664                 var w = cm.getColumnWidth(i); // make sure it's a number
55665                 if(!cm.isLocked(i) && locked){
55666                     pos = 0;
55667                     locked = false;
55668                 }
55669                 pos += w;
55670                 s[i].style.left = (pos-this.splitOffset) + "px";
55671             }
55672         }
55673     },
55674
55675     handleHiddenChange : function(colModel, colIndex, hidden){
55676         if(hidden){
55677             this.hideColumn(colIndex);
55678         }else{
55679             this.unhideColumn(colIndex);
55680         }
55681     },
55682
55683     hideColumn : function(colIndex){
55684         var cid = this.getColumnId(colIndex);
55685         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55686         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55687         if(Roo.isSafari){
55688             this.updateHeaders();
55689         }
55690         this.updateSplitters();
55691         this.layout();
55692     },
55693
55694     unhideColumn : function(colIndex){
55695         var cid = this.getColumnId(colIndex);
55696         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55697         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55698
55699         if(Roo.isSafari){
55700             this.updateHeaders();
55701         }
55702         this.updateSplitters();
55703         this.layout();
55704     },
55705
55706     insertRows : function(dm, firstRow, lastRow, isUpdate){
55707         if(firstRow == 0 && lastRow == dm.getCount()-1){
55708             this.refresh();
55709         }else{
55710             if(!isUpdate){
55711                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55712             }
55713             var s = this.getScrollState();
55714             var markup = this.renderRows(firstRow, lastRow);
55715             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55716             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55717             this.restoreScroll(s);
55718             if(!isUpdate){
55719                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55720                 this.syncRowHeights(firstRow, lastRow);
55721                 this.stripeRows(firstRow);
55722                 this.layout();
55723             }
55724         }
55725     },
55726
55727     bufferRows : function(markup, target, index){
55728         var before = null, trows = target.rows, tbody = target.tBodies[0];
55729         if(index < trows.length){
55730             before = trows[index];
55731         }
55732         var b = document.createElement("div");
55733         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55734         var rows = b.firstChild.rows;
55735         for(var i = 0, len = rows.length; i < len; i++){
55736             if(before){
55737                 tbody.insertBefore(rows[0], before);
55738             }else{
55739                 tbody.appendChild(rows[0]);
55740             }
55741         }
55742         b.innerHTML = "";
55743         b = null;
55744     },
55745
55746     deleteRows : function(dm, firstRow, lastRow){
55747         if(dm.getRowCount()<1){
55748             this.fireEvent("beforerefresh", this);
55749             this.mainBody.update("");
55750             this.lockedBody.update("");
55751             this.fireEvent("refresh", this);
55752         }else{
55753             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55754             var bt = this.getBodyTable();
55755             var tbody = bt.firstChild;
55756             var rows = bt.rows;
55757             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55758                 tbody.removeChild(rows[firstRow]);
55759             }
55760             this.stripeRows(firstRow);
55761             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55762         }
55763     },
55764
55765     updateRows : function(dataSource, firstRow, lastRow){
55766         var s = this.getScrollState();
55767         this.refresh();
55768         this.restoreScroll(s);
55769     },
55770
55771     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55772         if(!noRefresh){
55773            this.refresh();
55774         }
55775         this.updateHeaderSortState();
55776     },
55777
55778     getScrollState : function(){
55779         
55780         var sb = this.scroller.dom;
55781         return {left: sb.scrollLeft, top: sb.scrollTop};
55782     },
55783
55784     stripeRows : function(startRow){
55785         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55786             return;
55787         }
55788         startRow = startRow || 0;
55789         var rows = this.getBodyTable().rows;
55790         var lrows = this.getLockedTable().rows;
55791         var cls = ' x-grid-row-alt ';
55792         for(var i = startRow, len = rows.length; i < len; i++){
55793             var row = rows[i], lrow = lrows[i];
55794             var isAlt = ((i+1) % 2 == 0);
55795             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55796             if(isAlt == hasAlt){
55797                 continue;
55798             }
55799             if(isAlt){
55800                 row.className += " x-grid-row-alt";
55801             }else{
55802                 row.className = row.className.replace("x-grid-row-alt", "");
55803             }
55804             if(lrow){
55805                 lrow.className = row.className;
55806             }
55807         }
55808     },
55809
55810     restoreScroll : function(state){
55811         //Roo.log('GridView.restoreScroll');
55812         var sb = this.scroller.dom;
55813         sb.scrollLeft = state.left;
55814         sb.scrollTop = state.top;
55815         this.syncScroll();
55816     },
55817
55818     syncScroll : function(){
55819         //Roo.log('GridView.syncScroll');
55820         var sb = this.scroller.dom;
55821         var sh = this.mainHd.dom;
55822         var bs = this.mainBody.dom;
55823         var lv = this.lockedBody.dom;
55824         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55825         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55826     },
55827
55828     handleScroll : function(e){
55829         this.syncScroll();
55830         var sb = this.scroller.dom;
55831         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55832         e.stopEvent();
55833     },
55834
55835     handleWheel : function(e){
55836         var d = e.getWheelDelta();
55837         this.scroller.dom.scrollTop -= d*22;
55838         // set this here to prevent jumpy scrolling on large tables
55839         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55840         e.stopEvent();
55841     },
55842
55843     renderRows : function(startRow, endRow){
55844         // pull in all the crap needed to render rows
55845         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55846         var colCount = cm.getColumnCount();
55847
55848         if(ds.getCount() < 1){
55849             return ["", ""];
55850         }
55851
55852         // build a map for all the columns
55853         var cs = [];
55854         for(var i = 0; i < colCount; i++){
55855             var name = cm.getDataIndex(i);
55856             cs[i] = {
55857                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55858                 renderer : cm.getRenderer(i),
55859                 id : cm.getColumnId(i),
55860                 locked : cm.isLocked(i),
55861                 has_editor : cm.isCellEditable(i)
55862             };
55863         }
55864
55865         startRow = startRow || 0;
55866         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55867
55868         // records to render
55869         var rs = ds.getRange(startRow, endRow);
55870
55871         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55872     },
55873
55874     // As much as I hate to duplicate code, this was branched because FireFox really hates
55875     // [].join("") on strings. The performance difference was substantial enough to
55876     // branch this function
55877     doRender : Roo.isGecko ?
55878             function(cs, rs, ds, startRow, colCount, stripe){
55879                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55880                 // buffers
55881                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55882                 
55883                 var hasListener = this.grid.hasListener('rowclass');
55884                 var rowcfg = {};
55885                 for(var j = 0, len = rs.length; j < len; j++){
55886                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55887                     for(var i = 0; i < colCount; i++){
55888                         c = cs[i];
55889                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55890                         p.id = c.id;
55891                         p.css = p.attr = "";
55892                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55893                         if(p.value == undefined || p.value === "") {
55894                             p.value = "&#160;";
55895                         }
55896                         if(c.has_editor){
55897                             p.css += ' x-grid-editable-cell';
55898                         }
55899                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55900                             p.css +=  ' x-grid-dirty-cell';
55901                         }
55902                         var markup = ct.apply(p);
55903                         if(!c.locked){
55904                             cb+= markup;
55905                         }else{
55906                             lcb+= markup;
55907                         }
55908                     }
55909                     var alt = [];
55910                     if(stripe && ((rowIndex+1) % 2 == 0)){
55911                         alt.push("x-grid-row-alt")
55912                     }
55913                     if(r.dirty){
55914                         alt.push(  " x-grid-dirty-row");
55915                     }
55916                     rp.cells = lcb;
55917                     if(this.getRowClass){
55918                         alt.push(this.getRowClass(r, rowIndex));
55919                     }
55920                     if (hasListener) {
55921                         rowcfg = {
55922                              
55923                             record: r,
55924                             rowIndex : rowIndex,
55925                             rowClass : ''
55926                         };
55927                         this.grid.fireEvent('rowclass', this, rowcfg);
55928                         alt.push(rowcfg.rowClass);
55929                     }
55930                     rp.alt = alt.join(" ");
55931                     lbuf+= rt.apply(rp);
55932                     rp.cells = cb;
55933                     buf+=  rt.apply(rp);
55934                 }
55935                 return [lbuf, buf];
55936             } :
55937             function(cs, rs, ds, startRow, colCount, stripe){
55938                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55939                 // buffers
55940                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55941                 var hasListener = this.grid.hasListener('rowclass');
55942  
55943                 var rowcfg = {};
55944                 for(var j = 0, len = rs.length; j < len; j++){
55945                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55946                     for(var i = 0; i < colCount; i++){
55947                         c = cs[i];
55948                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55949                         p.id = c.id;
55950                         p.css = p.attr = "";
55951                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55952                         if(p.value == undefined || p.value === "") {
55953                             p.value = "&#160;";
55954                         }
55955                         //Roo.log(c);
55956                          if(c.has_editor){
55957                             p.css += ' x-grid-editable-cell';
55958                         }
55959                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55960                             p.css += ' x-grid-dirty-cell' 
55961                         }
55962                         
55963                         var markup = ct.apply(p);
55964                         if(!c.locked){
55965                             cb[cb.length] = markup;
55966                         }else{
55967                             lcb[lcb.length] = markup;
55968                         }
55969                     }
55970                     var alt = [];
55971                     if(stripe && ((rowIndex+1) % 2 == 0)){
55972                         alt.push( "x-grid-row-alt");
55973                     }
55974                     if(r.dirty){
55975                         alt.push(" x-grid-dirty-row");
55976                     }
55977                     rp.cells = lcb;
55978                     if(this.getRowClass){
55979                         alt.push( this.getRowClass(r, rowIndex));
55980                     }
55981                     if (hasListener) {
55982                         rowcfg = {
55983                              
55984                             record: r,
55985                             rowIndex : rowIndex,
55986                             rowClass : ''
55987                         };
55988                         this.grid.fireEvent('rowclass', this, rowcfg);
55989                         alt.push(rowcfg.rowClass);
55990                     }
55991                     
55992                     rp.alt = alt.join(" ");
55993                     rp.cells = lcb.join("");
55994                     lbuf[lbuf.length] = rt.apply(rp);
55995                     rp.cells = cb.join("");
55996                     buf[buf.length] =  rt.apply(rp);
55997                 }
55998                 return [lbuf.join(""), buf.join("")];
55999             },
56000
56001     renderBody : function(){
56002         var markup = this.renderRows();
56003         var bt = this.templates.body;
56004         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56005     },
56006
56007     /**
56008      * Refreshes the grid
56009      * @param {Boolean} headersToo
56010      */
56011     refresh : function(headersToo){
56012         this.fireEvent("beforerefresh", this);
56013         this.grid.stopEditing();
56014         var result = this.renderBody();
56015         this.lockedBody.update(result[0]);
56016         this.mainBody.update(result[1]);
56017         if(headersToo === true){
56018             this.updateHeaders();
56019             this.updateColumns();
56020             this.updateSplitters();
56021             this.updateHeaderSortState();
56022         }
56023         this.syncRowHeights();
56024         this.layout();
56025         this.fireEvent("refresh", this);
56026     },
56027
56028     handleColumnMove : function(cm, oldIndex, newIndex){
56029         this.indexMap = null;
56030         var s = this.getScrollState();
56031         this.refresh(true);
56032         this.restoreScroll(s);
56033         this.afterMove(newIndex);
56034     },
56035
56036     afterMove : function(colIndex){
56037         if(this.enableMoveAnim && Roo.enableFx){
56038             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56039         }
56040         // if multisort - fix sortOrder, and reload..
56041         if (this.grid.dataSource.multiSort) {
56042             // the we can call sort again..
56043             var dm = this.grid.dataSource;
56044             var cm = this.grid.colModel;
56045             var so = [];
56046             for(var i = 0; i < cm.config.length; i++ ) {
56047                 
56048                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56049                     continue; // dont' bother, it's not in sort list or being set.
56050                 }
56051                 
56052                 so.push(cm.config[i].dataIndex);
56053             };
56054             dm.sortOrder = so;
56055             dm.load(dm.lastOptions);
56056             
56057             
56058         }
56059         
56060     },
56061
56062     updateCell : function(dm, rowIndex, dataIndex){
56063         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56064         if(typeof colIndex == "undefined"){ // not present in grid
56065             return;
56066         }
56067         var cm = this.grid.colModel;
56068         var cell = this.getCell(rowIndex, colIndex);
56069         var cellText = this.getCellText(rowIndex, colIndex);
56070
56071         var p = {
56072             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56073             id : cm.getColumnId(colIndex),
56074             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56075         };
56076         var renderer = cm.getRenderer(colIndex);
56077         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56078         if(typeof val == "undefined" || val === "") {
56079             val = "&#160;";
56080         }
56081         cellText.innerHTML = val;
56082         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56083         this.syncRowHeights(rowIndex, rowIndex);
56084     },
56085
56086     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56087         var maxWidth = 0;
56088         if(this.grid.autoSizeHeaders){
56089             var h = this.getHeaderCellMeasure(colIndex);
56090             maxWidth = Math.max(maxWidth, h.scrollWidth);
56091         }
56092         var tb, index;
56093         if(this.cm.isLocked(colIndex)){
56094             tb = this.getLockedTable();
56095             index = colIndex;
56096         }else{
56097             tb = this.getBodyTable();
56098             index = colIndex - this.cm.getLockedCount();
56099         }
56100         if(tb && tb.rows){
56101             var rows = tb.rows;
56102             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56103             for(var i = 0; i < stopIndex; i++){
56104                 var cell = rows[i].childNodes[index].firstChild;
56105                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56106             }
56107         }
56108         return maxWidth + /*margin for error in IE*/ 5;
56109     },
56110     /**
56111      * Autofit a column to its content.
56112      * @param {Number} colIndex
56113      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56114      */
56115      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56116          if(this.cm.isHidden(colIndex)){
56117              return; // can't calc a hidden column
56118          }
56119         if(forceMinSize){
56120             var cid = this.cm.getColumnId(colIndex);
56121             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56122            if(this.grid.autoSizeHeaders){
56123                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56124            }
56125         }
56126         var newWidth = this.calcColumnWidth(colIndex);
56127         this.cm.setColumnWidth(colIndex,
56128             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56129         if(!suppressEvent){
56130             this.grid.fireEvent("columnresize", colIndex, newWidth);
56131         }
56132     },
56133
56134     /**
56135      * Autofits all columns to their content and then expands to fit any extra space in the grid
56136      */
56137      autoSizeColumns : function(){
56138         var cm = this.grid.colModel;
56139         var colCount = cm.getColumnCount();
56140         for(var i = 0; i < colCount; i++){
56141             this.autoSizeColumn(i, true, true);
56142         }
56143         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56144             this.fitColumns();
56145         }else{
56146             this.updateColumns();
56147             this.layout();
56148         }
56149     },
56150
56151     /**
56152      * Autofits all columns to the grid's width proportionate with their current size
56153      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56154      */
56155     fitColumns : function(reserveScrollSpace){
56156         var cm = this.grid.colModel;
56157         var colCount = cm.getColumnCount();
56158         var cols = [];
56159         var width = 0;
56160         var i, w;
56161         for (i = 0; i < colCount; i++){
56162             if(!cm.isHidden(i) && !cm.isFixed(i)){
56163                 w = cm.getColumnWidth(i);
56164                 cols.push(i);
56165                 cols.push(w);
56166                 width += w;
56167             }
56168         }
56169         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56170         if(reserveScrollSpace){
56171             avail -= 17;
56172         }
56173         var frac = (avail - cm.getTotalWidth())/width;
56174         while (cols.length){
56175             w = cols.pop();
56176             i = cols.pop();
56177             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56178         }
56179         this.updateColumns();
56180         this.layout();
56181     },
56182
56183     onRowSelect : function(rowIndex){
56184         var row = this.getRowComposite(rowIndex);
56185         row.addClass("x-grid-row-selected");
56186     },
56187
56188     onRowDeselect : function(rowIndex){
56189         var row = this.getRowComposite(rowIndex);
56190         row.removeClass("x-grid-row-selected");
56191     },
56192
56193     onCellSelect : function(row, col){
56194         var cell = this.getCell(row, col);
56195         if(cell){
56196             Roo.fly(cell).addClass("x-grid-cell-selected");
56197         }
56198     },
56199
56200     onCellDeselect : function(row, col){
56201         var cell = this.getCell(row, col);
56202         if(cell){
56203             Roo.fly(cell).removeClass("x-grid-cell-selected");
56204         }
56205     },
56206
56207     updateHeaderSortState : function(){
56208         
56209         // sort state can be single { field: xxx, direction : yyy}
56210         // or   { xxx=>ASC , yyy : DESC ..... }
56211         
56212         var mstate = {};
56213         if (!this.ds.multiSort) { 
56214             var state = this.ds.getSortState();
56215             if(!state){
56216                 return;
56217             }
56218             mstate[state.field] = state.direction;
56219             // FIXME... - this is not used here.. but might be elsewhere..
56220             this.sortState = state;
56221             
56222         } else {
56223             mstate = this.ds.sortToggle;
56224         }
56225         //remove existing sort classes..
56226         
56227         var sc = this.sortClasses;
56228         var hds = this.el.select(this.headerSelector).removeClass(sc);
56229         
56230         for(var f in mstate) {
56231         
56232             var sortColumn = this.cm.findColumnIndex(f);
56233             
56234             if(sortColumn != -1){
56235                 var sortDir = mstate[f];        
56236                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56237             }
56238         }
56239         
56240          
56241         
56242     },
56243
56244
56245     handleHeaderClick : function(g, index,e){
56246         
56247         Roo.log("header click");
56248         
56249         if (Roo.isTouch) {
56250             // touch events on header are handled by context
56251             this.handleHdCtx(g,index,e);
56252             return;
56253         }
56254         
56255         
56256         if(this.headersDisabled){
56257             return;
56258         }
56259         var dm = g.dataSource, cm = g.colModel;
56260         if(!cm.isSortable(index)){
56261             return;
56262         }
56263         g.stopEditing();
56264         
56265         if (dm.multiSort) {
56266             // update the sortOrder
56267             var so = [];
56268             for(var i = 0; i < cm.config.length; i++ ) {
56269                 
56270                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56271                     continue; // dont' bother, it's not in sort list or being set.
56272                 }
56273                 
56274                 so.push(cm.config[i].dataIndex);
56275             };
56276             dm.sortOrder = so;
56277         }
56278         
56279         
56280         dm.sort(cm.getDataIndex(index));
56281     },
56282
56283
56284     destroy : function(){
56285         if(this.colMenu){
56286             this.colMenu.removeAll();
56287             Roo.menu.MenuMgr.unregister(this.colMenu);
56288             this.colMenu.getEl().remove();
56289             delete this.colMenu;
56290         }
56291         if(this.hmenu){
56292             this.hmenu.removeAll();
56293             Roo.menu.MenuMgr.unregister(this.hmenu);
56294             this.hmenu.getEl().remove();
56295             delete this.hmenu;
56296         }
56297         if(this.grid.enableColumnMove){
56298             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56299             if(dds){
56300                 for(var dd in dds){
56301                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56302                         var elid = dds[dd].dragElId;
56303                         dds[dd].unreg();
56304                         Roo.get(elid).remove();
56305                     } else if(dds[dd].config.isTarget){
56306                         dds[dd].proxyTop.remove();
56307                         dds[dd].proxyBottom.remove();
56308                         dds[dd].unreg();
56309                     }
56310                     if(Roo.dd.DDM.locationCache[dd]){
56311                         delete Roo.dd.DDM.locationCache[dd];
56312                     }
56313                 }
56314                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56315             }
56316         }
56317         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56318         this.bind(null, null);
56319         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56320     },
56321
56322     handleLockChange : function(){
56323         this.refresh(true);
56324     },
56325
56326     onDenyColumnLock : function(){
56327
56328     },
56329
56330     onDenyColumnHide : function(){
56331
56332     },
56333
56334     handleHdMenuClick : function(item){
56335         var index = this.hdCtxIndex;
56336         var cm = this.cm, ds = this.ds;
56337         switch(item.id){
56338             case "asc":
56339                 ds.sort(cm.getDataIndex(index), "ASC");
56340                 break;
56341             case "desc":
56342                 ds.sort(cm.getDataIndex(index), "DESC");
56343                 break;
56344             case "lock":
56345                 var lc = cm.getLockedCount();
56346                 if(cm.getColumnCount(true) <= lc+1){
56347                     this.onDenyColumnLock();
56348                     return;
56349                 }
56350                 if(lc != index){
56351                     cm.setLocked(index, true, true);
56352                     cm.moveColumn(index, lc);
56353                     this.grid.fireEvent("columnmove", index, lc);
56354                 }else{
56355                     cm.setLocked(index, true);
56356                 }
56357             break;
56358             case "unlock":
56359                 var lc = cm.getLockedCount();
56360                 if((lc-1) != index){
56361                     cm.setLocked(index, false, true);
56362                     cm.moveColumn(index, lc-1);
56363                     this.grid.fireEvent("columnmove", index, lc-1);
56364                 }else{
56365                     cm.setLocked(index, false);
56366                 }
56367             break;
56368             case 'wider': // used to expand cols on touch..
56369             case 'narrow':
56370                 var cw = cm.getColumnWidth(index);
56371                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56372                 cw = Math.max(0, cw);
56373                 cw = Math.min(cw,4000);
56374                 cm.setColumnWidth(index, cw);
56375                 break;
56376                 
56377             default:
56378                 index = cm.getIndexById(item.id.substr(4));
56379                 if(index != -1){
56380                     if(item.checked && cm.getColumnCount(true) <= 1){
56381                         this.onDenyColumnHide();
56382                         return false;
56383                     }
56384                     cm.setHidden(index, item.checked);
56385                 }
56386         }
56387         return true;
56388     },
56389
56390     beforeColMenuShow : function(){
56391         var cm = this.cm,  colCount = cm.getColumnCount();
56392         this.colMenu.removeAll();
56393         for(var i = 0; i < colCount; i++){
56394             this.colMenu.add(new Roo.menu.CheckItem({
56395                 id: "col-"+cm.getColumnId(i),
56396                 text: cm.getColumnHeader(i),
56397                 checked: !cm.isHidden(i),
56398                 hideOnClick:false
56399             }));
56400         }
56401     },
56402
56403     handleHdCtx : function(g, index, e){
56404         e.stopEvent();
56405         var hd = this.getHeaderCell(index);
56406         this.hdCtxIndex = index;
56407         var ms = this.hmenu.items, cm = this.cm;
56408         ms.get("asc").setDisabled(!cm.isSortable(index));
56409         ms.get("desc").setDisabled(!cm.isSortable(index));
56410         if(this.grid.enableColLock !== false){
56411             ms.get("lock").setDisabled(cm.isLocked(index));
56412             ms.get("unlock").setDisabled(!cm.isLocked(index));
56413         }
56414         this.hmenu.show(hd, "tl-bl");
56415     },
56416
56417     handleHdOver : function(e){
56418         var hd = this.findHeaderCell(e.getTarget());
56419         if(hd && !this.headersDisabled){
56420             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56421                this.fly(hd).addClass("x-grid-hd-over");
56422             }
56423         }
56424     },
56425
56426     handleHdOut : function(e){
56427         var hd = this.findHeaderCell(e.getTarget());
56428         if(hd){
56429             this.fly(hd).removeClass("x-grid-hd-over");
56430         }
56431     },
56432
56433     handleSplitDblClick : function(e, t){
56434         var i = this.getCellIndex(t);
56435         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56436             this.autoSizeColumn(i, true);
56437             this.layout();
56438         }
56439     },
56440
56441     render : function(){
56442
56443         var cm = this.cm;
56444         var colCount = cm.getColumnCount();
56445
56446         if(this.grid.monitorWindowResize === true){
56447             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56448         }
56449         var header = this.renderHeaders();
56450         var body = this.templates.body.apply({rows:""});
56451         var html = this.templates.master.apply({
56452             lockedBody: body,
56453             body: body,
56454             lockedHeader: header[0],
56455             header: header[1]
56456         });
56457
56458         //this.updateColumns();
56459
56460         this.grid.getGridEl().dom.innerHTML = html;
56461
56462         this.initElements();
56463         
56464         // a kludge to fix the random scolling effect in webkit
56465         this.el.on("scroll", function() {
56466             this.el.dom.scrollTop=0; // hopefully not recursive..
56467         },this);
56468
56469         this.scroller.on("scroll", this.handleScroll, this);
56470         this.lockedBody.on("mousewheel", this.handleWheel, this);
56471         this.mainBody.on("mousewheel", this.handleWheel, this);
56472
56473         this.mainHd.on("mouseover", this.handleHdOver, this);
56474         this.mainHd.on("mouseout", this.handleHdOut, this);
56475         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56476                 {delegate: "."+this.splitClass});
56477
56478         this.lockedHd.on("mouseover", this.handleHdOver, this);
56479         this.lockedHd.on("mouseout", this.handleHdOut, this);
56480         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56481                 {delegate: "."+this.splitClass});
56482
56483         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56484             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56485         }
56486
56487         this.updateSplitters();
56488
56489         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56490             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56491             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56492         }
56493
56494         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56495             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56496             this.hmenu.add(
56497                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56498                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56499             );
56500             if(this.grid.enableColLock !== false){
56501                 this.hmenu.add('-',
56502                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56503                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56504                 );
56505             }
56506             if (Roo.isTouch) {
56507                  this.hmenu.add('-',
56508                     {id:"wider", text: this.columnsWiderText},
56509                     {id:"narrow", text: this.columnsNarrowText }
56510                 );
56511                 
56512                  
56513             }
56514             
56515             if(this.grid.enableColumnHide !== false){
56516
56517                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56518                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56519                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56520
56521                 this.hmenu.add('-',
56522                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56523                 );
56524             }
56525             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56526
56527             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56528         }
56529
56530         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56531             this.dd = new Roo.grid.GridDragZone(this.grid, {
56532                 ddGroup : this.grid.ddGroup || 'GridDD'
56533             });
56534             
56535         }
56536
56537         /*
56538         for(var i = 0; i < colCount; i++){
56539             if(cm.isHidden(i)){
56540                 this.hideColumn(i);
56541             }
56542             if(cm.config[i].align){
56543                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56544                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56545             }
56546         }*/
56547         
56548         this.updateHeaderSortState();
56549
56550         this.beforeInitialResize();
56551         this.layout(true);
56552
56553         // two part rendering gives faster view to the user
56554         this.renderPhase2.defer(1, this);
56555     },
56556
56557     renderPhase2 : function(){
56558         // render the rows now
56559         this.refresh();
56560         if(this.grid.autoSizeColumns){
56561             this.autoSizeColumns();
56562         }
56563     },
56564
56565     beforeInitialResize : function(){
56566
56567     },
56568
56569     onColumnSplitterMoved : function(i, w){
56570         this.userResized = true;
56571         var cm = this.grid.colModel;
56572         cm.setColumnWidth(i, w, true);
56573         var cid = cm.getColumnId(i);
56574         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56575         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56576         this.updateSplitters();
56577         this.layout();
56578         this.grid.fireEvent("columnresize", i, w);
56579     },
56580
56581     syncRowHeights : function(startIndex, endIndex){
56582         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56583             startIndex = startIndex || 0;
56584             var mrows = this.getBodyTable().rows;
56585             var lrows = this.getLockedTable().rows;
56586             var len = mrows.length-1;
56587             endIndex = Math.min(endIndex || len, len);
56588             for(var i = startIndex; i <= endIndex; i++){
56589                 var m = mrows[i], l = lrows[i];
56590                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56591                 m.style.height = l.style.height = h + "px";
56592             }
56593         }
56594     },
56595
56596     layout : function(initialRender, is2ndPass){
56597         var g = this.grid;
56598         var auto = g.autoHeight;
56599         var scrollOffset = 16;
56600         var c = g.getGridEl(), cm = this.cm,
56601                 expandCol = g.autoExpandColumn,
56602                 gv = this;
56603         //c.beginMeasure();
56604
56605         if(!c.dom.offsetWidth){ // display:none?
56606             if(initialRender){
56607                 this.lockedWrap.show();
56608                 this.mainWrap.show();
56609             }
56610             return;
56611         }
56612
56613         var hasLock = this.cm.isLocked(0);
56614
56615         var tbh = this.headerPanel.getHeight();
56616         var bbh = this.footerPanel.getHeight();
56617
56618         if(auto){
56619             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56620             var newHeight = ch + c.getBorderWidth("tb");
56621             if(g.maxHeight){
56622                 newHeight = Math.min(g.maxHeight, newHeight);
56623             }
56624             c.setHeight(newHeight);
56625         }
56626
56627         if(g.autoWidth){
56628             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56629         }
56630
56631         var s = this.scroller;
56632
56633         var csize = c.getSize(true);
56634
56635         this.el.setSize(csize.width, csize.height);
56636
56637         this.headerPanel.setWidth(csize.width);
56638         this.footerPanel.setWidth(csize.width);
56639
56640         var hdHeight = this.mainHd.getHeight();
56641         var vw = csize.width;
56642         var vh = csize.height - (tbh + bbh);
56643
56644         s.setSize(vw, vh);
56645
56646         var bt = this.getBodyTable();
56647         
56648         if(cm.getLockedCount() == cm.config.length){
56649             bt = this.getLockedTable();
56650         }
56651         
56652         var ltWidth = hasLock ?
56653                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56654
56655         var scrollHeight = bt.offsetHeight;
56656         var scrollWidth = ltWidth + bt.offsetWidth;
56657         var vscroll = false, hscroll = false;
56658
56659         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56660
56661         var lw = this.lockedWrap, mw = this.mainWrap;
56662         var lb = this.lockedBody, mb = this.mainBody;
56663
56664         setTimeout(function(){
56665             var t = s.dom.offsetTop;
56666             var w = s.dom.clientWidth,
56667                 h = s.dom.clientHeight;
56668
56669             lw.setTop(t);
56670             lw.setSize(ltWidth, h);
56671
56672             mw.setLeftTop(ltWidth, t);
56673             mw.setSize(w-ltWidth, h);
56674
56675             lb.setHeight(h-hdHeight);
56676             mb.setHeight(h-hdHeight);
56677
56678             if(is2ndPass !== true && !gv.userResized && expandCol){
56679                 // high speed resize without full column calculation
56680                 
56681                 var ci = cm.getIndexById(expandCol);
56682                 if (ci < 0) {
56683                     ci = cm.findColumnIndex(expandCol);
56684                 }
56685                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56686                 var expandId = cm.getColumnId(ci);
56687                 var  tw = cm.getTotalWidth(false);
56688                 var currentWidth = cm.getColumnWidth(ci);
56689                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56690                 if(currentWidth != cw){
56691                     cm.setColumnWidth(ci, cw, true);
56692                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56693                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56694                     gv.updateSplitters();
56695                     gv.layout(false, true);
56696                 }
56697             }
56698
56699             if(initialRender){
56700                 lw.show();
56701                 mw.show();
56702             }
56703             //c.endMeasure();
56704         }, 10);
56705     },
56706
56707     onWindowResize : function(){
56708         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56709             return;
56710         }
56711         this.layout();
56712     },
56713
56714     appendFooter : function(parentEl){
56715         return null;
56716     },
56717
56718     sortAscText : "Sort Ascending",
56719     sortDescText : "Sort Descending",
56720     lockText : "Lock Column",
56721     unlockText : "Unlock Column",
56722     columnsText : "Columns",
56723  
56724     columnsWiderText : "Wider",
56725     columnsNarrowText : "Thinner"
56726 });
56727
56728
56729 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56730     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56731     this.proxy.el.addClass('x-grid3-col-dd');
56732 };
56733
56734 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56735     handleMouseDown : function(e){
56736
56737     },
56738
56739     callHandleMouseDown : function(e){
56740         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56741     }
56742 });
56743 /*
56744  * Based on:
56745  * Ext JS Library 1.1.1
56746  * Copyright(c) 2006-2007, Ext JS, LLC.
56747  *
56748  * Originally Released Under LGPL - original licence link has changed is not relivant.
56749  *
56750  * Fork - LGPL
56751  * <script type="text/javascript">
56752  */
56753  
56754 // private
56755 // This is a support class used internally by the Grid components
56756 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56757     this.grid = grid;
56758     this.view = grid.getView();
56759     this.proxy = this.view.resizeProxy;
56760     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56761         "gridSplitters" + this.grid.getGridEl().id, {
56762         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56763     });
56764     this.setHandleElId(Roo.id(hd));
56765     this.setOuterHandleElId(Roo.id(hd2));
56766     this.scroll = false;
56767 };
56768 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56769     fly: Roo.Element.fly,
56770
56771     b4StartDrag : function(x, y){
56772         this.view.headersDisabled = true;
56773         this.proxy.setHeight(this.view.mainWrap.getHeight());
56774         var w = this.cm.getColumnWidth(this.cellIndex);
56775         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56776         this.resetConstraints();
56777         this.setXConstraint(minw, 1000);
56778         this.setYConstraint(0, 0);
56779         this.minX = x - minw;
56780         this.maxX = x + 1000;
56781         this.startPos = x;
56782         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56783     },
56784
56785
56786     handleMouseDown : function(e){
56787         ev = Roo.EventObject.setEvent(e);
56788         var t = this.fly(ev.getTarget());
56789         if(t.hasClass("x-grid-split")){
56790             this.cellIndex = this.view.getCellIndex(t.dom);
56791             this.split = t.dom;
56792             this.cm = this.grid.colModel;
56793             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56794                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56795             }
56796         }
56797     },
56798
56799     endDrag : function(e){
56800         this.view.headersDisabled = false;
56801         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56802         var diff = endX - this.startPos;
56803         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56804     },
56805
56806     autoOffset : function(){
56807         this.setDelta(0,0);
56808     }
56809 });/*
56810  * Based on:
56811  * Ext JS Library 1.1.1
56812  * Copyright(c) 2006-2007, Ext JS, LLC.
56813  *
56814  * Originally Released Under LGPL - original licence link has changed is not relivant.
56815  *
56816  * Fork - LGPL
56817  * <script type="text/javascript">
56818  */
56819  
56820 // private
56821 // This is a support class used internally by the Grid components
56822 Roo.grid.GridDragZone = function(grid, config){
56823     this.view = grid.getView();
56824     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56825     if(this.view.lockedBody){
56826         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56827         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56828     }
56829     this.scroll = false;
56830     this.grid = grid;
56831     this.ddel = document.createElement('div');
56832     this.ddel.className = 'x-grid-dd-wrap';
56833 };
56834
56835 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56836     ddGroup : "GridDD",
56837
56838     getDragData : function(e){
56839         var t = Roo.lib.Event.getTarget(e);
56840         var rowIndex = this.view.findRowIndex(t);
56841         var sm = this.grid.selModel;
56842             
56843         //Roo.log(rowIndex);
56844         
56845         if (sm.getSelectedCell) {
56846             // cell selection..
56847             if (!sm.getSelectedCell()) {
56848                 return false;
56849             }
56850             if (rowIndex != sm.getSelectedCell()[0]) {
56851                 return false;
56852             }
56853         
56854         }
56855         
56856         if(rowIndex !== false){
56857             
56858             // if editorgrid.. 
56859             
56860             
56861             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56862                
56863             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56864               //  
56865             //}
56866             if (e.hasModifier()){
56867                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56868             }
56869             
56870             Roo.log("getDragData");
56871             
56872             return {
56873                 grid: this.grid,
56874                 ddel: this.ddel,
56875                 rowIndex: rowIndex,
56876                 selections:sm.getSelections ? sm.getSelections() : (
56877                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56878                 )
56879             };
56880         }
56881         return false;
56882     },
56883
56884     onInitDrag : function(e){
56885         var data = this.dragData;
56886         this.ddel.innerHTML = this.grid.getDragDropText();
56887         this.proxy.update(this.ddel);
56888         // fire start drag?
56889     },
56890
56891     afterRepair : function(){
56892         this.dragging = false;
56893     },
56894
56895     getRepairXY : function(e, data){
56896         return false;
56897     },
56898
56899     onEndDrag : function(data, e){
56900         // fire end drag?
56901     },
56902
56903     onValidDrop : function(dd, e, id){
56904         // fire drag drop?
56905         this.hideProxy();
56906     },
56907
56908     beforeInvalidDrop : function(e, id){
56909
56910     }
56911 });/*
56912  * Based on:
56913  * Ext JS Library 1.1.1
56914  * Copyright(c) 2006-2007, Ext JS, LLC.
56915  *
56916  * Originally Released Under LGPL - original licence link has changed is not relivant.
56917  *
56918  * Fork - LGPL
56919  * <script type="text/javascript">
56920  */
56921  
56922
56923 /**
56924  * @class Roo.grid.ColumnModel
56925  * @extends Roo.util.Observable
56926  * This is the default implementation of a ColumnModel used by the Grid. It defines
56927  * the columns in the grid.
56928  * <br>Usage:<br>
56929  <pre><code>
56930  var colModel = new Roo.grid.ColumnModel([
56931         {header: "Ticker", width: 60, sortable: true, locked: true},
56932         {header: "Company Name", width: 150, sortable: true},
56933         {header: "Market Cap.", width: 100, sortable: true},
56934         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56935         {header: "Employees", width: 100, sortable: true, resizable: false}
56936  ]);
56937  </code></pre>
56938  * <p>
56939  
56940  * The config options listed for this class are options which may appear in each
56941  * individual column definition.
56942  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56943  * @constructor
56944  * @param {Object} config An Array of column config objects. See this class's
56945  * config objects for details.
56946 */
56947 Roo.grid.ColumnModel = function(config){
56948         /**
56949      * The config passed into the constructor
56950      */
56951     this.config = config;
56952     this.lookup = {};
56953
56954     // if no id, create one
56955     // if the column does not have a dataIndex mapping,
56956     // map it to the order it is in the config
56957     for(var i = 0, len = config.length; i < len; i++){
56958         var c = config[i];
56959         if(typeof c.dataIndex == "undefined"){
56960             c.dataIndex = i;
56961         }
56962         if(typeof c.renderer == "string"){
56963             c.renderer = Roo.util.Format[c.renderer];
56964         }
56965         if(typeof c.id == "undefined"){
56966             c.id = Roo.id();
56967         }
56968         if(c.editor && c.editor.xtype){
56969             c.editor  = Roo.factory(c.editor, Roo.grid);
56970         }
56971         if(c.editor && c.editor.isFormField){
56972             c.editor = new Roo.grid.GridEditor(c.editor);
56973         }
56974         this.lookup[c.id] = c;
56975     }
56976
56977     /**
56978      * The width of columns which have no width specified (defaults to 100)
56979      * @type Number
56980      */
56981     this.defaultWidth = 100;
56982
56983     /**
56984      * Default sortable of columns which have no sortable specified (defaults to false)
56985      * @type Boolean
56986      */
56987     this.defaultSortable = false;
56988
56989     this.addEvents({
56990         /**
56991              * @event widthchange
56992              * Fires when the width of a column changes.
56993              * @param {ColumnModel} this
56994              * @param {Number} columnIndex The column index
56995              * @param {Number} newWidth The new width
56996              */
56997             "widthchange": true,
56998         /**
56999              * @event headerchange
57000              * Fires when the text of a header changes.
57001              * @param {ColumnModel} this
57002              * @param {Number} columnIndex The column index
57003              * @param {Number} newText The new header text
57004              */
57005             "headerchange": true,
57006         /**
57007              * @event hiddenchange
57008              * Fires when a column is hidden or "unhidden".
57009              * @param {ColumnModel} this
57010              * @param {Number} columnIndex The column index
57011              * @param {Boolean} hidden true if hidden, false otherwise
57012              */
57013             "hiddenchange": true,
57014             /**
57015          * @event columnmoved
57016          * Fires when a column is moved.
57017          * @param {ColumnModel} this
57018          * @param {Number} oldIndex
57019          * @param {Number} newIndex
57020          */
57021         "columnmoved" : true,
57022         /**
57023          * @event columlockchange
57024          * Fires when a column's locked state is changed
57025          * @param {ColumnModel} this
57026          * @param {Number} colIndex
57027          * @param {Boolean} locked true if locked
57028          */
57029         "columnlockchange" : true
57030     });
57031     Roo.grid.ColumnModel.superclass.constructor.call(this);
57032 };
57033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57034     /**
57035      * @cfg {String} header The header text to display in the Grid view.
57036      */
57037     /**
57038      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57039      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57040      * specified, the column's index is used as an index into the Record's data Array.
57041      */
57042     /**
57043      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57044      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57045      */
57046     /**
57047      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57048      * Defaults to the value of the {@link #defaultSortable} property.
57049      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57050      */
57051     /**
57052      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57053      */
57054     /**
57055      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57056      */
57057     /**
57058      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57059      */
57060     /**
57061      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57062      */
57063     /**
57064      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57065      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57066      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57067      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57068      */
57069        /**
57070      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57071      */
57072     /**
57073      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57074      */
57075     /**
57076      * @cfg {String} cursor (Optional)
57077      */
57078     /**
57079      * @cfg {String} tooltip (Optional)
57080      */
57081     /**
57082      * @cfg {Number} xs (Optional)
57083      */
57084     /**
57085      * @cfg {Number} sm (Optional)
57086      */
57087     /**
57088      * @cfg {Number} md (Optional)
57089      */
57090     /**
57091      * @cfg {Number} lg (Optional)
57092      */
57093     /**
57094      * Returns the id of the column at the specified index.
57095      * @param {Number} index The column index
57096      * @return {String} the id
57097      */
57098     getColumnId : function(index){
57099         return this.config[index].id;
57100     },
57101
57102     /**
57103      * Returns the column for a specified id.
57104      * @param {String} id The column id
57105      * @return {Object} the column
57106      */
57107     getColumnById : function(id){
57108         return this.lookup[id];
57109     },
57110
57111     
57112     /**
57113      * Returns the column for a specified dataIndex.
57114      * @param {String} dataIndex The column dataIndex
57115      * @return {Object|Boolean} the column or false if not found
57116      */
57117     getColumnByDataIndex: function(dataIndex){
57118         var index = this.findColumnIndex(dataIndex);
57119         return index > -1 ? this.config[index] : false;
57120     },
57121     
57122     /**
57123      * Returns the index for a specified column id.
57124      * @param {String} id The column id
57125      * @return {Number} the index, or -1 if not found
57126      */
57127     getIndexById : function(id){
57128         for(var i = 0, len = this.config.length; i < len; i++){
57129             if(this.config[i].id == id){
57130                 return i;
57131             }
57132         }
57133         return -1;
57134     },
57135     
57136     /**
57137      * Returns the index for a specified column dataIndex.
57138      * @param {String} dataIndex The column dataIndex
57139      * @return {Number} the index, or -1 if not found
57140      */
57141     
57142     findColumnIndex : function(dataIndex){
57143         for(var i = 0, len = this.config.length; i < len; i++){
57144             if(this.config[i].dataIndex == dataIndex){
57145                 return i;
57146             }
57147         }
57148         return -1;
57149     },
57150     
57151     
57152     moveColumn : function(oldIndex, newIndex){
57153         var c = this.config[oldIndex];
57154         this.config.splice(oldIndex, 1);
57155         this.config.splice(newIndex, 0, c);
57156         this.dataMap = null;
57157         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57158     },
57159
57160     isLocked : function(colIndex){
57161         return this.config[colIndex].locked === true;
57162     },
57163
57164     setLocked : function(colIndex, value, suppressEvent){
57165         if(this.isLocked(colIndex) == value){
57166             return;
57167         }
57168         this.config[colIndex].locked = value;
57169         if(!suppressEvent){
57170             this.fireEvent("columnlockchange", this, colIndex, value);
57171         }
57172     },
57173
57174     getTotalLockedWidth : function(){
57175         var totalWidth = 0;
57176         for(var i = 0; i < this.config.length; i++){
57177             if(this.isLocked(i) && !this.isHidden(i)){
57178                 this.totalWidth += this.getColumnWidth(i);
57179             }
57180         }
57181         return totalWidth;
57182     },
57183
57184     getLockedCount : function(){
57185         for(var i = 0, len = this.config.length; i < len; i++){
57186             if(!this.isLocked(i)){
57187                 return i;
57188             }
57189         }
57190         
57191         return this.config.length;
57192     },
57193
57194     /**
57195      * Returns the number of columns.
57196      * @return {Number}
57197      */
57198     getColumnCount : function(visibleOnly){
57199         if(visibleOnly === true){
57200             var c = 0;
57201             for(var i = 0, len = this.config.length; i < len; i++){
57202                 if(!this.isHidden(i)){
57203                     c++;
57204                 }
57205             }
57206             return c;
57207         }
57208         return this.config.length;
57209     },
57210
57211     /**
57212      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57213      * @param {Function} fn
57214      * @param {Object} scope (optional)
57215      * @return {Array} result
57216      */
57217     getColumnsBy : function(fn, scope){
57218         var r = [];
57219         for(var i = 0, len = this.config.length; i < len; i++){
57220             var c = this.config[i];
57221             if(fn.call(scope||this, c, i) === true){
57222                 r[r.length] = c;
57223             }
57224         }
57225         return r;
57226     },
57227
57228     /**
57229      * Returns true if the specified column is sortable.
57230      * @param {Number} col The column index
57231      * @return {Boolean}
57232      */
57233     isSortable : function(col){
57234         if(typeof this.config[col].sortable == "undefined"){
57235             return this.defaultSortable;
57236         }
57237         return this.config[col].sortable;
57238     },
57239
57240     /**
57241      * Returns the rendering (formatting) function defined for the column.
57242      * @param {Number} col The column index.
57243      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57244      */
57245     getRenderer : function(col){
57246         if(!this.config[col].renderer){
57247             return Roo.grid.ColumnModel.defaultRenderer;
57248         }
57249         return this.config[col].renderer;
57250     },
57251
57252     /**
57253      * Sets the rendering (formatting) function for a column.
57254      * @param {Number} col The column index
57255      * @param {Function} fn The function to use to process the cell's raw data
57256      * to return HTML markup for the grid view. The render function is called with
57257      * the following parameters:<ul>
57258      * <li>Data value.</li>
57259      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57260      * <li>css A CSS style string to apply to the table cell.</li>
57261      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57262      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57263      * <li>Row index</li>
57264      * <li>Column index</li>
57265      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57266      */
57267     setRenderer : function(col, fn){
57268         this.config[col].renderer = fn;
57269     },
57270
57271     /**
57272      * Returns the width for the specified column.
57273      * @param {Number} col The column index
57274      * @return {Number}
57275      */
57276     getColumnWidth : function(col){
57277         return this.config[col].width * 1 || this.defaultWidth;
57278     },
57279
57280     /**
57281      * Sets the width for a column.
57282      * @param {Number} col The column index
57283      * @param {Number} width The new width
57284      */
57285     setColumnWidth : function(col, width, suppressEvent){
57286         this.config[col].width = width;
57287         this.totalWidth = null;
57288         if(!suppressEvent){
57289              this.fireEvent("widthchange", this, col, width);
57290         }
57291     },
57292
57293     /**
57294      * Returns the total width of all columns.
57295      * @param {Boolean} includeHidden True to include hidden column widths
57296      * @return {Number}
57297      */
57298     getTotalWidth : function(includeHidden){
57299         if(!this.totalWidth){
57300             this.totalWidth = 0;
57301             for(var i = 0, len = this.config.length; i < len; i++){
57302                 if(includeHidden || !this.isHidden(i)){
57303                     this.totalWidth += this.getColumnWidth(i);
57304                 }
57305             }
57306         }
57307         return this.totalWidth;
57308     },
57309
57310     /**
57311      * Returns the header for the specified column.
57312      * @param {Number} col The column index
57313      * @return {String}
57314      */
57315     getColumnHeader : function(col){
57316         return this.config[col].header;
57317     },
57318
57319     /**
57320      * Sets the header for a column.
57321      * @param {Number} col The column index
57322      * @param {String} header The new header
57323      */
57324     setColumnHeader : function(col, header){
57325         this.config[col].header = header;
57326         this.fireEvent("headerchange", this, col, header);
57327     },
57328
57329     /**
57330      * Returns the tooltip for the specified column.
57331      * @param {Number} col The column index
57332      * @return {String}
57333      */
57334     getColumnTooltip : function(col){
57335             return this.config[col].tooltip;
57336     },
57337     /**
57338      * Sets the tooltip for a column.
57339      * @param {Number} col The column index
57340      * @param {String} tooltip The new tooltip
57341      */
57342     setColumnTooltip : function(col, tooltip){
57343             this.config[col].tooltip = tooltip;
57344     },
57345
57346     /**
57347      * Returns the dataIndex for the specified column.
57348      * @param {Number} col The column index
57349      * @return {Number}
57350      */
57351     getDataIndex : function(col){
57352         return this.config[col].dataIndex;
57353     },
57354
57355     /**
57356      * Sets the dataIndex for a column.
57357      * @param {Number} col The column index
57358      * @param {Number} dataIndex The new dataIndex
57359      */
57360     setDataIndex : function(col, dataIndex){
57361         this.config[col].dataIndex = dataIndex;
57362     },
57363
57364     
57365     
57366     /**
57367      * Returns true if the cell is editable.
57368      * @param {Number} colIndex The column index
57369      * @param {Number} rowIndex The row index - this is nto actually used..?
57370      * @return {Boolean}
57371      */
57372     isCellEditable : function(colIndex, rowIndex){
57373         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57374     },
57375
57376     /**
57377      * Returns the editor defined for the cell/column.
57378      * return false or null to disable editing.
57379      * @param {Number} colIndex The column index
57380      * @param {Number} rowIndex The row index
57381      * @return {Object}
57382      */
57383     getCellEditor : function(colIndex, rowIndex){
57384         return this.config[colIndex].editor;
57385     },
57386
57387     /**
57388      * Sets if a column is editable.
57389      * @param {Number} col The column index
57390      * @param {Boolean} editable True if the column is editable
57391      */
57392     setEditable : function(col, editable){
57393         this.config[col].editable = editable;
57394     },
57395
57396
57397     /**
57398      * Returns true if the column is hidden.
57399      * @param {Number} colIndex The column index
57400      * @return {Boolean}
57401      */
57402     isHidden : function(colIndex){
57403         return this.config[colIndex].hidden;
57404     },
57405
57406
57407     /**
57408      * Returns true if the column width cannot be changed
57409      */
57410     isFixed : function(colIndex){
57411         return this.config[colIndex].fixed;
57412     },
57413
57414     /**
57415      * Returns true if the column can be resized
57416      * @return {Boolean}
57417      */
57418     isResizable : function(colIndex){
57419         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57420     },
57421     /**
57422      * Sets if a column is hidden.
57423      * @param {Number} colIndex The column index
57424      * @param {Boolean} hidden True if the column is hidden
57425      */
57426     setHidden : function(colIndex, hidden){
57427         this.config[colIndex].hidden = hidden;
57428         this.totalWidth = null;
57429         this.fireEvent("hiddenchange", this, colIndex, hidden);
57430     },
57431
57432     /**
57433      * Sets the editor for a column.
57434      * @param {Number} col The column index
57435      * @param {Object} editor The editor object
57436      */
57437     setEditor : function(col, editor){
57438         this.config[col].editor = editor;
57439     }
57440 });
57441
57442 Roo.grid.ColumnModel.defaultRenderer = function(value)
57443 {
57444     if(typeof value == "object") {
57445         return value;
57446     }
57447         if(typeof value == "string" && value.length < 1){
57448             return "&#160;";
57449         }
57450     
57451         return String.format("{0}", value);
57452 };
57453
57454 // Alias for backwards compatibility
57455 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57456 /*
57457  * Based on:
57458  * Ext JS Library 1.1.1
57459  * Copyright(c) 2006-2007, Ext JS, LLC.
57460  *
57461  * Originally Released Under LGPL - original licence link has changed is not relivant.
57462  *
57463  * Fork - LGPL
57464  * <script type="text/javascript">
57465  */
57466
57467 /**
57468  * @class Roo.grid.AbstractSelectionModel
57469  * @extends Roo.util.Observable
57470  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57471  * implemented by descendant classes.  This class should not be directly instantiated.
57472  * @constructor
57473  */
57474 Roo.grid.AbstractSelectionModel = function(){
57475     this.locked = false;
57476     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57477 };
57478
57479 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57480     /** @ignore Called by the grid automatically. Do not call directly. */
57481     init : function(grid){
57482         this.grid = grid;
57483         this.initEvents();
57484     },
57485
57486     /**
57487      * Locks the selections.
57488      */
57489     lock : function(){
57490         this.locked = true;
57491     },
57492
57493     /**
57494      * Unlocks the selections.
57495      */
57496     unlock : function(){
57497         this.locked = false;
57498     },
57499
57500     /**
57501      * Returns true if the selections are locked.
57502      * @return {Boolean}
57503      */
57504     isLocked : function(){
57505         return this.locked;
57506     }
57507 });/*
57508  * Based on:
57509  * Ext JS Library 1.1.1
57510  * Copyright(c) 2006-2007, Ext JS, LLC.
57511  *
57512  * Originally Released Under LGPL - original licence link has changed is not relivant.
57513  *
57514  * Fork - LGPL
57515  * <script type="text/javascript">
57516  */
57517 /**
57518  * @extends Roo.grid.AbstractSelectionModel
57519  * @class Roo.grid.RowSelectionModel
57520  * The default SelectionModel used by {@link Roo.grid.Grid}.
57521  * It supports multiple selections and keyboard selection/navigation. 
57522  * @constructor
57523  * @param {Object} config
57524  */
57525 Roo.grid.RowSelectionModel = function(config){
57526     Roo.apply(this, config);
57527     this.selections = new Roo.util.MixedCollection(false, function(o){
57528         return o.id;
57529     });
57530
57531     this.last = false;
57532     this.lastActive = false;
57533
57534     this.addEvents({
57535         /**
57536              * @event selectionchange
57537              * Fires when the selection changes
57538              * @param {SelectionModel} this
57539              */
57540             "selectionchange" : true,
57541         /**
57542              * @event afterselectionchange
57543              * Fires after the selection changes (eg. by key press or clicking)
57544              * @param {SelectionModel} this
57545              */
57546             "afterselectionchange" : true,
57547         /**
57548              * @event beforerowselect
57549              * Fires when a row is selected being selected, return false to cancel.
57550              * @param {SelectionModel} this
57551              * @param {Number} rowIndex The selected index
57552              * @param {Boolean} keepExisting False if other selections will be cleared
57553              */
57554             "beforerowselect" : true,
57555         /**
57556              * @event rowselect
57557              * Fires when a row is selected.
57558              * @param {SelectionModel} this
57559              * @param {Number} rowIndex The selected index
57560              * @param {Roo.data.Record} r The record
57561              */
57562             "rowselect" : true,
57563         /**
57564              * @event rowdeselect
57565              * Fires when a row is deselected.
57566              * @param {SelectionModel} this
57567              * @param {Number} rowIndex The selected index
57568              */
57569         "rowdeselect" : true
57570     });
57571     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57572     this.locked = false;
57573 };
57574
57575 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57576     /**
57577      * @cfg {Boolean} singleSelect
57578      * True to allow selection of only one row at a time (defaults to false)
57579      */
57580     singleSelect : false,
57581
57582     // private
57583     initEvents : function(){
57584
57585         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57586             this.grid.on("mousedown", this.handleMouseDown, this);
57587         }else{ // allow click to work like normal
57588             this.grid.on("rowclick", this.handleDragableRowClick, this);
57589         }
57590
57591         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57592             "up" : function(e){
57593                 if(!e.shiftKey){
57594                     this.selectPrevious(e.shiftKey);
57595                 }else if(this.last !== false && this.lastActive !== false){
57596                     var last = this.last;
57597                     this.selectRange(this.last,  this.lastActive-1);
57598                     this.grid.getView().focusRow(this.lastActive);
57599                     if(last !== false){
57600                         this.last = last;
57601                     }
57602                 }else{
57603                     this.selectFirstRow();
57604                 }
57605                 this.fireEvent("afterselectionchange", this);
57606             },
57607             "down" : function(e){
57608                 if(!e.shiftKey){
57609                     this.selectNext(e.shiftKey);
57610                 }else if(this.last !== false && this.lastActive !== false){
57611                     var last = this.last;
57612                     this.selectRange(this.last,  this.lastActive+1);
57613                     this.grid.getView().focusRow(this.lastActive);
57614                     if(last !== false){
57615                         this.last = last;
57616                     }
57617                 }else{
57618                     this.selectFirstRow();
57619                 }
57620                 this.fireEvent("afterselectionchange", this);
57621             },
57622             scope: this
57623         });
57624
57625         var view = this.grid.view;
57626         view.on("refresh", this.onRefresh, this);
57627         view.on("rowupdated", this.onRowUpdated, this);
57628         view.on("rowremoved", this.onRemove, this);
57629     },
57630
57631     // private
57632     onRefresh : function(){
57633         var ds = this.grid.dataSource, i, v = this.grid.view;
57634         var s = this.selections;
57635         s.each(function(r){
57636             if((i = ds.indexOfId(r.id)) != -1){
57637                 v.onRowSelect(i);
57638                 s.add(ds.getAt(i)); // updating the selection relate data
57639             }else{
57640                 s.remove(r);
57641             }
57642         });
57643     },
57644
57645     // private
57646     onRemove : function(v, index, r){
57647         this.selections.remove(r);
57648     },
57649
57650     // private
57651     onRowUpdated : function(v, index, r){
57652         if(this.isSelected(r)){
57653             v.onRowSelect(index);
57654         }
57655     },
57656
57657     /**
57658      * Select records.
57659      * @param {Array} records The records to select
57660      * @param {Boolean} keepExisting (optional) True to keep existing selections
57661      */
57662     selectRecords : function(records, keepExisting){
57663         if(!keepExisting){
57664             this.clearSelections();
57665         }
57666         var ds = this.grid.dataSource;
57667         for(var i = 0, len = records.length; i < len; i++){
57668             this.selectRow(ds.indexOf(records[i]), true);
57669         }
57670     },
57671
57672     /**
57673      * Gets the number of selected rows.
57674      * @return {Number}
57675      */
57676     getCount : function(){
57677         return this.selections.length;
57678     },
57679
57680     /**
57681      * Selects the first row in the grid.
57682      */
57683     selectFirstRow : function(){
57684         this.selectRow(0);
57685     },
57686
57687     /**
57688      * Select the last row.
57689      * @param {Boolean} keepExisting (optional) True to keep existing selections
57690      */
57691     selectLastRow : function(keepExisting){
57692         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57693     },
57694
57695     /**
57696      * Selects the row immediately following the last selected row.
57697      * @param {Boolean} keepExisting (optional) True to keep existing selections
57698      */
57699     selectNext : function(keepExisting){
57700         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57701             this.selectRow(this.last+1, keepExisting);
57702             this.grid.getView().focusRow(this.last);
57703         }
57704     },
57705
57706     /**
57707      * Selects the row that precedes the last selected row.
57708      * @param {Boolean} keepExisting (optional) True to keep existing selections
57709      */
57710     selectPrevious : function(keepExisting){
57711         if(this.last){
57712             this.selectRow(this.last-1, keepExisting);
57713             this.grid.getView().focusRow(this.last);
57714         }
57715     },
57716
57717     /**
57718      * Returns the selected records
57719      * @return {Array} Array of selected records
57720      */
57721     getSelections : function(){
57722         return [].concat(this.selections.items);
57723     },
57724
57725     /**
57726      * Returns the first selected record.
57727      * @return {Record}
57728      */
57729     getSelected : function(){
57730         return this.selections.itemAt(0);
57731     },
57732
57733
57734     /**
57735      * Clears all selections.
57736      */
57737     clearSelections : function(fast){
57738         if(this.locked) {
57739             return;
57740         }
57741         if(fast !== true){
57742             var ds = this.grid.dataSource;
57743             var s = this.selections;
57744             s.each(function(r){
57745                 this.deselectRow(ds.indexOfId(r.id));
57746             }, this);
57747             s.clear();
57748         }else{
57749             this.selections.clear();
57750         }
57751         this.last = false;
57752     },
57753
57754
57755     /**
57756      * Selects all rows.
57757      */
57758     selectAll : function(){
57759         if(this.locked) {
57760             return;
57761         }
57762         this.selections.clear();
57763         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57764             this.selectRow(i, true);
57765         }
57766     },
57767
57768     /**
57769      * Returns True if there is a selection.
57770      * @return {Boolean}
57771      */
57772     hasSelection : function(){
57773         return this.selections.length > 0;
57774     },
57775
57776     /**
57777      * Returns True if the specified row is selected.
57778      * @param {Number/Record} record The record or index of the record to check
57779      * @return {Boolean}
57780      */
57781     isSelected : function(index){
57782         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57783         return (r && this.selections.key(r.id) ? true : false);
57784     },
57785
57786     /**
57787      * Returns True if the specified record id is selected.
57788      * @param {String} id The id of record to check
57789      * @return {Boolean}
57790      */
57791     isIdSelected : function(id){
57792         return (this.selections.key(id) ? true : false);
57793     },
57794
57795     // private
57796     handleMouseDown : function(e, t){
57797         var view = this.grid.getView(), rowIndex;
57798         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57799             return;
57800         };
57801         if(e.shiftKey && this.last !== false){
57802             var last = this.last;
57803             this.selectRange(last, rowIndex, e.ctrlKey);
57804             this.last = last; // reset the last
57805             view.focusRow(rowIndex);
57806         }else{
57807             var isSelected = this.isSelected(rowIndex);
57808             if(e.button !== 0 && isSelected){
57809                 view.focusRow(rowIndex);
57810             }else if(e.ctrlKey && isSelected){
57811                 this.deselectRow(rowIndex);
57812             }else if(!isSelected){
57813                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57814                 view.focusRow(rowIndex);
57815             }
57816         }
57817         this.fireEvent("afterselectionchange", this);
57818     },
57819     // private
57820     handleDragableRowClick :  function(grid, rowIndex, e) 
57821     {
57822         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57823             this.selectRow(rowIndex, false);
57824             grid.view.focusRow(rowIndex);
57825              this.fireEvent("afterselectionchange", this);
57826         }
57827     },
57828     
57829     /**
57830      * Selects multiple rows.
57831      * @param {Array} rows Array of the indexes of the row to select
57832      * @param {Boolean} keepExisting (optional) True to keep existing selections
57833      */
57834     selectRows : function(rows, keepExisting){
57835         if(!keepExisting){
57836             this.clearSelections();
57837         }
57838         for(var i = 0, len = rows.length; i < len; i++){
57839             this.selectRow(rows[i], true);
57840         }
57841     },
57842
57843     /**
57844      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57845      * @param {Number} startRow The index of the first row in the range
57846      * @param {Number} endRow The index of the last row in the range
57847      * @param {Boolean} keepExisting (optional) True to retain existing selections
57848      */
57849     selectRange : function(startRow, endRow, keepExisting){
57850         if(this.locked) {
57851             return;
57852         }
57853         if(!keepExisting){
57854             this.clearSelections();
57855         }
57856         if(startRow <= endRow){
57857             for(var i = startRow; i <= endRow; i++){
57858                 this.selectRow(i, true);
57859             }
57860         }else{
57861             for(var i = startRow; i >= endRow; i--){
57862                 this.selectRow(i, true);
57863             }
57864         }
57865     },
57866
57867     /**
57868      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57869      * @param {Number} startRow The index of the first row in the range
57870      * @param {Number} endRow The index of the last row in the range
57871      */
57872     deselectRange : function(startRow, endRow, preventViewNotify){
57873         if(this.locked) {
57874             return;
57875         }
57876         for(var i = startRow; i <= endRow; i++){
57877             this.deselectRow(i, preventViewNotify);
57878         }
57879     },
57880
57881     /**
57882      * Selects a row.
57883      * @param {Number} row The index of the row to select
57884      * @param {Boolean} keepExisting (optional) True to keep existing selections
57885      */
57886     selectRow : function(index, keepExisting, preventViewNotify){
57887         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57888             return;
57889         }
57890         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57891             if(!keepExisting || this.singleSelect){
57892                 this.clearSelections();
57893             }
57894             var r = this.grid.dataSource.getAt(index);
57895             this.selections.add(r);
57896             this.last = this.lastActive = index;
57897             if(!preventViewNotify){
57898                 this.grid.getView().onRowSelect(index);
57899             }
57900             this.fireEvent("rowselect", this, index, r);
57901             this.fireEvent("selectionchange", this);
57902         }
57903     },
57904
57905     /**
57906      * Deselects a row.
57907      * @param {Number} row The index of the row to deselect
57908      */
57909     deselectRow : function(index, preventViewNotify){
57910         if(this.locked) {
57911             return;
57912         }
57913         if(this.last == index){
57914             this.last = false;
57915         }
57916         if(this.lastActive == index){
57917             this.lastActive = false;
57918         }
57919         var r = this.grid.dataSource.getAt(index);
57920         this.selections.remove(r);
57921         if(!preventViewNotify){
57922             this.grid.getView().onRowDeselect(index);
57923         }
57924         this.fireEvent("rowdeselect", this, index);
57925         this.fireEvent("selectionchange", this);
57926     },
57927
57928     // private
57929     restoreLast : function(){
57930         if(this._last){
57931             this.last = this._last;
57932         }
57933     },
57934
57935     // private
57936     acceptsNav : function(row, col, cm){
57937         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57938     },
57939
57940     // private
57941     onEditorKey : function(field, e){
57942         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57943         if(k == e.TAB){
57944             e.stopEvent();
57945             ed.completeEdit();
57946             if(e.shiftKey){
57947                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57948             }else{
57949                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57950             }
57951         }else if(k == e.ENTER && !e.ctrlKey){
57952             e.stopEvent();
57953             ed.completeEdit();
57954             if(e.shiftKey){
57955                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57956             }else{
57957                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57958             }
57959         }else if(k == e.ESC){
57960             ed.cancelEdit();
57961         }
57962         if(newCell){
57963             g.startEditing(newCell[0], newCell[1]);
57964         }
57965     }
57966 });/*
57967  * Based on:
57968  * Ext JS Library 1.1.1
57969  * Copyright(c) 2006-2007, Ext JS, LLC.
57970  *
57971  * Originally Released Under LGPL - original licence link has changed is not relivant.
57972  *
57973  * Fork - LGPL
57974  * <script type="text/javascript">
57975  */
57976 /**
57977  * @class Roo.grid.CellSelectionModel
57978  * @extends Roo.grid.AbstractSelectionModel
57979  * This class provides the basic implementation for cell selection in a grid.
57980  * @constructor
57981  * @param {Object} config The object containing the configuration of this model.
57982  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57983  */
57984 Roo.grid.CellSelectionModel = function(config){
57985     Roo.apply(this, config);
57986
57987     this.selection = null;
57988
57989     this.addEvents({
57990         /**
57991              * @event beforerowselect
57992              * Fires before a cell is selected.
57993              * @param {SelectionModel} this
57994              * @param {Number} rowIndex The selected row index
57995              * @param {Number} colIndex The selected cell index
57996              */
57997             "beforecellselect" : true,
57998         /**
57999              * @event cellselect
58000              * Fires when a cell is selected.
58001              * @param {SelectionModel} this
58002              * @param {Number} rowIndex The selected row index
58003              * @param {Number} colIndex The selected cell index
58004              */
58005             "cellselect" : true,
58006         /**
58007              * @event selectionchange
58008              * Fires when the active selection changes.
58009              * @param {SelectionModel} this
58010              * @param {Object} selection null for no selection or an object (o) with two properties
58011                 <ul>
58012                 <li>o.record: the record object for the row the selection is in</li>
58013                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58014                 </ul>
58015              */
58016             "selectionchange" : true,
58017         /**
58018              * @event tabend
58019              * Fires when the tab (or enter) was pressed on the last editable cell
58020              * You can use this to trigger add new row.
58021              * @param {SelectionModel} this
58022              */
58023             "tabend" : true,
58024          /**
58025              * @event beforeeditnext
58026              * Fires before the next editable sell is made active
58027              * You can use this to skip to another cell or fire the tabend
58028              *    if you set cell to false
58029              * @param {Object} eventdata object : { cell : [ row, col ] } 
58030              */
58031             "beforeeditnext" : true
58032     });
58033     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58034 };
58035
58036 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58037     
58038     enter_is_tab: false,
58039
58040     /** @ignore */
58041     initEvents : function(){
58042         this.grid.on("mousedown", this.handleMouseDown, this);
58043         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58044         var view = this.grid.view;
58045         view.on("refresh", this.onViewChange, this);
58046         view.on("rowupdated", this.onRowUpdated, this);
58047         view.on("beforerowremoved", this.clearSelections, this);
58048         view.on("beforerowsinserted", this.clearSelections, this);
58049         if(this.grid.isEditor){
58050             this.grid.on("beforeedit", this.beforeEdit,  this);
58051         }
58052     },
58053
58054         //private
58055     beforeEdit : function(e){
58056         this.select(e.row, e.column, false, true, e.record);
58057     },
58058
58059         //private
58060     onRowUpdated : function(v, index, r){
58061         if(this.selection && this.selection.record == r){
58062             v.onCellSelect(index, this.selection.cell[1]);
58063         }
58064     },
58065
58066         //private
58067     onViewChange : function(){
58068         this.clearSelections(true);
58069     },
58070
58071         /**
58072          * Returns the currently selected cell,.
58073          * @return {Array} The selected cell (row, column) or null if none selected.
58074          */
58075     getSelectedCell : function(){
58076         return this.selection ? this.selection.cell : null;
58077     },
58078
58079     /**
58080      * Clears all selections.
58081      * @param {Boolean} true to prevent the gridview from being notified about the change.
58082      */
58083     clearSelections : function(preventNotify){
58084         var s = this.selection;
58085         if(s){
58086             if(preventNotify !== true){
58087                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58088             }
58089             this.selection = null;
58090             this.fireEvent("selectionchange", this, null);
58091         }
58092     },
58093
58094     /**
58095      * Returns true if there is a selection.
58096      * @return {Boolean}
58097      */
58098     hasSelection : function(){
58099         return this.selection ? true : false;
58100     },
58101
58102     /** @ignore */
58103     handleMouseDown : function(e, t){
58104         var v = this.grid.getView();
58105         if(this.isLocked()){
58106             return;
58107         };
58108         var row = v.findRowIndex(t);
58109         var cell = v.findCellIndex(t);
58110         if(row !== false && cell !== false){
58111             this.select(row, cell);
58112         }
58113     },
58114
58115     /**
58116      * Selects a cell.
58117      * @param {Number} rowIndex
58118      * @param {Number} collIndex
58119      */
58120     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58121         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58122             this.clearSelections();
58123             r = r || this.grid.dataSource.getAt(rowIndex);
58124             this.selection = {
58125                 record : r,
58126                 cell : [rowIndex, colIndex]
58127             };
58128             if(!preventViewNotify){
58129                 var v = this.grid.getView();
58130                 v.onCellSelect(rowIndex, colIndex);
58131                 if(preventFocus !== true){
58132                     v.focusCell(rowIndex, colIndex);
58133                 }
58134             }
58135             this.fireEvent("cellselect", this, rowIndex, colIndex);
58136             this.fireEvent("selectionchange", this, this.selection);
58137         }
58138     },
58139
58140         //private
58141     isSelectable : function(rowIndex, colIndex, cm){
58142         return !cm.isHidden(colIndex);
58143     },
58144
58145     /** @ignore */
58146     handleKeyDown : function(e){
58147         //Roo.log('Cell Sel Model handleKeyDown');
58148         if(!e.isNavKeyPress()){
58149             return;
58150         }
58151         var g = this.grid, s = this.selection;
58152         if(!s){
58153             e.stopEvent();
58154             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58155             if(cell){
58156                 this.select(cell[0], cell[1]);
58157             }
58158             return;
58159         }
58160         var sm = this;
58161         var walk = function(row, col, step){
58162             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58163         };
58164         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58165         var newCell;
58166
58167       
58168
58169         switch(k){
58170             case e.TAB:
58171                 // handled by onEditorKey
58172                 if (g.isEditor && g.editing) {
58173                     return;
58174                 }
58175                 if(e.shiftKey) {
58176                     newCell = walk(r, c-1, -1);
58177                 } else {
58178                     newCell = walk(r, c+1, 1);
58179                 }
58180                 break;
58181             
58182             case e.DOWN:
58183                newCell = walk(r+1, c, 1);
58184                 break;
58185             
58186             case e.UP:
58187                 newCell = walk(r-1, c, -1);
58188                 break;
58189             
58190             case e.RIGHT:
58191                 newCell = walk(r, c+1, 1);
58192                 break;
58193             
58194             case e.LEFT:
58195                 newCell = walk(r, c-1, -1);
58196                 break;
58197             
58198             case e.ENTER:
58199                 
58200                 if(g.isEditor && !g.editing){
58201                    g.startEditing(r, c);
58202                    e.stopEvent();
58203                    return;
58204                 }
58205                 
58206                 
58207              break;
58208         };
58209         if(newCell){
58210             this.select(newCell[0], newCell[1]);
58211             e.stopEvent();
58212             
58213         }
58214     },
58215
58216     acceptsNav : function(row, col, cm){
58217         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58218     },
58219     /**
58220      * Selects a cell.
58221      * @param {Number} field (not used) - as it's normally used as a listener
58222      * @param {Number} e - event - fake it by using
58223      *
58224      * var e = Roo.EventObjectImpl.prototype;
58225      * e.keyCode = e.TAB
58226      *
58227      * 
58228      */
58229     onEditorKey : function(field, e){
58230         
58231         var k = e.getKey(),
58232             newCell,
58233             g = this.grid,
58234             ed = g.activeEditor,
58235             forward = false;
58236         ///Roo.log('onEditorKey' + k);
58237         
58238         
58239         if (this.enter_is_tab && k == e.ENTER) {
58240             k = e.TAB;
58241         }
58242         
58243         if(k == e.TAB){
58244             if(e.shiftKey){
58245                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58246             }else{
58247                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58248                 forward = true;
58249             }
58250             
58251             e.stopEvent();
58252             
58253         } else if(k == e.ENTER &&  !e.ctrlKey){
58254             ed.completeEdit();
58255             e.stopEvent();
58256             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58257         
58258                 } else if(k == e.ESC){
58259             ed.cancelEdit();
58260         }
58261                 
58262         if (newCell) {
58263             var ecall = { cell : newCell, forward : forward };
58264             this.fireEvent('beforeeditnext', ecall );
58265             newCell = ecall.cell;
58266                         forward = ecall.forward;
58267         }
58268                 
58269         if(newCell){
58270             //Roo.log('next cell after edit');
58271             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58272         } else if (forward) {
58273             // tabbed past last
58274             this.fireEvent.defer(100, this, ['tabend',this]);
58275         }
58276     }
58277 });/*
58278  * Based on:
58279  * Ext JS Library 1.1.1
58280  * Copyright(c) 2006-2007, Ext JS, LLC.
58281  *
58282  * Originally Released Under LGPL - original licence link has changed is not relivant.
58283  *
58284  * Fork - LGPL
58285  * <script type="text/javascript">
58286  */
58287  
58288 /**
58289  * @class Roo.grid.EditorGrid
58290  * @extends Roo.grid.Grid
58291  * Class for creating and editable grid.
58292  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58293  * The container MUST have some type of size defined for the grid to fill. The container will be 
58294  * automatically set to position relative if it isn't already.
58295  * @param {Object} dataSource The data model to bind to
58296  * @param {Object} colModel The column model with info about this grid's columns
58297  */
58298 Roo.grid.EditorGrid = function(container, config){
58299     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58300     this.getGridEl().addClass("xedit-grid");
58301
58302     if(!this.selModel){
58303         this.selModel = new Roo.grid.CellSelectionModel();
58304     }
58305
58306     this.activeEditor = null;
58307
58308         this.addEvents({
58309             /**
58310              * @event beforeedit
58311              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58312              * <ul style="padding:5px;padding-left:16px;">
58313              * <li>grid - This grid</li>
58314              * <li>record - The record being edited</li>
58315              * <li>field - The field name being edited</li>
58316              * <li>value - The value for the field being edited.</li>
58317              * <li>row - The grid row index</li>
58318              * <li>column - The grid column index</li>
58319              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58320              * </ul>
58321              * @param {Object} e An edit event (see above for description)
58322              */
58323             "beforeedit" : true,
58324             /**
58325              * @event afteredit
58326              * Fires after a cell is edited. <br />
58327              * <ul style="padding:5px;padding-left:16px;">
58328              * <li>grid - This grid</li>
58329              * <li>record - The record being edited</li>
58330              * <li>field - The field name being edited</li>
58331              * <li>value - The value being set</li>
58332              * <li>originalValue - The original value for the field, before the edit.</li>
58333              * <li>row - The grid row index</li>
58334              * <li>column - The grid column index</li>
58335              * </ul>
58336              * @param {Object} e An edit event (see above for description)
58337              */
58338             "afteredit" : true,
58339             /**
58340              * @event validateedit
58341              * Fires after a cell is edited, but before the value is set in the record. 
58342          * You can use this to modify the value being set in the field, Return false
58343              * to cancel the change. The edit event object has the following properties <br />
58344              * <ul style="padding:5px;padding-left:16px;">
58345          * <li>editor - This editor</li>
58346              * <li>grid - This grid</li>
58347              * <li>record - The record being edited</li>
58348              * <li>field - The field name being edited</li>
58349              * <li>value - The value being set</li>
58350              * <li>originalValue - The original value for the field, before the edit.</li>
58351              * <li>row - The grid row index</li>
58352              * <li>column - The grid column index</li>
58353              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58354              * </ul>
58355              * @param {Object} e An edit event (see above for description)
58356              */
58357             "validateedit" : true
58358         });
58359     this.on("bodyscroll", this.stopEditing,  this);
58360     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58361 };
58362
58363 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58364     /**
58365      * @cfg {Number} clicksToEdit
58366      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58367      */
58368     clicksToEdit: 2,
58369
58370     // private
58371     isEditor : true,
58372     // private
58373     trackMouseOver: false, // causes very odd FF errors
58374
58375     onCellDblClick : function(g, row, col){
58376         this.startEditing(row, col);
58377     },
58378
58379     onEditComplete : function(ed, value, startValue){
58380         this.editing = false;
58381         this.activeEditor = null;
58382         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58383         var r = ed.record;
58384         var field = this.colModel.getDataIndex(ed.col);
58385         var e = {
58386             grid: this,
58387             record: r,
58388             field: field,
58389             originalValue: startValue,
58390             value: value,
58391             row: ed.row,
58392             column: ed.col,
58393             cancel:false,
58394             editor: ed
58395         };
58396         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58397         cell.show();
58398           
58399         if(String(value) !== String(startValue)){
58400             
58401             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58402                 r.set(field, e.value);
58403                 // if we are dealing with a combo box..
58404                 // then we also set the 'name' colum to be the displayField
58405                 if (ed.field.displayField && ed.field.name) {
58406                     r.set(ed.field.name, ed.field.el.dom.value);
58407                 }
58408                 
58409                 delete e.cancel; //?? why!!!
58410                 this.fireEvent("afteredit", e);
58411             }
58412         } else {
58413             this.fireEvent("afteredit", e); // always fire it!
58414         }
58415         this.view.focusCell(ed.row, ed.col);
58416     },
58417
58418     /**
58419      * Starts editing the specified for the specified row/column
58420      * @param {Number} rowIndex
58421      * @param {Number} colIndex
58422      */
58423     startEditing : function(row, col){
58424         this.stopEditing();
58425         if(this.colModel.isCellEditable(col, row)){
58426             this.view.ensureVisible(row, col, true);
58427           
58428             var r = this.dataSource.getAt(row);
58429             var field = this.colModel.getDataIndex(col);
58430             var cell = Roo.get(this.view.getCell(row,col));
58431             var e = {
58432                 grid: this,
58433                 record: r,
58434                 field: field,
58435                 value: r.data[field],
58436                 row: row,
58437                 column: col,
58438                 cancel:false 
58439             };
58440             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58441                 this.editing = true;
58442                 var ed = this.colModel.getCellEditor(col, row);
58443                 
58444                 if (!ed) {
58445                     return;
58446                 }
58447                 if(!ed.rendered){
58448                     ed.render(ed.parentEl || document.body);
58449                 }
58450                 ed.field.reset();
58451                
58452                 cell.hide();
58453                 
58454                 (function(){ // complex but required for focus issues in safari, ie and opera
58455                     ed.row = row;
58456                     ed.col = col;
58457                     ed.record = r;
58458                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58459                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58460                     this.activeEditor = ed;
58461                     var v = r.data[field];
58462                     ed.startEdit(this.view.getCell(row, col), v);
58463                     // combo's with 'displayField and name set
58464                     if (ed.field.displayField && ed.field.name) {
58465                         ed.field.el.dom.value = r.data[ed.field.name];
58466                     }
58467                     
58468                     
58469                 }).defer(50, this);
58470             }
58471         }
58472     },
58473         
58474     /**
58475      * Stops any active editing
58476      */
58477     stopEditing : function(){
58478         if(this.activeEditor){
58479             this.activeEditor.completeEdit();
58480         }
58481         this.activeEditor = null;
58482     },
58483         
58484          /**
58485      * Called to get grid's drag proxy text, by default returns this.ddText.
58486      * @return {String}
58487      */
58488     getDragDropText : function(){
58489         var count = this.selModel.getSelectedCell() ? 1 : 0;
58490         return String.format(this.ddText, count, count == 1 ? '' : 's');
58491     }
58492         
58493 });/*
58494  * Based on:
58495  * Ext JS Library 1.1.1
58496  * Copyright(c) 2006-2007, Ext JS, LLC.
58497  *
58498  * Originally Released Under LGPL - original licence link has changed is not relivant.
58499  *
58500  * Fork - LGPL
58501  * <script type="text/javascript">
58502  */
58503
58504 // private - not really -- you end up using it !
58505 // This is a support class used internally by the Grid components
58506
58507 /**
58508  * @class Roo.grid.GridEditor
58509  * @extends Roo.Editor
58510  * Class for creating and editable grid elements.
58511  * @param {Object} config any settings (must include field)
58512  */
58513 Roo.grid.GridEditor = function(field, config){
58514     if (!config && field.field) {
58515         config = field;
58516         field = Roo.factory(config.field, Roo.form);
58517     }
58518     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58519     field.monitorTab = false;
58520 };
58521
58522 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58523     
58524     /**
58525      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58526      */
58527     
58528     alignment: "tl-tl",
58529     autoSize: "width",
58530     hideEl : false,
58531     cls: "x-small-editor x-grid-editor",
58532     shim:false,
58533     shadow:"frame"
58534 });/*
58535  * Based on:
58536  * Ext JS Library 1.1.1
58537  * Copyright(c) 2006-2007, Ext JS, LLC.
58538  *
58539  * Originally Released Under LGPL - original licence link has changed is not relivant.
58540  *
58541  * Fork - LGPL
58542  * <script type="text/javascript">
58543  */
58544   
58545
58546   
58547 Roo.grid.PropertyRecord = Roo.data.Record.create([
58548     {name:'name',type:'string'},  'value'
58549 ]);
58550
58551
58552 Roo.grid.PropertyStore = function(grid, source){
58553     this.grid = grid;
58554     this.store = new Roo.data.Store({
58555         recordType : Roo.grid.PropertyRecord
58556     });
58557     this.store.on('update', this.onUpdate,  this);
58558     if(source){
58559         this.setSource(source);
58560     }
58561     Roo.grid.PropertyStore.superclass.constructor.call(this);
58562 };
58563
58564
58565
58566 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58567     setSource : function(o){
58568         this.source = o;
58569         this.store.removeAll();
58570         var data = [];
58571         for(var k in o){
58572             if(this.isEditableValue(o[k])){
58573                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58574             }
58575         }
58576         this.store.loadRecords({records: data}, {}, true);
58577     },
58578
58579     onUpdate : function(ds, record, type){
58580         if(type == Roo.data.Record.EDIT){
58581             var v = record.data['value'];
58582             var oldValue = record.modified['value'];
58583             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58584                 this.source[record.id] = v;
58585                 record.commit();
58586                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58587             }else{
58588                 record.reject();
58589             }
58590         }
58591     },
58592
58593     getProperty : function(row){
58594        return this.store.getAt(row);
58595     },
58596
58597     isEditableValue: function(val){
58598         if(val && val instanceof Date){
58599             return true;
58600         }else if(typeof val == 'object' || typeof val == 'function'){
58601             return false;
58602         }
58603         return true;
58604     },
58605
58606     setValue : function(prop, value){
58607         this.source[prop] = value;
58608         this.store.getById(prop).set('value', value);
58609     },
58610
58611     getSource : function(){
58612         return this.source;
58613     }
58614 });
58615
58616 Roo.grid.PropertyColumnModel = function(grid, store){
58617     this.grid = grid;
58618     var g = Roo.grid;
58619     g.PropertyColumnModel.superclass.constructor.call(this, [
58620         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58621         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58622     ]);
58623     this.store = store;
58624     this.bselect = Roo.DomHelper.append(document.body, {
58625         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58626             {tag: 'option', value: 'true', html: 'true'},
58627             {tag: 'option', value: 'false', html: 'false'}
58628         ]
58629     });
58630     Roo.id(this.bselect);
58631     var f = Roo.form;
58632     this.editors = {
58633         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58634         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58635         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58636         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58637         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58638     };
58639     this.renderCellDelegate = this.renderCell.createDelegate(this);
58640     this.renderPropDelegate = this.renderProp.createDelegate(this);
58641 };
58642
58643 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58644     
58645     
58646     nameText : 'Name',
58647     valueText : 'Value',
58648     
58649     dateFormat : 'm/j/Y',
58650     
58651     
58652     renderDate : function(dateVal){
58653         return dateVal.dateFormat(this.dateFormat);
58654     },
58655
58656     renderBool : function(bVal){
58657         return bVal ? 'true' : 'false';
58658     },
58659
58660     isCellEditable : function(colIndex, rowIndex){
58661         return colIndex == 1;
58662     },
58663
58664     getRenderer : function(col){
58665         return col == 1 ?
58666             this.renderCellDelegate : this.renderPropDelegate;
58667     },
58668
58669     renderProp : function(v){
58670         return this.getPropertyName(v);
58671     },
58672
58673     renderCell : function(val){
58674         var rv = val;
58675         if(val instanceof Date){
58676             rv = this.renderDate(val);
58677         }else if(typeof val == 'boolean'){
58678             rv = this.renderBool(val);
58679         }
58680         return Roo.util.Format.htmlEncode(rv);
58681     },
58682
58683     getPropertyName : function(name){
58684         var pn = this.grid.propertyNames;
58685         return pn && pn[name] ? pn[name] : name;
58686     },
58687
58688     getCellEditor : function(colIndex, rowIndex){
58689         var p = this.store.getProperty(rowIndex);
58690         var n = p.data['name'], val = p.data['value'];
58691         
58692         if(typeof(this.grid.customEditors[n]) == 'string'){
58693             return this.editors[this.grid.customEditors[n]];
58694         }
58695         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58696             return this.grid.customEditors[n];
58697         }
58698         if(val instanceof Date){
58699             return this.editors['date'];
58700         }else if(typeof val == 'number'){
58701             return this.editors['number'];
58702         }else if(typeof val == 'boolean'){
58703             return this.editors['boolean'];
58704         }else{
58705             return this.editors['string'];
58706         }
58707     }
58708 });
58709
58710 /**
58711  * @class Roo.grid.PropertyGrid
58712  * @extends Roo.grid.EditorGrid
58713  * This class represents the  interface of a component based property grid control.
58714  * <br><br>Usage:<pre><code>
58715  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58716       
58717  });
58718  // set any options
58719  grid.render();
58720  * </code></pre>
58721   
58722  * @constructor
58723  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58724  * The container MUST have some type of size defined for the grid to fill. The container will be
58725  * automatically set to position relative if it isn't already.
58726  * @param {Object} config A config object that sets properties on this grid.
58727  */
58728 Roo.grid.PropertyGrid = function(container, config){
58729     config = config || {};
58730     var store = new Roo.grid.PropertyStore(this);
58731     this.store = store;
58732     var cm = new Roo.grid.PropertyColumnModel(this, store);
58733     store.store.sort('name', 'ASC');
58734     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58735         ds: store.store,
58736         cm: cm,
58737         enableColLock:false,
58738         enableColumnMove:false,
58739         stripeRows:false,
58740         trackMouseOver: false,
58741         clicksToEdit:1
58742     }, config));
58743     this.getGridEl().addClass('x-props-grid');
58744     this.lastEditRow = null;
58745     this.on('columnresize', this.onColumnResize, this);
58746     this.addEvents({
58747          /**
58748              * @event beforepropertychange
58749              * Fires before a property changes (return false to stop?)
58750              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58751              * @param {String} id Record Id
58752              * @param {String} newval New Value
58753          * @param {String} oldval Old Value
58754              */
58755         "beforepropertychange": true,
58756         /**
58757              * @event propertychange
58758              * Fires after a property changes
58759              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58760              * @param {String} id Record Id
58761              * @param {String} newval New Value
58762          * @param {String} oldval Old Value
58763              */
58764         "propertychange": true
58765     });
58766     this.customEditors = this.customEditors || {};
58767 };
58768 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58769     
58770      /**
58771      * @cfg {Object} customEditors map of colnames=> custom editors.
58772      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58773      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58774      * false disables editing of the field.
58775          */
58776     
58777       /**
58778      * @cfg {Object} propertyNames map of property Names to their displayed value
58779          */
58780     
58781     render : function(){
58782         Roo.grid.PropertyGrid.superclass.render.call(this);
58783         this.autoSize.defer(100, this);
58784     },
58785
58786     autoSize : function(){
58787         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58788         if(this.view){
58789             this.view.fitColumns();
58790         }
58791     },
58792
58793     onColumnResize : function(){
58794         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58795         this.autoSize();
58796     },
58797     /**
58798      * Sets the data for the Grid
58799      * accepts a Key => Value object of all the elements avaiable.
58800      * @param {Object} data  to appear in grid.
58801      */
58802     setSource : function(source){
58803         this.store.setSource(source);
58804         //this.autoSize();
58805     },
58806     /**
58807      * Gets all the data from the grid.
58808      * @return {Object} data  data stored in grid
58809      */
58810     getSource : function(){
58811         return this.store.getSource();
58812     }
58813 });/*
58814   
58815  * Licence LGPL
58816  
58817  */
58818  
58819 /**
58820  * @class Roo.grid.Calendar
58821  * @extends Roo.util.Grid
58822  * This class extends the Grid to provide a calendar widget
58823  * <br><br>Usage:<pre><code>
58824  var grid = new Roo.grid.Calendar("my-container-id", {
58825      ds: myDataStore,
58826      cm: myColModel,
58827      selModel: mySelectionModel,
58828      autoSizeColumns: true,
58829      monitorWindowResize: false,
58830      trackMouseOver: true
58831      eventstore : real data store..
58832  });
58833  // set any options
58834  grid.render();
58835   
58836   * @constructor
58837  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58838  * The container MUST have some type of size defined for the grid to fill. The container will be
58839  * automatically set to position relative if it isn't already.
58840  * @param {Object} config A config object that sets properties on this grid.
58841  */
58842 Roo.grid.Calendar = function(container, config){
58843         // initialize the container
58844         this.container = Roo.get(container);
58845         this.container.update("");
58846         this.container.setStyle("overflow", "hidden");
58847     this.container.addClass('x-grid-container');
58848
58849     this.id = this.container.id;
58850
58851     Roo.apply(this, config);
58852     // check and correct shorthanded configs
58853     
58854     var rows = [];
58855     var d =1;
58856     for (var r = 0;r < 6;r++) {
58857         
58858         rows[r]=[];
58859         for (var c =0;c < 7;c++) {
58860             rows[r][c]= '';
58861         }
58862     }
58863     if (this.eventStore) {
58864         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58865         this.eventStore.on('load',this.onLoad, this);
58866         this.eventStore.on('beforeload',this.clearEvents, this);
58867          
58868     }
58869     
58870     this.dataSource = new Roo.data.Store({
58871             proxy: new Roo.data.MemoryProxy(rows),
58872             reader: new Roo.data.ArrayReader({}, [
58873                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58874     });
58875
58876     this.dataSource.load();
58877     this.ds = this.dataSource;
58878     this.ds.xmodule = this.xmodule || false;
58879     
58880     
58881     var cellRender = function(v,x,r)
58882     {
58883         return String.format(
58884             '<div class="fc-day  fc-widget-content"><div>' +
58885                 '<div class="fc-event-container"></div>' +
58886                 '<div class="fc-day-number">{0}</div>'+
58887                 
58888                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58889             '</div></div>', v);
58890     
58891     }
58892     
58893     
58894     this.colModel = new Roo.grid.ColumnModel( [
58895         {
58896             xtype: 'ColumnModel',
58897             xns: Roo.grid,
58898             dataIndex : 'weekday0',
58899             header : 'Sunday',
58900             renderer : cellRender
58901         },
58902         {
58903             xtype: 'ColumnModel',
58904             xns: Roo.grid,
58905             dataIndex : 'weekday1',
58906             header : 'Monday',
58907             renderer : cellRender
58908         },
58909         {
58910             xtype: 'ColumnModel',
58911             xns: Roo.grid,
58912             dataIndex : 'weekday2',
58913             header : 'Tuesday',
58914             renderer : cellRender
58915         },
58916         {
58917             xtype: 'ColumnModel',
58918             xns: Roo.grid,
58919             dataIndex : 'weekday3',
58920             header : 'Wednesday',
58921             renderer : cellRender
58922         },
58923         {
58924             xtype: 'ColumnModel',
58925             xns: Roo.grid,
58926             dataIndex : 'weekday4',
58927             header : 'Thursday',
58928             renderer : cellRender
58929         },
58930         {
58931             xtype: 'ColumnModel',
58932             xns: Roo.grid,
58933             dataIndex : 'weekday5',
58934             header : 'Friday',
58935             renderer : cellRender
58936         },
58937         {
58938             xtype: 'ColumnModel',
58939             xns: Roo.grid,
58940             dataIndex : 'weekday6',
58941             header : 'Saturday',
58942             renderer : cellRender
58943         }
58944     ]);
58945     this.cm = this.colModel;
58946     this.cm.xmodule = this.xmodule || false;
58947  
58948         
58949           
58950     //this.selModel = new Roo.grid.CellSelectionModel();
58951     //this.sm = this.selModel;
58952     //this.selModel.init(this);
58953     
58954     
58955     if(this.width){
58956         this.container.setWidth(this.width);
58957     }
58958
58959     if(this.height){
58960         this.container.setHeight(this.height);
58961     }
58962     /** @private */
58963         this.addEvents({
58964         // raw events
58965         /**
58966          * @event click
58967          * The raw click event for the entire grid.
58968          * @param {Roo.EventObject} e
58969          */
58970         "click" : true,
58971         /**
58972          * @event dblclick
58973          * The raw dblclick event for the entire grid.
58974          * @param {Roo.EventObject} e
58975          */
58976         "dblclick" : true,
58977         /**
58978          * @event contextmenu
58979          * The raw contextmenu event for the entire grid.
58980          * @param {Roo.EventObject} e
58981          */
58982         "contextmenu" : true,
58983         /**
58984          * @event mousedown
58985          * The raw mousedown event for the entire grid.
58986          * @param {Roo.EventObject} e
58987          */
58988         "mousedown" : true,
58989         /**
58990          * @event mouseup
58991          * The raw mouseup event for the entire grid.
58992          * @param {Roo.EventObject} e
58993          */
58994         "mouseup" : true,
58995         /**
58996          * @event mouseover
58997          * The raw mouseover event for the entire grid.
58998          * @param {Roo.EventObject} e
58999          */
59000         "mouseover" : true,
59001         /**
59002          * @event mouseout
59003          * The raw mouseout event for the entire grid.
59004          * @param {Roo.EventObject} e
59005          */
59006         "mouseout" : true,
59007         /**
59008          * @event keypress
59009          * The raw keypress event for the entire grid.
59010          * @param {Roo.EventObject} e
59011          */
59012         "keypress" : true,
59013         /**
59014          * @event keydown
59015          * The raw keydown event for the entire grid.
59016          * @param {Roo.EventObject} e
59017          */
59018         "keydown" : true,
59019
59020         // custom events
59021
59022         /**
59023          * @event cellclick
59024          * Fires when a cell is clicked
59025          * @param {Grid} this
59026          * @param {Number} rowIndex
59027          * @param {Number} columnIndex
59028          * @param {Roo.EventObject} e
59029          */
59030         "cellclick" : true,
59031         /**
59032          * @event celldblclick
59033          * Fires when a cell is double clicked
59034          * @param {Grid} this
59035          * @param {Number} rowIndex
59036          * @param {Number} columnIndex
59037          * @param {Roo.EventObject} e
59038          */
59039         "celldblclick" : true,
59040         /**
59041          * @event rowclick
59042          * Fires when a row is clicked
59043          * @param {Grid} this
59044          * @param {Number} rowIndex
59045          * @param {Roo.EventObject} e
59046          */
59047         "rowclick" : true,
59048         /**
59049          * @event rowdblclick
59050          * Fires when a row is double clicked
59051          * @param {Grid} this
59052          * @param {Number} rowIndex
59053          * @param {Roo.EventObject} e
59054          */
59055         "rowdblclick" : true,
59056         /**
59057          * @event headerclick
59058          * Fires when a header is clicked
59059          * @param {Grid} this
59060          * @param {Number} columnIndex
59061          * @param {Roo.EventObject} e
59062          */
59063         "headerclick" : true,
59064         /**
59065          * @event headerdblclick
59066          * Fires when a header cell is double clicked
59067          * @param {Grid} this
59068          * @param {Number} columnIndex
59069          * @param {Roo.EventObject} e
59070          */
59071         "headerdblclick" : true,
59072         /**
59073          * @event rowcontextmenu
59074          * Fires when a row is right clicked
59075          * @param {Grid} this
59076          * @param {Number} rowIndex
59077          * @param {Roo.EventObject} e
59078          */
59079         "rowcontextmenu" : true,
59080         /**
59081          * @event cellcontextmenu
59082          * Fires when a cell is right clicked
59083          * @param {Grid} this
59084          * @param {Number} rowIndex
59085          * @param {Number} cellIndex
59086          * @param {Roo.EventObject} e
59087          */
59088          "cellcontextmenu" : true,
59089         /**
59090          * @event headercontextmenu
59091          * Fires when a header is right clicked
59092          * @param {Grid} this
59093          * @param {Number} columnIndex
59094          * @param {Roo.EventObject} e
59095          */
59096         "headercontextmenu" : true,
59097         /**
59098          * @event bodyscroll
59099          * Fires when the body element is scrolled
59100          * @param {Number} scrollLeft
59101          * @param {Number} scrollTop
59102          */
59103         "bodyscroll" : true,
59104         /**
59105          * @event columnresize
59106          * Fires when the user resizes a column
59107          * @param {Number} columnIndex
59108          * @param {Number} newSize
59109          */
59110         "columnresize" : true,
59111         /**
59112          * @event columnmove
59113          * Fires when the user moves a column
59114          * @param {Number} oldIndex
59115          * @param {Number} newIndex
59116          */
59117         "columnmove" : true,
59118         /**
59119          * @event startdrag
59120          * Fires when row(s) start being dragged
59121          * @param {Grid} this
59122          * @param {Roo.GridDD} dd The drag drop object
59123          * @param {event} e The raw browser event
59124          */
59125         "startdrag" : true,
59126         /**
59127          * @event enddrag
59128          * Fires when a drag operation is complete
59129          * @param {Grid} this
59130          * @param {Roo.GridDD} dd The drag drop object
59131          * @param {event} e The raw browser event
59132          */
59133         "enddrag" : true,
59134         /**
59135          * @event dragdrop
59136          * Fires when dragged row(s) are dropped on a valid DD target
59137          * @param {Grid} this
59138          * @param {Roo.GridDD} dd The drag drop object
59139          * @param {String} targetId The target drag drop object
59140          * @param {event} e The raw browser event
59141          */
59142         "dragdrop" : true,
59143         /**
59144          * @event dragover
59145          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59146          * @param {Grid} this
59147          * @param {Roo.GridDD} dd The drag drop object
59148          * @param {String} targetId The target drag drop object
59149          * @param {event} e The raw browser event
59150          */
59151         "dragover" : true,
59152         /**
59153          * @event dragenter
59154          *  Fires when the dragged row(s) first cross another DD target while being dragged
59155          * @param {Grid} this
59156          * @param {Roo.GridDD} dd The drag drop object
59157          * @param {String} targetId The target drag drop object
59158          * @param {event} e The raw browser event
59159          */
59160         "dragenter" : true,
59161         /**
59162          * @event dragout
59163          * Fires when the dragged row(s) leave another DD target while being dragged
59164          * @param {Grid} this
59165          * @param {Roo.GridDD} dd The drag drop object
59166          * @param {String} targetId The target drag drop object
59167          * @param {event} e The raw browser event
59168          */
59169         "dragout" : true,
59170         /**
59171          * @event rowclass
59172          * Fires when a row is rendered, so you can change add a style to it.
59173          * @param {GridView} gridview   The grid view
59174          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59175          */
59176         'rowclass' : true,
59177
59178         /**
59179          * @event render
59180          * Fires when the grid is rendered
59181          * @param {Grid} grid
59182          */
59183         'render' : true,
59184             /**
59185              * @event select
59186              * Fires when a date is selected
59187              * @param {DatePicker} this
59188              * @param {Date} date The selected date
59189              */
59190         'select': true,
59191         /**
59192              * @event monthchange
59193              * Fires when the displayed month changes 
59194              * @param {DatePicker} this
59195              * @param {Date} date The selected month
59196              */
59197         'monthchange': true,
59198         /**
59199              * @event evententer
59200              * Fires when mouse over an event
59201              * @param {Calendar} this
59202              * @param {event} Event
59203              */
59204         'evententer': true,
59205         /**
59206              * @event eventleave
59207              * Fires when the mouse leaves an
59208              * @param {Calendar} this
59209              * @param {event}
59210              */
59211         'eventleave': true,
59212         /**
59213              * @event eventclick
59214              * Fires when the mouse click an
59215              * @param {Calendar} this
59216              * @param {event}
59217              */
59218         'eventclick': true,
59219         /**
59220              * @event eventrender
59221              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59222              * @param {Calendar} this
59223              * @param {data} data to be modified
59224              */
59225         'eventrender': true
59226         
59227     });
59228
59229     Roo.grid.Grid.superclass.constructor.call(this);
59230     this.on('render', function() {
59231         this.view.el.addClass('x-grid-cal'); 
59232         
59233         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59234
59235     },this);
59236     
59237     if (!Roo.grid.Calendar.style) {
59238         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59239             
59240             
59241             '.x-grid-cal .x-grid-col' :  {
59242                 height: 'auto !important',
59243                 'vertical-align': 'top'
59244             },
59245             '.x-grid-cal  .fc-event-hori' : {
59246                 height: '14px'
59247             }
59248              
59249             
59250         }, Roo.id());
59251     }
59252
59253     
59254     
59255 };
59256 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59257     /**
59258      * @cfg {Store} eventStore The store that loads events.
59259      */
59260     eventStore : 25,
59261
59262      
59263     activeDate : false,
59264     startDay : 0,
59265     autoWidth : true,
59266     monitorWindowResize : false,
59267
59268     
59269     resizeColumns : function() {
59270         var col = (this.view.el.getWidth() / 7) - 3;
59271         // loop through cols, and setWidth
59272         for(var i =0 ; i < 7 ; i++){
59273             this.cm.setColumnWidth(i, col);
59274         }
59275     },
59276      setDate :function(date) {
59277         
59278         Roo.log('setDate?');
59279         
59280         this.resizeColumns();
59281         var vd = this.activeDate;
59282         this.activeDate = date;
59283 //        if(vd && this.el){
59284 //            var t = date.getTime();
59285 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59286 //                Roo.log('using add remove');
59287 //                
59288 //                this.fireEvent('monthchange', this, date);
59289 //                
59290 //                this.cells.removeClass("fc-state-highlight");
59291 //                this.cells.each(function(c){
59292 //                   if(c.dateValue == t){
59293 //                       c.addClass("fc-state-highlight");
59294 //                       setTimeout(function(){
59295 //                            try{c.dom.firstChild.focus();}catch(e){}
59296 //                       }, 50);
59297 //                       return false;
59298 //                   }
59299 //                   return true;
59300 //                });
59301 //                return;
59302 //            }
59303 //        }
59304         
59305         var days = date.getDaysInMonth();
59306         
59307         var firstOfMonth = date.getFirstDateOfMonth();
59308         var startingPos = firstOfMonth.getDay()-this.startDay;
59309         
59310         if(startingPos < this.startDay){
59311             startingPos += 7;
59312         }
59313         
59314         var pm = date.add(Date.MONTH, -1);
59315         var prevStart = pm.getDaysInMonth()-startingPos;
59316 //        
59317         
59318         
59319         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59320         
59321         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59322         //this.cells.addClassOnOver('fc-state-hover');
59323         
59324         var cells = this.cells.elements;
59325         var textEls = this.textNodes;
59326         
59327         //Roo.each(cells, function(cell){
59328         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59329         //});
59330         
59331         days += startingPos;
59332
59333         // convert everything to numbers so it's fast
59334         var day = 86400000;
59335         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59336         //Roo.log(d);
59337         //Roo.log(pm);
59338         //Roo.log(prevStart);
59339         
59340         var today = new Date().clearTime().getTime();
59341         var sel = date.clearTime().getTime();
59342         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59343         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59344         var ddMatch = this.disabledDatesRE;
59345         var ddText = this.disabledDatesText;
59346         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59347         var ddaysText = this.disabledDaysText;
59348         var format = this.format;
59349         
59350         var setCellClass = function(cal, cell){
59351             
59352             //Roo.log('set Cell Class');
59353             cell.title = "";
59354             var t = d.getTime();
59355             
59356             //Roo.log(d);
59357             
59358             
59359             cell.dateValue = t;
59360             if(t == today){
59361                 cell.className += " fc-today";
59362                 cell.className += " fc-state-highlight";
59363                 cell.title = cal.todayText;
59364             }
59365             if(t == sel){
59366                 // disable highlight in other month..
59367                 cell.className += " fc-state-highlight";
59368                 
59369             }
59370             // disabling
59371             if(t < min) {
59372                 //cell.className = " fc-state-disabled";
59373                 cell.title = cal.minText;
59374                 return;
59375             }
59376             if(t > max) {
59377                 //cell.className = " fc-state-disabled";
59378                 cell.title = cal.maxText;
59379                 return;
59380             }
59381             if(ddays){
59382                 if(ddays.indexOf(d.getDay()) != -1){
59383                     // cell.title = ddaysText;
59384                    // cell.className = " fc-state-disabled";
59385                 }
59386             }
59387             if(ddMatch && format){
59388                 var fvalue = d.dateFormat(format);
59389                 if(ddMatch.test(fvalue)){
59390                     cell.title = ddText.replace("%0", fvalue);
59391                    cell.className = " fc-state-disabled";
59392                 }
59393             }
59394             
59395             if (!cell.initialClassName) {
59396                 cell.initialClassName = cell.dom.className;
59397             }
59398             
59399             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59400         };
59401
59402         var i = 0;
59403         
59404         for(; i < startingPos; i++) {
59405             cells[i].dayName =  (++prevStart);
59406             Roo.log(textEls[i]);
59407             d.setDate(d.getDate()+1);
59408             
59409             //cells[i].className = "fc-past fc-other-month";
59410             setCellClass(this, cells[i]);
59411         }
59412         
59413         var intDay = 0;
59414         
59415         for(; i < days; i++){
59416             intDay = i - startingPos + 1;
59417             cells[i].dayName =  (intDay);
59418             d.setDate(d.getDate()+1);
59419             
59420             cells[i].className = ''; // "x-date-active";
59421             setCellClass(this, cells[i]);
59422         }
59423         var extraDays = 0;
59424         
59425         for(; i < 42; i++) {
59426             //textEls[i].innerHTML = (++extraDays);
59427             
59428             d.setDate(d.getDate()+1);
59429             cells[i].dayName = (++extraDays);
59430             cells[i].className = "fc-future fc-other-month";
59431             setCellClass(this, cells[i]);
59432         }
59433         
59434         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59435         
59436         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59437         
59438         // this will cause all the cells to mis
59439         var rows= [];
59440         var i =0;
59441         for (var r = 0;r < 6;r++) {
59442             for (var c =0;c < 7;c++) {
59443                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59444             }    
59445         }
59446         
59447         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59448         for(i=0;i<cells.length;i++) {
59449             
59450             this.cells.elements[i].dayName = cells[i].dayName ;
59451             this.cells.elements[i].className = cells[i].className;
59452             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59453             this.cells.elements[i].title = cells[i].title ;
59454             this.cells.elements[i].dateValue = cells[i].dateValue ;
59455         }
59456         
59457         
59458         
59459         
59460         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59461         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59462         
59463         ////if(totalRows != 6){
59464             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59465            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59466        // }
59467         
59468         this.fireEvent('monthchange', this, date);
59469         
59470         
59471     },
59472  /**
59473      * Returns the grid's SelectionModel.
59474      * @return {SelectionModel}
59475      */
59476     getSelectionModel : function(){
59477         if(!this.selModel){
59478             this.selModel = new Roo.grid.CellSelectionModel();
59479         }
59480         return this.selModel;
59481     },
59482
59483     load: function() {
59484         this.eventStore.load()
59485         
59486         
59487         
59488     },
59489     
59490     findCell : function(dt) {
59491         dt = dt.clearTime().getTime();
59492         var ret = false;
59493         this.cells.each(function(c){
59494             //Roo.log("check " +c.dateValue + '?=' + dt);
59495             if(c.dateValue == dt){
59496                 ret = c;
59497                 return false;
59498             }
59499             return true;
59500         });
59501         
59502         return ret;
59503     },
59504     
59505     findCells : function(rec) {
59506         var s = rec.data.start_dt.clone().clearTime().getTime();
59507        // Roo.log(s);
59508         var e= rec.data.end_dt.clone().clearTime().getTime();
59509        // Roo.log(e);
59510         var ret = [];
59511         this.cells.each(function(c){
59512              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59513             
59514             if(c.dateValue > e){
59515                 return ;
59516             }
59517             if(c.dateValue < s){
59518                 return ;
59519             }
59520             ret.push(c);
59521         });
59522         
59523         return ret;    
59524     },
59525     
59526     findBestRow: function(cells)
59527     {
59528         var ret = 0;
59529         
59530         for (var i =0 ; i < cells.length;i++) {
59531             ret  = Math.max(cells[i].rows || 0,ret);
59532         }
59533         return ret;
59534         
59535     },
59536     
59537     
59538     addItem : function(rec)
59539     {
59540         // look for vertical location slot in
59541         var cells = this.findCells(rec);
59542         
59543         rec.row = this.findBestRow(cells);
59544         
59545         // work out the location.
59546         
59547         var crow = false;
59548         var rows = [];
59549         for(var i =0; i < cells.length; i++) {
59550             if (!crow) {
59551                 crow = {
59552                     start : cells[i],
59553                     end :  cells[i]
59554                 };
59555                 continue;
59556             }
59557             if (crow.start.getY() == cells[i].getY()) {
59558                 // on same row.
59559                 crow.end = cells[i];
59560                 continue;
59561             }
59562             // different row.
59563             rows.push(crow);
59564             crow = {
59565                 start: cells[i],
59566                 end : cells[i]
59567             };
59568             
59569         }
59570         
59571         rows.push(crow);
59572         rec.els = [];
59573         rec.rows = rows;
59574         rec.cells = cells;
59575         for (var i = 0; i < cells.length;i++) {
59576             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59577             
59578         }
59579         
59580         
59581     },
59582     
59583     clearEvents: function() {
59584         
59585         if (!this.eventStore.getCount()) {
59586             return;
59587         }
59588         // reset number of rows in cells.
59589         Roo.each(this.cells.elements, function(c){
59590             c.rows = 0;
59591         });
59592         
59593         this.eventStore.each(function(e) {
59594             this.clearEvent(e);
59595         },this);
59596         
59597     },
59598     
59599     clearEvent : function(ev)
59600     {
59601         if (ev.els) {
59602             Roo.each(ev.els, function(el) {
59603                 el.un('mouseenter' ,this.onEventEnter, this);
59604                 el.un('mouseleave' ,this.onEventLeave, this);
59605                 el.remove();
59606             },this);
59607             ev.els = [];
59608         }
59609     },
59610     
59611     
59612     renderEvent : function(ev,ctr) {
59613         if (!ctr) {
59614              ctr = this.view.el.select('.fc-event-container',true).first();
59615         }
59616         
59617          
59618         this.clearEvent(ev);
59619             //code
59620        
59621         
59622         
59623         ev.els = [];
59624         var cells = ev.cells;
59625         var rows = ev.rows;
59626         this.fireEvent('eventrender', this, ev);
59627         
59628         for(var i =0; i < rows.length; i++) {
59629             
59630             cls = '';
59631             if (i == 0) {
59632                 cls += ' fc-event-start';
59633             }
59634             if ((i+1) == rows.length) {
59635                 cls += ' fc-event-end';
59636             }
59637             
59638             //Roo.log(ev.data);
59639             // how many rows should it span..
59640             var cg = this.eventTmpl.append(ctr,Roo.apply({
59641                 fccls : cls
59642                 
59643             }, ev.data) , true);
59644             
59645             
59646             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59647             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59648             cg.on('click', this.onEventClick, this, ev);
59649             
59650             ev.els.push(cg);
59651             
59652             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59653             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59654             //Roo.log(cg);
59655              
59656             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59657             cg.setWidth(ebox.right - sbox.x -2);
59658         }
59659     },
59660     
59661     renderEvents: function()
59662     {   
59663         // first make sure there is enough space..
59664         
59665         if (!this.eventTmpl) {
59666             this.eventTmpl = new Roo.Template(
59667                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59668                     '<div class="fc-event-inner">' +
59669                         '<span class="fc-event-time">{time}</span>' +
59670                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59671                     '</div>' +
59672                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59673                 '</div>'
59674             );
59675                 
59676         }
59677                
59678         
59679         
59680         this.cells.each(function(c) {
59681             //Roo.log(c.select('.fc-day-content div',true).first());
59682             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59683         });
59684         
59685         var ctr = this.view.el.select('.fc-event-container',true).first();
59686         
59687         var cls;
59688         this.eventStore.each(function(ev){
59689             
59690             this.renderEvent(ev);
59691              
59692              
59693         }, this);
59694         this.view.layout();
59695         
59696     },
59697     
59698     onEventEnter: function (e, el,event,d) {
59699         this.fireEvent('evententer', this, el, event);
59700     },
59701     
59702     onEventLeave: function (e, el,event,d) {
59703         this.fireEvent('eventleave', this, el, event);
59704     },
59705     
59706     onEventClick: function (e, el,event,d) {
59707         this.fireEvent('eventclick', this, el, event);
59708     },
59709     
59710     onMonthChange: function () {
59711         this.store.load();
59712     },
59713     
59714     onLoad: function () {
59715         
59716         //Roo.log('calendar onload');
59717 //         
59718         if(this.eventStore.getCount() > 0){
59719             
59720            
59721             
59722             this.eventStore.each(function(d){
59723                 
59724                 
59725                 // FIXME..
59726                 var add =   d.data;
59727                 if (typeof(add.end_dt) == 'undefined')  {
59728                     Roo.log("Missing End time in calendar data: ");
59729                     Roo.log(d);
59730                     return;
59731                 }
59732                 if (typeof(add.start_dt) == 'undefined')  {
59733                     Roo.log("Missing Start time in calendar data: ");
59734                     Roo.log(d);
59735                     return;
59736                 }
59737                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59738                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59739                 add.id = add.id || d.id;
59740                 add.title = add.title || '??';
59741                 
59742                 this.addItem(d);
59743                 
59744              
59745             },this);
59746         }
59747         
59748         this.renderEvents();
59749     }
59750     
59751
59752 });
59753 /*
59754  grid : {
59755                 xtype: 'Grid',
59756                 xns: Roo.grid,
59757                 listeners : {
59758                     render : function ()
59759                     {
59760                         _this.grid = this;
59761                         
59762                         if (!this.view.el.hasClass('course-timesheet')) {
59763                             this.view.el.addClass('course-timesheet');
59764                         }
59765                         if (this.tsStyle) {
59766                             this.ds.load({});
59767                             return; 
59768                         }
59769                         Roo.log('width');
59770                         Roo.log(_this.grid.view.el.getWidth());
59771                         
59772                         
59773                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59774                             '.course-timesheet .x-grid-row' : {
59775                                 height: '80px'
59776                             },
59777                             '.x-grid-row td' : {
59778                                 'vertical-align' : 0
59779                             },
59780                             '.course-edit-link' : {
59781                                 'color' : 'blue',
59782                                 'text-overflow' : 'ellipsis',
59783                                 'overflow' : 'hidden',
59784                                 'white-space' : 'nowrap',
59785                                 'cursor' : 'pointer'
59786                             },
59787                             '.sub-link' : {
59788                                 'color' : 'green'
59789                             },
59790                             '.de-act-sup-link' : {
59791                                 'color' : 'purple',
59792                                 'text-decoration' : 'line-through'
59793                             },
59794                             '.de-act-link' : {
59795                                 'color' : 'red',
59796                                 'text-decoration' : 'line-through'
59797                             },
59798                             '.course-timesheet .course-highlight' : {
59799                                 'border-top-style': 'dashed !important',
59800                                 'border-bottom-bottom': 'dashed !important'
59801                             },
59802                             '.course-timesheet .course-item' : {
59803                                 'font-family'   : 'tahoma, arial, helvetica',
59804                                 'font-size'     : '11px',
59805                                 'overflow'      : 'hidden',
59806                                 'padding-left'  : '10px',
59807                                 'padding-right' : '10px',
59808                                 'padding-top' : '10px' 
59809                             }
59810                             
59811                         }, Roo.id());
59812                                 this.ds.load({});
59813                     }
59814                 },
59815                 autoWidth : true,
59816                 monitorWindowResize : false,
59817                 cellrenderer : function(v,x,r)
59818                 {
59819                     return v;
59820                 },
59821                 sm : {
59822                     xtype: 'CellSelectionModel',
59823                     xns: Roo.grid
59824                 },
59825                 dataSource : {
59826                     xtype: 'Store',
59827                     xns: Roo.data,
59828                     listeners : {
59829                         beforeload : function (_self, options)
59830                         {
59831                             options.params = options.params || {};
59832                             options.params._month = _this.monthField.getValue();
59833                             options.params.limit = 9999;
59834                             options.params['sort'] = 'when_dt';    
59835                             options.params['dir'] = 'ASC';    
59836                             this.proxy.loadResponse = this.loadResponse;
59837                             Roo.log("load?");
59838                             //this.addColumns();
59839                         },
59840                         load : function (_self, records, options)
59841                         {
59842                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59843                                 // if you click on the translation.. you can edit it...
59844                                 var el = Roo.get(this);
59845                                 var id = el.dom.getAttribute('data-id');
59846                                 var d = el.dom.getAttribute('data-date');
59847                                 var t = el.dom.getAttribute('data-time');
59848                                 //var id = this.child('span').dom.textContent;
59849                                 
59850                                 //Roo.log(this);
59851                                 Pman.Dialog.CourseCalendar.show({
59852                                     id : id,
59853                                     when_d : d,
59854                                     when_t : t,
59855                                     productitem_active : id ? 1 : 0
59856                                 }, function() {
59857                                     _this.grid.ds.load({});
59858                                 });
59859                            
59860                            });
59861                            
59862                            _this.panel.fireEvent('resize', [ '', '' ]);
59863                         }
59864                     },
59865                     loadResponse : function(o, success, response){
59866                             // this is overridden on before load..
59867                             
59868                             Roo.log("our code?");       
59869                             //Roo.log(success);
59870                             //Roo.log(response)
59871                             delete this.activeRequest;
59872                             if(!success){
59873                                 this.fireEvent("loadexception", this, o, response);
59874                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59875                                 return;
59876                             }
59877                             var result;
59878                             try {
59879                                 result = o.reader.read(response);
59880                             }catch(e){
59881                                 Roo.log("load exception?");
59882                                 this.fireEvent("loadexception", this, o, response, e);
59883                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59884                                 return;
59885                             }
59886                             Roo.log("ready...");        
59887                             // loop through result.records;
59888                             // and set this.tdate[date] = [] << array of records..
59889                             _this.tdata  = {};
59890                             Roo.each(result.records, function(r){
59891                                 //Roo.log(r.data);
59892                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59893                                     _this.tdata[r.data.when_dt.format('j')] = [];
59894                                 }
59895                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59896                             });
59897                             
59898                             //Roo.log(_this.tdata);
59899                             
59900                             result.records = [];
59901                             result.totalRecords = 6;
59902                     
59903                             // let's generate some duumy records for the rows.
59904                             //var st = _this.dateField.getValue();
59905                             
59906                             // work out monday..
59907                             //st = st.add(Date.DAY, -1 * st.format('w'));
59908                             
59909                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59910                             
59911                             var firstOfMonth = date.getFirstDayOfMonth();
59912                             var days = date.getDaysInMonth();
59913                             var d = 1;
59914                             var firstAdded = false;
59915                             for (var i = 0; i < result.totalRecords ; i++) {
59916                                 //var d= st.add(Date.DAY, i);
59917                                 var row = {};
59918                                 var added = 0;
59919                                 for(var w = 0 ; w < 7 ; w++){
59920                                     if(!firstAdded && firstOfMonth != w){
59921                                         continue;
59922                                     }
59923                                     if(d > days){
59924                                         continue;
59925                                     }
59926                                     firstAdded = true;
59927                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59928                                     row['weekday'+w] = String.format(
59929                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59930                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59931                                                     d,
59932                                                     date.format('Y-m-')+dd
59933                                                 );
59934                                     added++;
59935                                     if(typeof(_this.tdata[d]) != 'undefined'){
59936                                         Roo.each(_this.tdata[d], function(r){
59937                                             var is_sub = '';
59938                                             var deactive = '';
59939                                             var id = r.id;
59940                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59941                                             if(r.parent_id*1>0){
59942                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59943                                                 id = r.parent_id;
59944                                             }
59945                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59946                                                 deactive = 'de-act-link';
59947                                             }
59948                                             
59949                                             row['weekday'+w] += String.format(
59950                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59951                                                     id, //0
59952                                                     r.product_id_name, //1
59953                                                     r.when_dt.format('h:ia'), //2
59954                                                     is_sub, //3
59955                                                     deactive, //4
59956                                                     desc // 5
59957                                             );
59958                                         });
59959                                     }
59960                                     d++;
59961                                 }
59962                                 
59963                                 // only do this if something added..
59964                                 if(added > 0){ 
59965                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59966                                 }
59967                                 
59968                                 
59969                                 // push it twice. (second one with an hour..
59970                                 
59971                             }
59972                             //Roo.log(result);
59973                             this.fireEvent("load", this, o, o.request.arg);
59974                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59975                         },
59976                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59977                     proxy : {
59978                         xtype: 'HttpProxy',
59979                         xns: Roo.data,
59980                         method : 'GET',
59981                         url : baseURL + '/Roo/Shop_course.php'
59982                     },
59983                     reader : {
59984                         xtype: 'JsonReader',
59985                         xns: Roo.data,
59986                         id : 'id',
59987                         fields : [
59988                             {
59989                                 'name': 'id',
59990                                 'type': 'int'
59991                             },
59992                             {
59993                                 'name': 'when_dt',
59994                                 'type': 'string'
59995                             },
59996                             {
59997                                 'name': 'end_dt',
59998                                 'type': 'string'
59999                             },
60000                             {
60001                                 'name': 'parent_id',
60002                                 'type': 'int'
60003                             },
60004                             {
60005                                 'name': 'product_id',
60006                                 'type': 'int'
60007                             },
60008                             {
60009                                 'name': 'productitem_id',
60010                                 'type': 'int'
60011                             },
60012                             {
60013                                 'name': 'guid',
60014                                 'type': 'int'
60015                             }
60016                         ]
60017                     }
60018                 },
60019                 toolbar : {
60020                     xtype: 'Toolbar',
60021                     xns: Roo,
60022                     items : [
60023                         {
60024                             xtype: 'Button',
60025                             xns: Roo.Toolbar,
60026                             listeners : {
60027                                 click : function (_self, e)
60028                                 {
60029                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60030                                     sd.setMonth(sd.getMonth()-1);
60031                                     _this.monthField.setValue(sd.format('Y-m-d'));
60032                                     _this.grid.ds.load({});
60033                                 }
60034                             },
60035                             text : "Back"
60036                         },
60037                         {
60038                             xtype: 'Separator',
60039                             xns: Roo.Toolbar
60040                         },
60041                         {
60042                             xtype: 'MonthField',
60043                             xns: Roo.form,
60044                             listeners : {
60045                                 render : function (_self)
60046                                 {
60047                                     _this.monthField = _self;
60048                                    // _this.monthField.set  today
60049                                 },
60050                                 select : function (combo, date)
60051                                 {
60052                                     _this.grid.ds.load({});
60053                                 }
60054                             },
60055                             value : (function() { return new Date(); })()
60056                         },
60057                         {
60058                             xtype: 'Separator',
60059                             xns: Roo.Toolbar
60060                         },
60061                         {
60062                             xtype: 'TextItem',
60063                             xns: Roo.Toolbar,
60064                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60065                         },
60066                         {
60067                             xtype: 'Fill',
60068                             xns: Roo.Toolbar
60069                         },
60070                         {
60071                             xtype: 'Button',
60072                             xns: Roo.Toolbar,
60073                             listeners : {
60074                                 click : function (_self, e)
60075                                 {
60076                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60077                                     sd.setMonth(sd.getMonth()+1);
60078                                     _this.monthField.setValue(sd.format('Y-m-d'));
60079                                     _this.grid.ds.load({});
60080                                 }
60081                             },
60082                             text : "Next"
60083                         }
60084                     ]
60085                 },
60086                  
60087             }
60088         };
60089         
60090         *//*
60091  * Based on:
60092  * Ext JS Library 1.1.1
60093  * Copyright(c) 2006-2007, Ext JS, LLC.
60094  *
60095  * Originally Released Under LGPL - original licence link has changed is not relivant.
60096  *
60097  * Fork - LGPL
60098  * <script type="text/javascript">
60099  */
60100  
60101 /**
60102  * @class Roo.LoadMask
60103  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60104  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60105  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60106  * element's UpdateManager load indicator and will be destroyed after the initial load.
60107  * @constructor
60108  * Create a new LoadMask
60109  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60110  * @param {Object} config The config object
60111  */
60112 Roo.LoadMask = function(el, config){
60113     this.el = Roo.get(el);
60114     Roo.apply(this, config);
60115     if(this.store){
60116         this.store.on('beforeload', this.onBeforeLoad, this);
60117         this.store.on('load', this.onLoad, this);
60118         this.store.on('loadexception', this.onLoadException, this);
60119         this.removeMask = false;
60120     }else{
60121         var um = this.el.getUpdateManager();
60122         um.showLoadIndicator = false; // disable the default indicator
60123         um.on('beforeupdate', this.onBeforeLoad, this);
60124         um.on('update', this.onLoad, this);
60125         um.on('failure', this.onLoad, this);
60126         this.removeMask = true;
60127     }
60128 };
60129
60130 Roo.LoadMask.prototype = {
60131     /**
60132      * @cfg {Boolean} removeMask
60133      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60134      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60135      */
60136     /**
60137      * @cfg {String} msg
60138      * The text to display in a centered loading message box (defaults to 'Loading...')
60139      */
60140     msg : 'Loading...',
60141     /**
60142      * @cfg {String} msgCls
60143      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60144      */
60145     msgCls : 'x-mask-loading',
60146
60147     /**
60148      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60149      * @type Boolean
60150      */
60151     disabled: false,
60152
60153     /**
60154      * Disables the mask to prevent it from being displayed
60155      */
60156     disable : function(){
60157        this.disabled = true;
60158     },
60159
60160     /**
60161      * Enables the mask so that it can be displayed
60162      */
60163     enable : function(){
60164         this.disabled = false;
60165     },
60166     
60167     onLoadException : function()
60168     {
60169         Roo.log(arguments);
60170         
60171         if (typeof(arguments[3]) != 'undefined') {
60172             Roo.MessageBox.alert("Error loading",arguments[3]);
60173         } 
60174         /*
60175         try {
60176             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60177                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60178             }   
60179         } catch(e) {
60180             
60181         }
60182         */
60183     
60184         
60185         
60186         this.el.unmask(this.removeMask);
60187     },
60188     // private
60189     onLoad : function()
60190     {
60191         this.el.unmask(this.removeMask);
60192     },
60193
60194     // private
60195     onBeforeLoad : function(){
60196         if(!this.disabled){
60197             this.el.mask(this.msg, this.msgCls);
60198         }
60199     },
60200
60201     // private
60202     destroy : function(){
60203         if(this.store){
60204             this.store.un('beforeload', this.onBeforeLoad, this);
60205             this.store.un('load', this.onLoad, this);
60206             this.store.un('loadexception', this.onLoadException, this);
60207         }else{
60208             var um = this.el.getUpdateManager();
60209             um.un('beforeupdate', this.onBeforeLoad, this);
60210             um.un('update', this.onLoad, this);
60211             um.un('failure', this.onLoad, this);
60212         }
60213     }
60214 };/*
60215  * Based on:
60216  * Ext JS Library 1.1.1
60217  * Copyright(c) 2006-2007, Ext JS, LLC.
60218  *
60219  * Originally Released Under LGPL - original licence link has changed is not relivant.
60220  *
60221  * Fork - LGPL
60222  * <script type="text/javascript">
60223  */
60224
60225
60226 /**
60227  * @class Roo.XTemplate
60228  * @extends Roo.Template
60229  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60230 <pre><code>
60231 var t = new Roo.XTemplate(
60232         '&lt;select name="{name}"&gt;',
60233                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60234         '&lt;/select&gt;'
60235 );
60236  
60237 // then append, applying the master template values
60238  </code></pre>
60239  *
60240  * Supported features:
60241  *
60242  *  Tags:
60243
60244 <pre><code>
60245       {a_variable} - output encoded.
60246       {a_variable.format:("Y-m-d")} - call a method on the variable
60247       {a_variable:raw} - unencoded output
60248       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60249       {a_variable:this.method_on_template(...)} - call a method on the template object.
60250  
60251 </code></pre>
60252  *  The tpl tag:
60253 <pre><code>
60254         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60255         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60256         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60257         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60258   
60259         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60260         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60261 </code></pre>
60262  *      
60263  */
60264 Roo.XTemplate = function()
60265 {
60266     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60267     if (this.html) {
60268         this.compile();
60269     }
60270 };
60271
60272
60273 Roo.extend(Roo.XTemplate, Roo.Template, {
60274
60275     /**
60276      * The various sub templates
60277      */
60278     tpls : false,
60279     /**
60280      *
60281      * basic tag replacing syntax
60282      * WORD:WORD()
60283      *
60284      * // you can fake an object call by doing this
60285      *  x.t:(test,tesT) 
60286      * 
60287      */
60288     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60289
60290     /**
60291      * compile the template
60292      *
60293      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60294      *
60295      */
60296     compile: function()
60297     {
60298         var s = this.html;
60299      
60300         s = ['<tpl>', s, '</tpl>'].join('');
60301     
60302         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60303             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60304             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60305             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60306             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60307             m,
60308             id     = 0,
60309             tpls   = [];
60310     
60311         while(true == !!(m = s.match(re))){
60312             var forMatch   = m[0].match(nameRe),
60313                 ifMatch   = m[0].match(ifRe),
60314                 execMatch   = m[0].match(execRe),
60315                 namedMatch   = m[0].match(namedRe),
60316                 
60317                 exp  = null, 
60318                 fn   = null,
60319                 exec = null,
60320                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60321                 
60322             if (ifMatch) {
60323                 // if - puts fn into test..
60324                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60325                 if(exp){
60326                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60327                 }
60328             }
60329             
60330             if (execMatch) {
60331                 // exec - calls a function... returns empty if true is  returned.
60332                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60333                 if(exp){
60334                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60335                 }
60336             }
60337             
60338             
60339             if (name) {
60340                 // for = 
60341                 switch(name){
60342                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60343                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60344                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60345                 }
60346             }
60347             var uid = namedMatch ? namedMatch[1] : id;
60348             
60349             
60350             tpls.push({
60351                 id:     namedMatch ? namedMatch[1] : id,
60352                 target: name,
60353                 exec:   exec,
60354                 test:   fn,
60355                 body:   m[1] || ''
60356             });
60357             if (namedMatch) {
60358                 s = s.replace(m[0], '');
60359             } else { 
60360                 s = s.replace(m[0], '{xtpl'+ id + '}');
60361             }
60362             ++id;
60363         }
60364         this.tpls = [];
60365         for(var i = tpls.length-1; i >= 0; --i){
60366             this.compileTpl(tpls[i]);
60367             this.tpls[tpls[i].id] = tpls[i];
60368         }
60369         this.master = tpls[tpls.length-1];
60370         return this;
60371     },
60372     /**
60373      * same as applyTemplate, except it's done to one of the subTemplates
60374      * when using named templates, you can do:
60375      *
60376      * var str = pl.applySubTemplate('your-name', values);
60377      *
60378      * 
60379      * @param {Number} id of the template
60380      * @param {Object} values to apply to template
60381      * @param {Object} parent (normaly the instance of this object)
60382      */
60383     applySubTemplate : function(id, values, parent)
60384     {
60385         
60386         
60387         var t = this.tpls[id];
60388         
60389         
60390         try { 
60391             if(t.test && !t.test.call(this, values, parent)){
60392                 return '';
60393             }
60394         } catch(e) {
60395             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60396             Roo.log(e.toString());
60397             Roo.log(t.test);
60398             return ''
60399         }
60400         try { 
60401             
60402             if(t.exec && t.exec.call(this, values, parent)){
60403                 return '';
60404             }
60405         } catch(e) {
60406             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60407             Roo.log(e.toString());
60408             Roo.log(t.exec);
60409             return ''
60410         }
60411         try {
60412             var vs = t.target ? t.target.call(this, values, parent) : values;
60413             parent = t.target ? values : parent;
60414             if(t.target && vs instanceof Array){
60415                 var buf = [];
60416                 for(var i = 0, len = vs.length; i < len; i++){
60417                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60418                 }
60419                 return buf.join('');
60420             }
60421             return t.compiled.call(this, vs, parent);
60422         } catch (e) {
60423             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60424             Roo.log(e.toString());
60425             Roo.log(t.compiled);
60426             return '';
60427         }
60428     },
60429
60430     compileTpl : function(tpl)
60431     {
60432         var fm = Roo.util.Format;
60433         var useF = this.disableFormats !== true;
60434         var sep = Roo.isGecko ? "+" : ",";
60435         var undef = function(str) {
60436             Roo.log("Property not found :"  + str);
60437             return '';
60438         };
60439         
60440         var fn = function(m, name, format, args)
60441         {
60442             //Roo.log(arguments);
60443             args = args ? args.replace(/\\'/g,"'") : args;
60444             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60445             if (typeof(format) == 'undefined') {
60446                 format= 'htmlEncode';
60447             }
60448             if (format == 'raw' ) {
60449                 format = false;
60450             }
60451             
60452             if(name.substr(0, 4) == 'xtpl'){
60453                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60454             }
60455             
60456             // build an array of options to determine if value is undefined..
60457             
60458             // basically get 'xxxx.yyyy' then do
60459             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60460             //    (function () { Roo.log("Property not found"); return ''; })() :
60461             //    ......
60462             
60463             var udef_ar = [];
60464             var lookfor = '';
60465             Roo.each(name.split('.'), function(st) {
60466                 lookfor += (lookfor.length ? '.': '') + st;
60467                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60468             });
60469             
60470             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60471             
60472             
60473             if(format && useF){
60474                 
60475                 args = args ? ',' + args : "";
60476                  
60477                 if(format.substr(0, 5) != "this."){
60478                     format = "fm." + format + '(';
60479                 }else{
60480                     format = 'this.call("'+ format.substr(5) + '", ';
60481                     args = ", values";
60482                 }
60483                 
60484                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60485             }
60486              
60487             if (args.length) {
60488                 // called with xxyx.yuu:(test,test)
60489                 // change to ()
60490                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60491             }
60492             // raw.. - :raw modifier..
60493             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60494             
60495         };
60496         var body;
60497         // branched to use + in gecko and [].join() in others
60498         if(Roo.isGecko){
60499             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60500                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60501                     "';};};";
60502         }else{
60503             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60504             body.push(tpl.body.replace(/(\r\n|\n)/g,
60505                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60506             body.push("'].join('');};};");
60507             body = body.join('');
60508         }
60509         
60510         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60511        
60512         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60513         eval(body);
60514         
60515         return this;
60516     },
60517
60518     applyTemplate : function(values){
60519         return this.master.compiled.call(this, values, {});
60520         //var s = this.subs;
60521     },
60522
60523     apply : function(){
60524         return this.applyTemplate.apply(this, arguments);
60525     }
60526
60527  });
60528
60529 Roo.XTemplate.from = function(el){
60530     el = Roo.getDom(el);
60531     return new Roo.XTemplate(el.value || el.innerHTML);
60532 };