4e48176b841debd219ed472d6cc67304f9d72f01
[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         if(!this.activated){
43254             return;
43255         }
43256         /*
43257         if(Roo.isIE){
43258             this.win.focus();
43259             var r = this.doc.selection.createRange();
43260             if(r){
43261                 r.collapse(true);
43262                 r.pasteHTML(text);
43263                 this.syncValue();
43264                 this.deferFocus();
43265             
43266             }
43267             return;
43268         }
43269         */
43270         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43271             this.win.focus();
43272             
43273             
43274             // from jquery ui (MIT licenced)
43275             var range, node;
43276             var win = this.win;
43277             
43278             if (win.getSelection && win.getSelection().getRangeAt) {
43279                 range = win.getSelection().getRangeAt(0);
43280                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43281                 range.insertNode(node);
43282             } else if (win.document.selection && win.document.selection.createRange) {
43283                 // no firefox support
43284                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43285                 win.document.selection.createRange().pasteHTML(txt);
43286             } else {
43287                 // no firefox support
43288                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43289                 this.execCmd('InsertHTML', txt);
43290             } 
43291             
43292             this.syncValue();
43293             
43294             this.deferFocus();
43295         }
43296     },
43297  // private
43298     mozKeyPress : function(e){
43299         if(e.ctrlKey){
43300             var c = e.getCharCode(), cmd;
43301           
43302             if(c > 0){
43303                 c = String.fromCharCode(c).toLowerCase();
43304                 switch(c){
43305                     case 'b':
43306                         cmd = 'bold';
43307                         break;
43308                     case 'i':
43309                         cmd = 'italic';
43310                         break;
43311                     
43312                     case 'u':
43313                         cmd = 'underline';
43314                         break;
43315                     
43316                     case 'v':
43317                         this.cleanUpPaste.defer(100, this);
43318                         return;
43319                         
43320                 }
43321                 if(cmd){
43322                     this.win.focus();
43323                     this.execCmd(cmd);
43324                     this.deferFocus();
43325                     e.preventDefault();
43326                 }
43327                 
43328             }
43329         }
43330     },
43331
43332     // private
43333     fixKeys : function(){ // load time branching for fastest keydown performance
43334         if(Roo.isIE){
43335             return function(e){
43336                 var k = e.getKey(), r;
43337                 if(k == e.TAB){
43338                     e.stopEvent();
43339                     r = this.doc.selection.createRange();
43340                     if(r){
43341                         r.collapse(true);
43342                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43343                         this.deferFocus();
43344                     }
43345                     return;
43346                 }
43347                 
43348                 if(k == e.ENTER){
43349                     r = this.doc.selection.createRange();
43350                     if(r){
43351                         var target = r.parentElement();
43352                         if(!target || target.tagName.toLowerCase() != 'li'){
43353                             e.stopEvent();
43354                             r.pasteHTML('<br />');
43355                             r.collapse(false);
43356                             r.select();
43357                         }
43358                     }
43359                 }
43360                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43361                     this.cleanUpPaste.defer(100, this);
43362                     return;
43363                 }
43364                 
43365                 
43366             };
43367         }else if(Roo.isOpera){
43368             return function(e){
43369                 var k = e.getKey();
43370                 if(k == e.TAB){
43371                     e.stopEvent();
43372                     this.win.focus();
43373                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43374                     this.deferFocus();
43375                 }
43376                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43377                     this.cleanUpPaste.defer(100, this);
43378                     return;
43379                 }
43380                 
43381             };
43382         }else if(Roo.isSafari){
43383             return function(e){
43384                 var k = e.getKey();
43385                 
43386                 if(k == e.TAB){
43387                     e.stopEvent();
43388                     this.execCmd('InsertText','\t');
43389                     this.deferFocus();
43390                     return;
43391                 }
43392                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43393                     this.cleanUpPaste.defer(100, this);
43394                     return;
43395                 }
43396                 
43397              };
43398         }
43399     }(),
43400     
43401     getAllAncestors: function()
43402     {
43403         var p = this.getSelectedNode();
43404         var a = [];
43405         if (!p) {
43406             a.push(p); // push blank onto stack..
43407             p = this.getParentElement();
43408         }
43409         
43410         
43411         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43412             a.push(p);
43413             p = p.parentNode;
43414         }
43415         a.push(this.doc.body);
43416         return a;
43417     },
43418     lastSel : false,
43419     lastSelNode : false,
43420     
43421     
43422     getSelection : function() 
43423     {
43424         this.assignDocWin();
43425         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43426     },
43427     
43428     getSelectedNode: function() 
43429     {
43430         // this may only work on Gecko!!!
43431         
43432         // should we cache this!!!!
43433         
43434         
43435         
43436          
43437         var range = this.createRange(this.getSelection()).cloneRange();
43438         
43439         if (Roo.isIE) {
43440             var parent = range.parentElement();
43441             while (true) {
43442                 var testRange = range.duplicate();
43443                 testRange.moveToElementText(parent);
43444                 if (testRange.inRange(range)) {
43445                     break;
43446                 }
43447                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43448                     break;
43449                 }
43450                 parent = parent.parentElement;
43451             }
43452             return parent;
43453         }
43454         
43455         // is ancestor a text element.
43456         var ac =  range.commonAncestorContainer;
43457         if (ac.nodeType == 3) {
43458             ac = ac.parentNode;
43459         }
43460         
43461         var ar = ac.childNodes;
43462          
43463         var nodes = [];
43464         var other_nodes = [];
43465         var has_other_nodes = false;
43466         for (var i=0;i<ar.length;i++) {
43467             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43468                 continue;
43469             }
43470             // fullly contained node.
43471             
43472             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43473                 nodes.push(ar[i]);
43474                 continue;
43475             }
43476             
43477             // probably selected..
43478             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43479                 other_nodes.push(ar[i]);
43480                 continue;
43481             }
43482             // outer..
43483             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43484                 continue;
43485             }
43486             
43487             
43488             has_other_nodes = true;
43489         }
43490         if (!nodes.length && other_nodes.length) {
43491             nodes= other_nodes;
43492         }
43493         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43494             return false;
43495         }
43496         
43497         return nodes[0];
43498     },
43499     createRange: function(sel)
43500     {
43501         // this has strange effects when using with 
43502         // top toolbar - not sure if it's a great idea.
43503         //this.editor.contentWindow.focus();
43504         if (typeof sel != "undefined") {
43505             try {
43506                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43507             } catch(e) {
43508                 return this.doc.createRange();
43509             }
43510         } else {
43511             return this.doc.createRange();
43512         }
43513     },
43514     getParentElement: function()
43515     {
43516         
43517         this.assignDocWin();
43518         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43519         
43520         var range = this.createRange(sel);
43521          
43522         try {
43523             var p = range.commonAncestorContainer;
43524             while (p.nodeType == 3) { // text node
43525                 p = p.parentNode;
43526             }
43527             return p;
43528         } catch (e) {
43529             return null;
43530         }
43531     
43532     },
43533     /***
43534      *
43535      * Range intersection.. the hard stuff...
43536      *  '-1' = before
43537      *  '0' = hits..
43538      *  '1' = after.
43539      *         [ -- selected range --- ]
43540      *   [fail]                        [fail]
43541      *
43542      *    basically..
43543      *      if end is before start or  hits it. fail.
43544      *      if start is after end or hits it fail.
43545      *
43546      *   if either hits (but other is outside. - then it's not 
43547      *   
43548      *    
43549      **/
43550     
43551     
43552     // @see http://www.thismuchiknow.co.uk/?p=64.
43553     rangeIntersectsNode : function(range, node)
43554     {
43555         var nodeRange = node.ownerDocument.createRange();
43556         try {
43557             nodeRange.selectNode(node);
43558         } catch (e) {
43559             nodeRange.selectNodeContents(node);
43560         }
43561     
43562         var rangeStartRange = range.cloneRange();
43563         rangeStartRange.collapse(true);
43564     
43565         var rangeEndRange = range.cloneRange();
43566         rangeEndRange.collapse(false);
43567     
43568         var nodeStartRange = nodeRange.cloneRange();
43569         nodeStartRange.collapse(true);
43570     
43571         var nodeEndRange = nodeRange.cloneRange();
43572         nodeEndRange.collapse(false);
43573     
43574         return rangeStartRange.compareBoundaryPoints(
43575                  Range.START_TO_START, nodeEndRange) == -1 &&
43576                rangeEndRange.compareBoundaryPoints(
43577                  Range.START_TO_START, nodeStartRange) == 1;
43578         
43579          
43580     },
43581     rangeCompareNode : function(range, node)
43582     {
43583         var nodeRange = node.ownerDocument.createRange();
43584         try {
43585             nodeRange.selectNode(node);
43586         } catch (e) {
43587             nodeRange.selectNodeContents(node);
43588         }
43589         
43590         
43591         range.collapse(true);
43592     
43593         nodeRange.collapse(true);
43594      
43595         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43596         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43597          
43598         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43599         
43600         var nodeIsBefore   =  ss == 1;
43601         var nodeIsAfter    = ee == -1;
43602         
43603         if (nodeIsBefore && nodeIsAfter) {
43604             return 0; // outer
43605         }
43606         if (!nodeIsBefore && nodeIsAfter) {
43607             return 1; //right trailed.
43608         }
43609         
43610         if (nodeIsBefore && !nodeIsAfter) {
43611             return 2;  // left trailed.
43612         }
43613         // fully contined.
43614         return 3;
43615     },
43616
43617     // private? - in a new class?
43618     cleanUpPaste :  function()
43619     {
43620         // cleans up the whole document..
43621         Roo.log('cleanuppaste');
43622         
43623         this.cleanUpChildren(this.doc.body);
43624         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43625         if (clean != this.doc.body.innerHTML) {
43626             this.doc.body.innerHTML = clean;
43627         }
43628         
43629     },
43630     
43631     cleanWordChars : function(input) {// change the chars to hex code
43632         var he = Roo.HtmlEditorCore;
43633         
43634         var output = input;
43635         Roo.each(he.swapCodes, function(sw) { 
43636             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43637             
43638             output = output.replace(swapper, sw[1]);
43639         });
43640         
43641         return output;
43642     },
43643     
43644     
43645     cleanUpChildren : function (n)
43646     {
43647         if (!n.childNodes.length) {
43648             return;
43649         }
43650         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43651            this.cleanUpChild(n.childNodes[i]);
43652         }
43653     },
43654     
43655     
43656         
43657     
43658     cleanUpChild : function (node)
43659     {
43660         var ed = this;
43661         //console.log(node);
43662         if (node.nodeName == "#text") {
43663             // clean up silly Windows -- stuff?
43664             return; 
43665         }
43666         if (node.nodeName == "#comment") {
43667             node.parentNode.removeChild(node);
43668             // clean up silly Windows -- stuff?
43669             return; 
43670         }
43671         var lcname = node.tagName.toLowerCase();
43672         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43673         // whitelist of tags..
43674         
43675         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43676             // remove node.
43677             node.parentNode.removeChild(node);
43678             return;
43679             
43680         }
43681         
43682         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43683         
43684         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43685         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43686         
43687         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43688         //    remove_keep_children = true;
43689         //}
43690         
43691         if (remove_keep_children) {
43692             this.cleanUpChildren(node);
43693             // inserts everything just before this node...
43694             while (node.childNodes.length) {
43695                 var cn = node.childNodes[0];
43696                 node.removeChild(cn);
43697                 node.parentNode.insertBefore(cn, node);
43698             }
43699             node.parentNode.removeChild(node);
43700             return;
43701         }
43702         
43703         if (!node.attributes || !node.attributes.length) {
43704             this.cleanUpChildren(node);
43705             return;
43706         }
43707         
43708         function cleanAttr(n,v)
43709         {
43710             
43711             if (v.match(/^\./) || v.match(/^\//)) {
43712                 return;
43713             }
43714             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43715                 return;
43716             }
43717             if (v.match(/^#/)) {
43718                 return;
43719             }
43720 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43721             node.removeAttribute(n);
43722             
43723         }
43724         
43725         var cwhite = this.cwhite;
43726         var cblack = this.cblack;
43727             
43728         function cleanStyle(n,v)
43729         {
43730             if (v.match(/expression/)) { //XSS?? should we even bother..
43731                 node.removeAttribute(n);
43732                 return;
43733             }
43734             
43735             var parts = v.split(/;/);
43736             var clean = [];
43737             
43738             Roo.each(parts, function(p) {
43739                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43740                 if (!p.length) {
43741                     return true;
43742                 }
43743                 var l = p.split(':').shift().replace(/\s+/g,'');
43744                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43745                 
43746                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43747 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43748                     //node.removeAttribute(n);
43749                     return true;
43750                 }
43751                 //Roo.log()
43752                 // only allow 'c whitelisted system attributes'
43753                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43754 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43755                     //node.removeAttribute(n);
43756                     return true;
43757                 }
43758                 
43759                 
43760                  
43761                 
43762                 clean.push(p);
43763                 return true;
43764             });
43765             if (clean.length) { 
43766                 node.setAttribute(n, clean.join(';'));
43767             } else {
43768                 node.removeAttribute(n);
43769             }
43770             
43771         }
43772         
43773         
43774         for (var i = node.attributes.length-1; i > -1 ; i--) {
43775             var a = node.attributes[i];
43776             //console.log(a);
43777             
43778             if (a.name.toLowerCase().substr(0,2)=='on')  {
43779                 node.removeAttribute(a.name);
43780                 continue;
43781             }
43782             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43783                 node.removeAttribute(a.name);
43784                 continue;
43785             }
43786             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43787                 cleanAttr(a.name,a.value); // fixme..
43788                 continue;
43789             }
43790             if (a.name == 'style') {
43791                 cleanStyle(a.name,a.value);
43792                 continue;
43793             }
43794             /// clean up MS crap..
43795             // tecnically this should be a list of valid class'es..
43796             
43797             
43798             if (a.name == 'class') {
43799                 if (a.value.match(/^Mso/)) {
43800                     node.className = '';
43801                 }
43802                 
43803                 if (a.value.match(/^body$/)) {
43804                     node.className = '';
43805                 }
43806                 continue;
43807             }
43808             
43809             // style cleanup!?
43810             // class cleanup?
43811             
43812         }
43813         
43814         
43815         this.cleanUpChildren(node);
43816         
43817         
43818     },
43819     
43820     /**
43821      * Clean up MS wordisms...
43822      */
43823     cleanWord : function(node)
43824     {
43825         
43826         
43827         if (!node) {
43828             this.cleanWord(this.doc.body);
43829             return;
43830         }
43831         if (node.nodeName == "#text") {
43832             // clean up silly Windows -- stuff?
43833             return; 
43834         }
43835         if (node.nodeName == "#comment") {
43836             node.parentNode.removeChild(node);
43837             // clean up silly Windows -- stuff?
43838             return; 
43839         }
43840         
43841         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43842             node.parentNode.removeChild(node);
43843             return;
43844         }
43845         
43846         // remove - but keep children..
43847         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43848             while (node.childNodes.length) {
43849                 var cn = node.childNodes[0];
43850                 node.removeChild(cn);
43851                 node.parentNode.insertBefore(cn, node);
43852             }
43853             node.parentNode.removeChild(node);
43854             this.iterateChildren(node, this.cleanWord);
43855             return;
43856         }
43857         // clean styles
43858         if (node.className.length) {
43859             
43860             var cn = node.className.split(/\W+/);
43861             var cna = [];
43862             Roo.each(cn, function(cls) {
43863                 if (cls.match(/Mso[a-zA-Z]+/)) {
43864                     return;
43865                 }
43866                 cna.push(cls);
43867             });
43868             node.className = cna.length ? cna.join(' ') : '';
43869             if (!cna.length) {
43870                 node.removeAttribute("class");
43871             }
43872         }
43873         
43874         if (node.hasAttribute("lang")) {
43875             node.removeAttribute("lang");
43876         }
43877         
43878         if (node.hasAttribute("style")) {
43879             
43880             var styles = node.getAttribute("style").split(";");
43881             var nstyle = [];
43882             Roo.each(styles, function(s) {
43883                 if (!s.match(/:/)) {
43884                     return;
43885                 }
43886                 var kv = s.split(":");
43887                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43888                     return;
43889                 }
43890                 // what ever is left... we allow.
43891                 nstyle.push(s);
43892             });
43893             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43894             if (!nstyle.length) {
43895                 node.removeAttribute('style');
43896             }
43897         }
43898         this.iterateChildren(node, this.cleanWord);
43899         
43900         
43901         
43902     },
43903     /**
43904      * iterateChildren of a Node, calling fn each time, using this as the scole..
43905      * @param {DomNode} node node to iterate children of.
43906      * @param {Function} fn method of this class to call on each item.
43907      */
43908     iterateChildren : function(node, fn)
43909     {
43910         if (!node.childNodes.length) {
43911                 return;
43912         }
43913         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43914            fn.call(this, node.childNodes[i])
43915         }
43916     },
43917     
43918     
43919     /**
43920      * cleanTableWidths.
43921      *
43922      * Quite often pasting from word etc.. results in tables with column and widths.
43923      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43924      *
43925      */
43926     cleanTableWidths : function(node)
43927     {
43928          
43929          
43930         if (!node) {
43931             this.cleanTableWidths(this.doc.body);
43932             return;
43933         }
43934         
43935         // ignore list...
43936         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43937             return; 
43938         }
43939         Roo.log(node.tagName);
43940         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43941             this.iterateChildren(node, this.cleanTableWidths);
43942             return;
43943         }
43944         if (node.hasAttribute('width')) {
43945             node.removeAttribute('width');
43946         }
43947         
43948          
43949         if (node.hasAttribute("style")) {
43950             // pretty basic...
43951             
43952             var styles = node.getAttribute("style").split(";");
43953             var nstyle = [];
43954             Roo.each(styles, function(s) {
43955                 if (!s.match(/:/)) {
43956                     return;
43957                 }
43958                 var kv = s.split(":");
43959                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43960                     return;
43961                 }
43962                 // what ever is left... we allow.
43963                 nstyle.push(s);
43964             });
43965             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43966             if (!nstyle.length) {
43967                 node.removeAttribute('style');
43968             }
43969         }
43970         
43971         this.iterateChildren(node, this.cleanTableWidths);
43972         
43973         
43974     },
43975     
43976     
43977     
43978     
43979     domToHTML : function(currentElement, depth, nopadtext) {
43980         
43981         depth = depth || 0;
43982         nopadtext = nopadtext || false;
43983     
43984         if (!currentElement) {
43985             return this.domToHTML(this.doc.body);
43986         }
43987         
43988         //Roo.log(currentElement);
43989         var j;
43990         var allText = false;
43991         var nodeName = currentElement.nodeName;
43992         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43993         
43994         if  (nodeName == '#text') {
43995             
43996             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43997         }
43998         
43999         
44000         var ret = '';
44001         if (nodeName != 'BODY') {
44002              
44003             var i = 0;
44004             // Prints the node tagName, such as <A>, <IMG>, etc
44005             if (tagName) {
44006                 var attr = [];
44007                 for(i = 0; i < currentElement.attributes.length;i++) {
44008                     // quoting?
44009                     var aname = currentElement.attributes.item(i).name;
44010                     if (!currentElement.attributes.item(i).value.length) {
44011                         continue;
44012                     }
44013                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44014                 }
44015                 
44016                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44017             } 
44018             else {
44019                 
44020                 // eack
44021             }
44022         } else {
44023             tagName = false;
44024         }
44025         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44026             return ret;
44027         }
44028         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44029             nopadtext = true;
44030         }
44031         
44032         
44033         // Traverse the tree
44034         i = 0;
44035         var currentElementChild = currentElement.childNodes.item(i);
44036         var allText = true;
44037         var innerHTML  = '';
44038         lastnode = '';
44039         while (currentElementChild) {
44040             // Formatting code (indent the tree so it looks nice on the screen)
44041             var nopad = nopadtext;
44042             if (lastnode == 'SPAN') {
44043                 nopad  = true;
44044             }
44045             // text
44046             if  (currentElementChild.nodeName == '#text') {
44047                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44048                 toadd = nopadtext ? toadd : toadd.trim();
44049                 if (!nopad && toadd.length > 80) {
44050                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44051                 }
44052                 innerHTML  += toadd;
44053                 
44054                 i++;
44055                 currentElementChild = currentElement.childNodes.item(i);
44056                 lastNode = '';
44057                 continue;
44058             }
44059             allText = false;
44060             
44061             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44062                 
44063             // Recursively traverse the tree structure of the child node
44064             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44065             lastnode = currentElementChild.nodeName;
44066             i++;
44067             currentElementChild=currentElement.childNodes.item(i);
44068         }
44069         
44070         ret += innerHTML;
44071         
44072         if (!allText) {
44073                 // The remaining code is mostly for formatting the tree
44074             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44075         }
44076         
44077         
44078         if (tagName) {
44079             ret+= "</"+tagName+">";
44080         }
44081         return ret;
44082         
44083     },
44084         
44085     applyBlacklists : function()
44086     {
44087         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44088         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44089         
44090         this.white = [];
44091         this.black = [];
44092         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44093             if (b.indexOf(tag) > -1) {
44094                 return;
44095             }
44096             this.white.push(tag);
44097             
44098         }, this);
44099         
44100         Roo.each(w, function(tag) {
44101             if (b.indexOf(tag) > -1) {
44102                 return;
44103             }
44104             if (this.white.indexOf(tag) > -1) {
44105                 return;
44106             }
44107             this.white.push(tag);
44108             
44109         }, this);
44110         
44111         
44112         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44113             if (w.indexOf(tag) > -1) {
44114                 return;
44115             }
44116             this.black.push(tag);
44117             
44118         }, this);
44119         
44120         Roo.each(b, function(tag) {
44121             if (w.indexOf(tag) > -1) {
44122                 return;
44123             }
44124             if (this.black.indexOf(tag) > -1) {
44125                 return;
44126             }
44127             this.black.push(tag);
44128             
44129         }, this);
44130         
44131         
44132         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44133         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44134         
44135         this.cwhite = [];
44136         this.cblack = [];
44137         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44138             if (b.indexOf(tag) > -1) {
44139                 return;
44140             }
44141             this.cwhite.push(tag);
44142             
44143         }, this);
44144         
44145         Roo.each(w, function(tag) {
44146             if (b.indexOf(tag) > -1) {
44147                 return;
44148             }
44149             if (this.cwhite.indexOf(tag) > -1) {
44150                 return;
44151             }
44152             this.cwhite.push(tag);
44153             
44154         }, this);
44155         
44156         
44157         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44158             if (w.indexOf(tag) > -1) {
44159                 return;
44160             }
44161             this.cblack.push(tag);
44162             
44163         }, this);
44164         
44165         Roo.each(b, function(tag) {
44166             if (w.indexOf(tag) > -1) {
44167                 return;
44168             }
44169             if (this.cblack.indexOf(tag) > -1) {
44170                 return;
44171             }
44172             this.cblack.push(tag);
44173             
44174         }, this);
44175     },
44176     
44177     setStylesheets : function(stylesheets)
44178     {
44179         if(typeof(stylesheets) == 'string'){
44180             Roo.get(this.iframe.contentDocument.head).createChild({
44181                 tag : 'link',
44182                 rel : 'stylesheet',
44183                 type : 'text/css',
44184                 href : stylesheets
44185             });
44186             
44187             return;
44188         }
44189         var _this = this;
44190      
44191         Roo.each(stylesheets, function(s) {
44192             if(!s.length){
44193                 return;
44194             }
44195             
44196             Roo.get(_this.iframe.contentDocument.head).createChild({
44197                 tag : 'link',
44198                 rel : 'stylesheet',
44199                 type : 'text/css',
44200                 href : s
44201             });
44202         });
44203
44204         
44205     },
44206     
44207     removeStylesheets : function()
44208     {
44209         var _this = this;
44210         
44211         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44212             s.remove();
44213         });
44214     }
44215     
44216     // hide stuff that is not compatible
44217     /**
44218      * @event blur
44219      * @hide
44220      */
44221     /**
44222      * @event change
44223      * @hide
44224      */
44225     /**
44226      * @event focus
44227      * @hide
44228      */
44229     /**
44230      * @event specialkey
44231      * @hide
44232      */
44233     /**
44234      * @cfg {String} fieldClass @hide
44235      */
44236     /**
44237      * @cfg {String} focusClass @hide
44238      */
44239     /**
44240      * @cfg {String} autoCreate @hide
44241      */
44242     /**
44243      * @cfg {String} inputType @hide
44244      */
44245     /**
44246      * @cfg {String} invalidClass @hide
44247      */
44248     /**
44249      * @cfg {String} invalidText @hide
44250      */
44251     /**
44252      * @cfg {String} msgFx @hide
44253      */
44254     /**
44255      * @cfg {String} validateOnBlur @hide
44256      */
44257 });
44258
44259 Roo.HtmlEditorCore.white = [
44260         'area', 'br', 'img', 'input', 'hr', 'wbr',
44261         
44262        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44263        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44264        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44265        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44266        'table',   'ul',         'xmp', 
44267        
44268        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44269       'thead',   'tr', 
44270      
44271       'dir', 'menu', 'ol', 'ul', 'dl',
44272        
44273       'embed',  'object'
44274 ];
44275
44276
44277 Roo.HtmlEditorCore.black = [
44278     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44279         'applet', // 
44280         'base',   'basefont', 'bgsound', 'blink',  'body', 
44281         'frame',  'frameset', 'head',    'html',   'ilayer', 
44282         'iframe', 'layer',  'link',     'meta',    'object',   
44283         'script', 'style' ,'title',  'xml' // clean later..
44284 ];
44285 Roo.HtmlEditorCore.clean = [
44286     'script', 'style', 'title', 'xml'
44287 ];
44288 Roo.HtmlEditorCore.remove = [
44289     'font'
44290 ];
44291 // attributes..
44292
44293 Roo.HtmlEditorCore.ablack = [
44294     'on'
44295 ];
44296     
44297 Roo.HtmlEditorCore.aclean = [ 
44298     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44299 ];
44300
44301 // protocols..
44302 Roo.HtmlEditorCore.pwhite= [
44303         'http',  'https',  'mailto'
44304 ];
44305
44306 // white listed style attributes.
44307 Roo.HtmlEditorCore.cwhite= [
44308       //  'text-align', /// default is to allow most things..
44309       
44310          
44311 //        'font-size'//??
44312 ];
44313
44314 // black listed style attributes.
44315 Roo.HtmlEditorCore.cblack= [
44316       //  'font-size' -- this can be set by the project 
44317 ];
44318
44319
44320 Roo.HtmlEditorCore.swapCodes   =[ 
44321     [    8211, "--" ], 
44322     [    8212, "--" ], 
44323     [    8216,  "'" ],  
44324     [    8217, "'" ],  
44325     [    8220, '"' ],  
44326     [    8221, '"' ],  
44327     [    8226, "*" ],  
44328     [    8230, "..." ]
44329 ]; 
44330
44331     //<script type="text/javascript">
44332
44333 /*
44334  * Ext JS Library 1.1.1
44335  * Copyright(c) 2006-2007, Ext JS, LLC.
44336  * Licence LGPL
44337  * 
44338  */
44339  
44340  
44341 Roo.form.HtmlEditor = function(config){
44342     
44343     
44344     
44345     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44346     
44347     if (!this.toolbars) {
44348         this.toolbars = [];
44349     }
44350     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44351     
44352     
44353 };
44354
44355 /**
44356  * @class Roo.form.HtmlEditor
44357  * @extends Roo.form.Field
44358  * Provides a lightweight HTML Editor component.
44359  *
44360  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44361  * 
44362  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44363  * supported by this editor.</b><br/><br/>
44364  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44365  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44366  */
44367 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44368     /**
44369      * @cfg {Boolean} clearUp
44370      */
44371     clearUp : true,
44372       /**
44373      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44374      */
44375     toolbars : false,
44376    
44377      /**
44378      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44379      *                        Roo.resizable.
44380      */
44381     resizable : false,
44382      /**
44383      * @cfg {Number} height (in pixels)
44384      */   
44385     height: 300,
44386    /**
44387      * @cfg {Number} width (in pixels)
44388      */   
44389     width: 500,
44390     
44391     /**
44392      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44393      * 
44394      */
44395     stylesheets: false,
44396     
44397     
44398      /**
44399      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44400      * 
44401      */
44402     cblack: false,
44403     /**
44404      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44405      * 
44406      */
44407     cwhite: false,
44408     
44409      /**
44410      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44411      * 
44412      */
44413     black: false,
44414     /**
44415      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44416      * 
44417      */
44418     white: false,
44419     
44420     // id of frame..
44421     frameId: false,
44422     
44423     // private properties
44424     validationEvent : false,
44425     deferHeight: true,
44426     initialized : false,
44427     activated : false,
44428     
44429     onFocus : Roo.emptyFn,
44430     iframePad:3,
44431     hideMode:'offsets',
44432     
44433     actionMode : 'container', // defaults to hiding it...
44434     
44435     defaultAutoCreate : { // modified by initCompnoent..
44436         tag: "textarea",
44437         style:"width:500px;height:300px;",
44438         autocomplete: "new-password"
44439     },
44440
44441     // private
44442     initComponent : function(){
44443         this.addEvents({
44444             /**
44445              * @event initialize
44446              * Fires when the editor is fully initialized (including the iframe)
44447              * @param {HtmlEditor} this
44448              */
44449             initialize: true,
44450             /**
44451              * @event activate
44452              * Fires when the editor is first receives the focus. Any insertion must wait
44453              * until after this event.
44454              * @param {HtmlEditor} this
44455              */
44456             activate: true,
44457              /**
44458              * @event beforesync
44459              * Fires before the textarea is updated with content from the editor iframe. Return false
44460              * to cancel the sync.
44461              * @param {HtmlEditor} this
44462              * @param {String} html
44463              */
44464             beforesync: true,
44465              /**
44466              * @event beforepush
44467              * Fires before the iframe editor is updated with content from the textarea. Return false
44468              * to cancel the push.
44469              * @param {HtmlEditor} this
44470              * @param {String} html
44471              */
44472             beforepush: true,
44473              /**
44474              * @event sync
44475              * Fires when the textarea is updated with content from the editor iframe.
44476              * @param {HtmlEditor} this
44477              * @param {String} html
44478              */
44479             sync: true,
44480              /**
44481              * @event push
44482              * Fires when the iframe editor is updated with content from the textarea.
44483              * @param {HtmlEditor} this
44484              * @param {String} html
44485              */
44486             push: true,
44487              /**
44488              * @event editmodechange
44489              * Fires when the editor switches edit modes
44490              * @param {HtmlEditor} this
44491              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44492              */
44493             editmodechange: true,
44494             /**
44495              * @event editorevent
44496              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44497              * @param {HtmlEditor} this
44498              */
44499             editorevent: true,
44500             /**
44501              * @event firstfocus
44502              * Fires when on first focus - needed by toolbars..
44503              * @param {HtmlEditor} this
44504              */
44505             firstfocus: true,
44506             /**
44507              * @event autosave
44508              * Auto save the htmlEditor value as a file into Events
44509              * @param {HtmlEditor} this
44510              */
44511             autosave: true,
44512             /**
44513              * @event savedpreview
44514              * preview the saved version of htmlEditor
44515              * @param {HtmlEditor} this
44516              */
44517             savedpreview: true,
44518             
44519             /**
44520             * @event stylesheetsclick
44521             * Fires when press the Sytlesheets button
44522             * @param {Roo.HtmlEditorCore} this
44523             */
44524             stylesheetsclick: true
44525         });
44526         this.defaultAutoCreate =  {
44527             tag: "textarea",
44528             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44529             autocomplete: "new-password"
44530         };
44531     },
44532
44533     /**
44534      * Protected method that will not generally be called directly. It
44535      * is called when the editor creates its toolbar. Override this method if you need to
44536      * add custom toolbar buttons.
44537      * @param {HtmlEditor} editor
44538      */
44539     createToolbar : function(editor){
44540         Roo.log("create toolbars");
44541         if (!editor.toolbars || !editor.toolbars.length) {
44542             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44543         }
44544         
44545         for (var i =0 ; i < editor.toolbars.length;i++) {
44546             editor.toolbars[i] = Roo.factory(
44547                     typeof(editor.toolbars[i]) == 'string' ?
44548                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44549                 Roo.form.HtmlEditor);
44550             editor.toolbars[i].init(editor);
44551         }
44552          
44553         
44554     },
44555
44556      
44557     // private
44558     onRender : function(ct, position)
44559     {
44560         var _t = this;
44561         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44562         
44563         this.wrap = this.el.wrap({
44564             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44565         });
44566         
44567         this.editorcore.onRender(ct, position);
44568          
44569         if (this.resizable) {
44570             this.resizeEl = new Roo.Resizable(this.wrap, {
44571                 pinned : true,
44572                 wrap: true,
44573                 dynamic : true,
44574                 minHeight : this.height,
44575                 height: this.height,
44576                 handles : this.resizable,
44577                 width: this.width,
44578                 listeners : {
44579                     resize : function(r, w, h) {
44580                         _t.onResize(w,h); // -something
44581                     }
44582                 }
44583             });
44584             
44585         }
44586         this.createToolbar(this);
44587        
44588         
44589         if(!this.width){
44590             this.setSize(this.wrap.getSize());
44591         }
44592         if (this.resizeEl) {
44593             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44594             // should trigger onReize..
44595         }
44596         
44597         this.keyNav = new Roo.KeyNav(this.el, {
44598             
44599             "tab" : function(e){
44600                 e.preventDefault();
44601                 
44602                 var value = this.getValue();
44603                 
44604                 var start = this.el.dom.selectionStart;
44605                 var end = this.el.dom.selectionEnd;
44606                 
44607                 if(!e.shiftKey){
44608                     
44609                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44610                     this.el.dom.setSelectionRange(end + 1, end + 1);
44611                     return;
44612                 }
44613                 
44614                 var f = value.substring(0, start).split("\t");
44615                 
44616                 if(f.pop().length != 0){
44617                     return;
44618                 }
44619                 
44620                 this.setValue(f.join("\t") + value.substring(end));
44621                 this.el.dom.setSelectionRange(start - 1, start - 1);
44622                 
44623             },
44624             
44625             "home" : function(e){
44626                 e.preventDefault();
44627                 
44628                 var curr = this.el.dom.selectionStart;
44629                 var lines = this.getValue().split("\n");
44630                 
44631                 if(!lines.length){
44632                     return;
44633                 }
44634                 
44635                 if(e.ctrlKey){
44636                     this.el.dom.setSelectionRange(0, 0);
44637                     return;
44638                 }
44639                 
44640                 var pos = 0;
44641                 
44642                 for (var i = 0; i < lines.length;i++) {
44643                     pos += lines[i].length;
44644                     
44645                     if(i != 0){
44646                         pos += 1;
44647                     }
44648                     
44649                     if(pos < curr){
44650                         continue;
44651                     }
44652                     
44653                     pos -= lines[i].length;
44654                     
44655                     break;
44656                 }
44657                 
44658                 if(!e.shiftKey){
44659                     this.el.dom.setSelectionRange(pos, pos);
44660                     return;
44661                 }
44662                 
44663                 this.el.dom.selectionStart = pos;
44664                 this.el.dom.selectionEnd = curr;
44665             },
44666             
44667             "end" : function(e){
44668                 e.preventDefault();
44669                 
44670                 var curr = this.el.dom.selectionStart;
44671                 var lines = this.getValue().split("\n");
44672                 
44673                 if(!lines.length){
44674                     return;
44675                 }
44676                 
44677                 if(e.ctrlKey){
44678                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44679                     return;
44680                 }
44681                 
44682                 var pos = 0;
44683                 
44684                 for (var i = 0; i < lines.length;i++) {
44685                     
44686                     pos += lines[i].length;
44687                     
44688                     if(i != 0){
44689                         pos += 1;
44690                     }
44691                     
44692                     if(pos < curr){
44693                         continue;
44694                     }
44695                     
44696                     break;
44697                 }
44698                 
44699                 if(!e.shiftKey){
44700                     this.el.dom.setSelectionRange(pos, pos);
44701                     return;
44702                 }
44703                 
44704                 this.el.dom.selectionStart = curr;
44705                 this.el.dom.selectionEnd = pos;
44706             },
44707
44708             scope : this,
44709
44710             doRelay : function(foo, bar, hname){
44711                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44712             },
44713
44714             forceKeyDown: true
44715         });
44716         
44717 //        if(this.autosave && this.w){
44718 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44719 //        }
44720     },
44721
44722     // private
44723     onResize : function(w, h)
44724     {
44725         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44726         var ew = false;
44727         var eh = false;
44728         
44729         if(this.el ){
44730             if(typeof w == 'number'){
44731                 var aw = w - this.wrap.getFrameWidth('lr');
44732                 this.el.setWidth(this.adjustWidth('textarea', aw));
44733                 ew = aw;
44734             }
44735             if(typeof h == 'number'){
44736                 var tbh = 0;
44737                 for (var i =0; i < this.toolbars.length;i++) {
44738                     // fixme - ask toolbars for heights?
44739                     tbh += this.toolbars[i].tb.el.getHeight();
44740                     if (this.toolbars[i].footer) {
44741                         tbh += this.toolbars[i].footer.el.getHeight();
44742                     }
44743                 }
44744                 
44745                 
44746                 
44747                 
44748                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44749                 ah -= 5; // knock a few pixes off for look..
44750 //                Roo.log(ah);
44751                 this.el.setHeight(this.adjustWidth('textarea', ah));
44752                 var eh = ah;
44753             }
44754         }
44755         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44756         this.editorcore.onResize(ew,eh);
44757         
44758     },
44759
44760     /**
44761      * Toggles the editor between standard and source edit mode.
44762      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44763      */
44764     toggleSourceEdit : function(sourceEditMode)
44765     {
44766         this.editorcore.toggleSourceEdit(sourceEditMode);
44767         
44768         if(this.editorcore.sourceEditMode){
44769             Roo.log('editor - showing textarea');
44770             
44771 //            Roo.log('in');
44772 //            Roo.log(this.syncValue());
44773             this.editorcore.syncValue();
44774             this.el.removeClass('x-hidden');
44775             this.el.dom.removeAttribute('tabIndex');
44776             this.el.focus();
44777             
44778             for (var i = 0; i < this.toolbars.length; i++) {
44779                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44780                     this.toolbars[i].tb.hide();
44781                     this.toolbars[i].footer.hide();
44782                 }
44783             }
44784             
44785         }else{
44786             Roo.log('editor - hiding textarea');
44787 //            Roo.log('out')
44788 //            Roo.log(this.pushValue()); 
44789             this.editorcore.pushValue();
44790             
44791             this.el.addClass('x-hidden');
44792             this.el.dom.setAttribute('tabIndex', -1);
44793             
44794             for (var i = 0; i < this.toolbars.length; i++) {
44795                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44796                     this.toolbars[i].tb.show();
44797                     this.toolbars[i].footer.show();
44798                 }
44799             }
44800             
44801             //this.deferFocus();
44802         }
44803         
44804         this.setSize(this.wrap.getSize());
44805         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44806         
44807         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44808     },
44809  
44810     // private (for BoxComponent)
44811     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44812
44813     // private (for BoxComponent)
44814     getResizeEl : function(){
44815         return this.wrap;
44816     },
44817
44818     // private (for BoxComponent)
44819     getPositionEl : function(){
44820         return this.wrap;
44821     },
44822
44823     // private
44824     initEvents : function(){
44825         this.originalValue = this.getValue();
44826     },
44827
44828     /**
44829      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44830      * @method
44831      */
44832     markInvalid : Roo.emptyFn,
44833     /**
44834      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44835      * @method
44836      */
44837     clearInvalid : Roo.emptyFn,
44838
44839     setValue : function(v){
44840         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44841         this.editorcore.pushValue();
44842     },
44843
44844      
44845     // private
44846     deferFocus : function(){
44847         this.focus.defer(10, this);
44848     },
44849
44850     // doc'ed in Field
44851     focus : function(){
44852         this.editorcore.focus();
44853         
44854     },
44855       
44856
44857     // private
44858     onDestroy : function(){
44859         
44860         
44861         
44862         if(this.rendered){
44863             
44864             for (var i =0; i < this.toolbars.length;i++) {
44865                 // fixme - ask toolbars for heights?
44866                 this.toolbars[i].onDestroy();
44867             }
44868             
44869             this.wrap.dom.innerHTML = '';
44870             this.wrap.remove();
44871         }
44872     },
44873
44874     // private
44875     onFirstFocus : function(){
44876         //Roo.log("onFirstFocus");
44877         this.editorcore.onFirstFocus();
44878          for (var i =0; i < this.toolbars.length;i++) {
44879             this.toolbars[i].onFirstFocus();
44880         }
44881         
44882     },
44883     
44884     // private
44885     syncValue : function()
44886     {
44887         this.editorcore.syncValue();
44888     },
44889     
44890     pushValue : function()
44891     {
44892         this.editorcore.pushValue();
44893     },
44894     
44895     setStylesheets : function(stylesheets)
44896     {
44897         this.editorcore.setStylesheets(stylesheets);
44898     },
44899     
44900     removeStylesheets : function()
44901     {
44902         this.editorcore.removeStylesheets();
44903     }
44904      
44905     
44906     // hide stuff that is not compatible
44907     /**
44908      * @event blur
44909      * @hide
44910      */
44911     /**
44912      * @event change
44913      * @hide
44914      */
44915     /**
44916      * @event focus
44917      * @hide
44918      */
44919     /**
44920      * @event specialkey
44921      * @hide
44922      */
44923     /**
44924      * @cfg {String} fieldClass @hide
44925      */
44926     /**
44927      * @cfg {String} focusClass @hide
44928      */
44929     /**
44930      * @cfg {String} autoCreate @hide
44931      */
44932     /**
44933      * @cfg {String} inputType @hide
44934      */
44935     /**
44936      * @cfg {String} invalidClass @hide
44937      */
44938     /**
44939      * @cfg {String} invalidText @hide
44940      */
44941     /**
44942      * @cfg {String} msgFx @hide
44943      */
44944     /**
44945      * @cfg {String} validateOnBlur @hide
44946      */
44947 });
44948  
44949     // <script type="text/javascript">
44950 /*
44951  * Based on
44952  * Ext JS Library 1.1.1
44953  * Copyright(c) 2006-2007, Ext JS, LLC.
44954  *  
44955  
44956  */
44957
44958 /**
44959  * @class Roo.form.HtmlEditorToolbar1
44960  * Basic Toolbar
44961  * 
44962  * Usage:
44963  *
44964  new Roo.form.HtmlEditor({
44965     ....
44966     toolbars : [
44967         new Roo.form.HtmlEditorToolbar1({
44968             disable : { fonts: 1 , format: 1, ..., ... , ...],
44969             btns : [ .... ]
44970         })
44971     }
44972      
44973  * 
44974  * @cfg {Object} disable List of elements to disable..
44975  * @cfg {Array} btns List of additional buttons.
44976  * 
44977  * 
44978  * NEEDS Extra CSS? 
44979  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44980  */
44981  
44982 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44983 {
44984     
44985     Roo.apply(this, config);
44986     
44987     // default disabled, based on 'good practice'..
44988     this.disable = this.disable || {};
44989     Roo.applyIf(this.disable, {
44990         fontSize : true,
44991         colors : true,
44992         specialElements : true
44993     });
44994     
44995     
44996     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44997     // dont call parent... till later.
44998 }
44999
45000 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45001     
45002     tb: false,
45003     
45004     rendered: false,
45005     
45006     editor : false,
45007     editorcore : false,
45008     /**
45009      * @cfg {Object} disable  List of toolbar elements to disable
45010          
45011      */
45012     disable : false,
45013     
45014     
45015      /**
45016      * @cfg {String} createLinkText The default text for the create link prompt
45017      */
45018     createLinkText : 'Please enter the URL for the link:',
45019     /**
45020      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45021      */
45022     defaultLinkValue : 'http:/'+'/',
45023    
45024     
45025       /**
45026      * @cfg {Array} fontFamilies An array of available font families
45027      */
45028     fontFamilies : [
45029         'Arial',
45030         'Courier New',
45031         'Tahoma',
45032         'Times New Roman',
45033         'Verdana'
45034     ],
45035     
45036     specialChars : [
45037            "&#169;",
45038           "&#174;",     
45039           "&#8482;",    
45040           "&#163;" ,    
45041          // "&#8212;",    
45042           "&#8230;",    
45043           "&#247;" ,    
45044         //  "&#225;" ,     ?? a acute?
45045            "&#8364;"    , //Euro
45046        //   "&#8220;"    ,
45047         //  "&#8221;"    ,
45048         //  "&#8226;"    ,
45049           "&#176;"  //   , // degrees
45050
45051          // "&#233;"     , // e ecute
45052          // "&#250;"     , // u ecute?
45053     ],
45054     
45055     specialElements : [
45056         {
45057             text: "Insert Table",
45058             xtype: 'MenuItem',
45059             xns : Roo.Menu,
45060             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45061                 
45062         },
45063         {    
45064             text: "Insert Image",
45065             xtype: 'MenuItem',
45066             xns : Roo.Menu,
45067             ihtml : '<img src="about:blank"/>'
45068             
45069         }
45070         
45071          
45072     ],
45073     
45074     
45075     inputElements : [ 
45076             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45077             "input:submit", "input:button", "select", "textarea", "label" ],
45078     formats : [
45079         ["p"] ,  
45080         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45081         ["pre"],[ "code"], 
45082         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45083         ['div'],['span']
45084     ],
45085     
45086     cleanStyles : [
45087         "font-size"
45088     ],
45089      /**
45090      * @cfg {String} defaultFont default font to use.
45091      */
45092     defaultFont: 'tahoma',
45093    
45094     fontSelect : false,
45095     
45096     
45097     formatCombo : false,
45098     
45099     init : function(editor)
45100     {
45101         this.editor = editor;
45102         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45103         var editorcore = this.editorcore;
45104         
45105         var _t = this;
45106         
45107         var fid = editorcore.frameId;
45108         var etb = this;
45109         function btn(id, toggle, handler){
45110             var xid = fid + '-'+ id ;
45111             return {
45112                 id : xid,
45113                 cmd : id,
45114                 cls : 'x-btn-icon x-edit-'+id,
45115                 enableToggle:toggle !== false,
45116                 scope: _t, // was editor...
45117                 handler:handler||_t.relayBtnCmd,
45118                 clickEvent:'mousedown',
45119                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45120                 tabIndex:-1
45121             };
45122         }
45123         
45124         
45125         
45126         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45127         this.tb = tb;
45128          // stop form submits
45129         tb.el.on('click', function(e){
45130             e.preventDefault(); // what does this do?
45131         });
45132
45133         if(!this.disable.font) { // && !Roo.isSafari){
45134             /* why no safari for fonts 
45135             editor.fontSelect = tb.el.createChild({
45136                 tag:'select',
45137                 tabIndex: -1,
45138                 cls:'x-font-select',
45139                 html: this.createFontOptions()
45140             });
45141             
45142             editor.fontSelect.on('change', function(){
45143                 var font = editor.fontSelect.dom.value;
45144                 editor.relayCmd('fontname', font);
45145                 editor.deferFocus();
45146             }, editor);
45147             
45148             tb.add(
45149                 editor.fontSelect.dom,
45150                 '-'
45151             );
45152             */
45153             
45154         };
45155         if(!this.disable.formats){
45156             this.formatCombo = new Roo.form.ComboBox({
45157                 store: new Roo.data.SimpleStore({
45158                     id : 'tag',
45159                     fields: ['tag'],
45160                     data : this.formats // from states.js
45161                 }),
45162                 blockFocus : true,
45163                 name : '',
45164                 //autoCreate : {tag: "div",  size: "20"},
45165                 displayField:'tag',
45166                 typeAhead: false,
45167                 mode: 'local',
45168                 editable : false,
45169                 triggerAction: 'all',
45170                 emptyText:'Add tag',
45171                 selectOnFocus:true,
45172                 width:135,
45173                 listeners : {
45174                     'select': function(c, r, i) {
45175                         editorcore.insertTag(r.get('tag'));
45176                         editor.focus();
45177                     }
45178                 }
45179
45180             });
45181             tb.addField(this.formatCombo);
45182             
45183         }
45184         
45185         if(!this.disable.format){
45186             tb.add(
45187                 btn('bold'),
45188                 btn('italic'),
45189                 btn('underline'),
45190                 btn('strikethrough')
45191             );
45192         };
45193         if(!this.disable.fontSize){
45194             tb.add(
45195                 '-',
45196                 
45197                 
45198                 btn('increasefontsize', false, editorcore.adjustFont),
45199                 btn('decreasefontsize', false, editorcore.adjustFont)
45200             );
45201         };
45202         
45203         
45204         if(!this.disable.colors){
45205             tb.add(
45206                 '-', {
45207                     id:editorcore.frameId +'-forecolor',
45208                     cls:'x-btn-icon x-edit-forecolor',
45209                     clickEvent:'mousedown',
45210                     tooltip: this.buttonTips['forecolor'] || undefined,
45211                     tabIndex:-1,
45212                     menu : new Roo.menu.ColorMenu({
45213                         allowReselect: true,
45214                         focus: Roo.emptyFn,
45215                         value:'000000',
45216                         plain:true,
45217                         selectHandler: function(cp, color){
45218                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45219                             editor.deferFocus();
45220                         },
45221                         scope: editorcore,
45222                         clickEvent:'mousedown'
45223                     })
45224                 }, {
45225                     id:editorcore.frameId +'backcolor',
45226                     cls:'x-btn-icon x-edit-backcolor',
45227                     clickEvent:'mousedown',
45228                     tooltip: this.buttonTips['backcolor'] || undefined,
45229                     tabIndex:-1,
45230                     menu : new Roo.menu.ColorMenu({
45231                         focus: Roo.emptyFn,
45232                         value:'FFFFFF',
45233                         plain:true,
45234                         allowReselect: true,
45235                         selectHandler: function(cp, color){
45236                             if(Roo.isGecko){
45237                                 editorcore.execCmd('useCSS', false);
45238                                 editorcore.execCmd('hilitecolor', color);
45239                                 editorcore.execCmd('useCSS', true);
45240                                 editor.deferFocus();
45241                             }else{
45242                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45243                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45244                                 editor.deferFocus();
45245                             }
45246                         },
45247                         scope:editorcore,
45248                         clickEvent:'mousedown'
45249                     })
45250                 }
45251             );
45252         };
45253         // now add all the items...
45254         
45255
45256         if(!this.disable.alignments){
45257             tb.add(
45258                 '-',
45259                 btn('justifyleft'),
45260                 btn('justifycenter'),
45261                 btn('justifyright')
45262             );
45263         };
45264
45265         //if(!Roo.isSafari){
45266             if(!this.disable.links){
45267                 tb.add(
45268                     '-',
45269                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45270                 );
45271             };
45272
45273             if(!this.disable.lists){
45274                 tb.add(
45275                     '-',
45276                     btn('insertorderedlist'),
45277                     btn('insertunorderedlist')
45278                 );
45279             }
45280             if(!this.disable.sourceEdit){
45281                 tb.add(
45282                     '-',
45283                     btn('sourceedit', true, function(btn){
45284                         this.toggleSourceEdit(btn.pressed);
45285                     })
45286                 );
45287             }
45288         //}
45289         
45290         var smenu = { };
45291         // special menu.. - needs to be tidied up..
45292         if (!this.disable.special) {
45293             smenu = {
45294                 text: "&#169;",
45295                 cls: 'x-edit-none',
45296                 
45297                 menu : {
45298                     items : []
45299                 }
45300             };
45301             for (var i =0; i < this.specialChars.length; i++) {
45302                 smenu.menu.items.push({
45303                     
45304                     html: this.specialChars[i],
45305                     handler: function(a,b) {
45306                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45307                         //editor.insertAtCursor(a.html);
45308                         
45309                     },
45310                     tabIndex:-1
45311                 });
45312             }
45313             
45314             
45315             tb.add(smenu);
45316             
45317             
45318         }
45319         
45320         var cmenu = { };
45321         if (!this.disable.cleanStyles) {
45322             cmenu = {
45323                 cls: 'x-btn-icon x-btn-clear',
45324                 
45325                 menu : {
45326                     items : []
45327                 }
45328             };
45329             for (var i =0; i < this.cleanStyles.length; i++) {
45330                 cmenu.menu.items.push({
45331                     actiontype : this.cleanStyles[i],
45332                     html: 'Remove ' + this.cleanStyles[i],
45333                     handler: function(a,b) {
45334 //                        Roo.log(a);
45335 //                        Roo.log(b);
45336                         var c = Roo.get(editorcore.doc.body);
45337                         c.select('[style]').each(function(s) {
45338                             s.dom.style.removeProperty(a.actiontype);
45339                         });
45340                         editorcore.syncValue();
45341                     },
45342                     tabIndex:-1
45343                 });
45344             }
45345              cmenu.menu.items.push({
45346                 actiontype : 'tablewidths',
45347                 html: 'Remove Table Widths',
45348                 handler: function(a,b) {
45349                     editorcore.cleanTableWidths();
45350                     editorcore.syncValue();
45351                 },
45352                 tabIndex:-1
45353             });
45354             cmenu.menu.items.push({
45355                 actiontype : 'word',
45356                 html: 'Remove MS Word Formating',
45357                 handler: function(a,b) {
45358                     editorcore.cleanWord();
45359                     editorcore.syncValue();
45360                 },
45361                 tabIndex:-1
45362             });
45363             
45364             cmenu.menu.items.push({
45365                 actiontype : 'all',
45366                 html: 'Remove All Styles',
45367                 handler: function(a,b) {
45368                     
45369                     var c = Roo.get(editorcore.doc.body);
45370                     c.select('[style]').each(function(s) {
45371                         s.dom.removeAttribute('style');
45372                     });
45373                     editorcore.syncValue();
45374                 },
45375                 tabIndex:-1
45376             });
45377             
45378             cmenu.menu.items.push({
45379                 actiontype : 'all',
45380                 html: 'Remove All CSS Classes',
45381                 handler: function(a,b) {
45382                     
45383                     var c = Roo.get(editorcore.doc.body);
45384                     c.select('[class]').each(function(s) {
45385                         s.dom.className = '';
45386                     });
45387                     editorcore.syncValue();
45388                 },
45389                 tabIndex:-1
45390             });
45391             
45392              cmenu.menu.items.push({
45393                 actiontype : 'tidy',
45394                 html: 'Tidy HTML Source',
45395                 handler: function(a,b) {
45396                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45397                     editorcore.syncValue();
45398                 },
45399                 tabIndex:-1
45400             });
45401             
45402             
45403             tb.add(cmenu);
45404         }
45405          
45406         if (!this.disable.specialElements) {
45407             var semenu = {
45408                 text: "Other;",
45409                 cls: 'x-edit-none',
45410                 menu : {
45411                     items : []
45412                 }
45413             };
45414             for (var i =0; i < this.specialElements.length; i++) {
45415                 semenu.menu.items.push(
45416                     Roo.apply({ 
45417                         handler: function(a,b) {
45418                             editor.insertAtCursor(this.ihtml);
45419                         }
45420                     }, this.specialElements[i])
45421                 );
45422                     
45423             }
45424             
45425             tb.add(semenu);
45426             
45427             
45428         }
45429          
45430         
45431         if (this.btns) {
45432             for(var i =0; i< this.btns.length;i++) {
45433                 var b = Roo.factory(this.btns[i],Roo.form);
45434                 b.cls =  'x-edit-none';
45435                 
45436                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45437                     b.cls += ' x-init-enable';
45438                 }
45439                 
45440                 b.scope = editorcore;
45441                 tb.add(b);
45442             }
45443         
45444         }
45445         
45446         
45447         
45448         // disable everything...
45449         
45450         this.tb.items.each(function(item){
45451             
45452            if(
45453                 item.id != editorcore.frameId+ '-sourceedit' && 
45454                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45455             ){
45456                 
45457                 item.disable();
45458             }
45459         });
45460         this.rendered = true;
45461         
45462         // the all the btns;
45463         editor.on('editorevent', this.updateToolbar, this);
45464         // other toolbars need to implement this..
45465         //editor.on('editmodechange', this.updateToolbar, this);
45466     },
45467     
45468     
45469     relayBtnCmd : function(btn) {
45470         this.editorcore.relayCmd(btn.cmd);
45471     },
45472     // private used internally
45473     createLink : function(){
45474         Roo.log("create link?");
45475         var url = prompt(this.createLinkText, this.defaultLinkValue);
45476         if(url && url != 'http:/'+'/'){
45477             this.editorcore.relayCmd('createlink', url);
45478         }
45479     },
45480
45481     
45482     /**
45483      * Protected method that will not generally be called directly. It triggers
45484      * a toolbar update by reading the markup state of the current selection in the editor.
45485      */
45486     updateToolbar: function(){
45487
45488         if(!this.editorcore.activated){
45489             this.editor.onFirstFocus();
45490             return;
45491         }
45492
45493         var btns = this.tb.items.map, 
45494             doc = this.editorcore.doc,
45495             frameId = this.editorcore.frameId;
45496
45497         if(!this.disable.font && !Roo.isSafari){
45498             /*
45499             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45500             if(name != this.fontSelect.dom.value){
45501                 this.fontSelect.dom.value = name;
45502             }
45503             */
45504         }
45505         if(!this.disable.format){
45506             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45507             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45508             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45509             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45510         }
45511         if(!this.disable.alignments){
45512             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45513             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45514             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45515         }
45516         if(!Roo.isSafari && !this.disable.lists){
45517             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45518             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45519         }
45520         
45521         var ans = this.editorcore.getAllAncestors();
45522         if (this.formatCombo) {
45523             
45524             
45525             var store = this.formatCombo.store;
45526             this.formatCombo.setValue("");
45527             for (var i =0; i < ans.length;i++) {
45528                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45529                     // select it..
45530                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45531                     break;
45532                 }
45533             }
45534         }
45535         
45536         
45537         
45538         // hides menus... - so this cant be on a menu...
45539         Roo.menu.MenuMgr.hideAll();
45540
45541         //this.editorsyncValue();
45542     },
45543    
45544     
45545     createFontOptions : function(){
45546         var buf = [], fs = this.fontFamilies, ff, lc;
45547         
45548         
45549         
45550         for(var i = 0, len = fs.length; i< len; i++){
45551             ff = fs[i];
45552             lc = ff.toLowerCase();
45553             buf.push(
45554                 '<option value="',lc,'" style="font-family:',ff,';"',
45555                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45556                     ff,
45557                 '</option>'
45558             );
45559         }
45560         return buf.join('');
45561     },
45562     
45563     toggleSourceEdit : function(sourceEditMode){
45564         
45565         Roo.log("toolbar toogle");
45566         if(sourceEditMode === undefined){
45567             sourceEditMode = !this.sourceEditMode;
45568         }
45569         this.sourceEditMode = sourceEditMode === true;
45570         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45571         // just toggle the button?
45572         if(btn.pressed !== this.sourceEditMode){
45573             btn.toggle(this.sourceEditMode);
45574             return;
45575         }
45576         
45577         if(sourceEditMode){
45578             Roo.log("disabling buttons");
45579             this.tb.items.each(function(item){
45580                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45581                     item.disable();
45582                 }
45583             });
45584           
45585         }else{
45586             Roo.log("enabling buttons");
45587             if(this.editorcore.initialized){
45588                 this.tb.items.each(function(item){
45589                     item.enable();
45590                 });
45591             }
45592             
45593         }
45594         Roo.log("calling toggole on editor");
45595         // tell the editor that it's been pressed..
45596         this.editor.toggleSourceEdit(sourceEditMode);
45597        
45598     },
45599      /**
45600      * Object collection of toolbar tooltips for the buttons in the editor. The key
45601      * is the command id associated with that button and the value is a valid QuickTips object.
45602      * For example:
45603 <pre><code>
45604 {
45605     bold : {
45606         title: 'Bold (Ctrl+B)',
45607         text: 'Make the selected text bold.',
45608         cls: 'x-html-editor-tip'
45609     },
45610     italic : {
45611         title: 'Italic (Ctrl+I)',
45612         text: 'Make the selected text italic.',
45613         cls: 'x-html-editor-tip'
45614     },
45615     ...
45616 </code></pre>
45617     * @type Object
45618      */
45619     buttonTips : {
45620         bold : {
45621             title: 'Bold (Ctrl+B)',
45622             text: 'Make the selected text bold.',
45623             cls: 'x-html-editor-tip'
45624         },
45625         italic : {
45626             title: 'Italic (Ctrl+I)',
45627             text: 'Make the selected text italic.',
45628             cls: 'x-html-editor-tip'
45629         },
45630         underline : {
45631             title: 'Underline (Ctrl+U)',
45632             text: 'Underline the selected text.',
45633             cls: 'x-html-editor-tip'
45634         },
45635         strikethrough : {
45636             title: 'Strikethrough',
45637             text: 'Strikethrough the selected text.',
45638             cls: 'x-html-editor-tip'
45639         },
45640         increasefontsize : {
45641             title: 'Grow Text',
45642             text: 'Increase the font size.',
45643             cls: 'x-html-editor-tip'
45644         },
45645         decreasefontsize : {
45646             title: 'Shrink Text',
45647             text: 'Decrease the font size.',
45648             cls: 'x-html-editor-tip'
45649         },
45650         backcolor : {
45651             title: 'Text Highlight Color',
45652             text: 'Change the background color of the selected text.',
45653             cls: 'x-html-editor-tip'
45654         },
45655         forecolor : {
45656             title: 'Font Color',
45657             text: 'Change the color of the selected text.',
45658             cls: 'x-html-editor-tip'
45659         },
45660         justifyleft : {
45661             title: 'Align Text Left',
45662             text: 'Align text to the left.',
45663             cls: 'x-html-editor-tip'
45664         },
45665         justifycenter : {
45666             title: 'Center Text',
45667             text: 'Center text in the editor.',
45668             cls: 'x-html-editor-tip'
45669         },
45670         justifyright : {
45671             title: 'Align Text Right',
45672             text: 'Align text to the right.',
45673             cls: 'x-html-editor-tip'
45674         },
45675         insertunorderedlist : {
45676             title: 'Bullet List',
45677             text: 'Start a bulleted list.',
45678             cls: 'x-html-editor-tip'
45679         },
45680         insertorderedlist : {
45681             title: 'Numbered List',
45682             text: 'Start a numbered list.',
45683             cls: 'x-html-editor-tip'
45684         },
45685         createlink : {
45686             title: 'Hyperlink',
45687             text: 'Make the selected text a hyperlink.',
45688             cls: 'x-html-editor-tip'
45689         },
45690         sourceedit : {
45691             title: 'Source Edit',
45692             text: 'Switch to source editing mode.',
45693             cls: 'x-html-editor-tip'
45694         }
45695     },
45696     // private
45697     onDestroy : function(){
45698         if(this.rendered){
45699             
45700             this.tb.items.each(function(item){
45701                 if(item.menu){
45702                     item.menu.removeAll();
45703                     if(item.menu.el){
45704                         item.menu.el.destroy();
45705                     }
45706                 }
45707                 item.destroy();
45708             });
45709              
45710         }
45711     },
45712     onFirstFocus: function() {
45713         this.tb.items.each(function(item){
45714            item.enable();
45715         });
45716     }
45717 });
45718
45719
45720
45721
45722 // <script type="text/javascript">
45723 /*
45724  * Based on
45725  * Ext JS Library 1.1.1
45726  * Copyright(c) 2006-2007, Ext JS, LLC.
45727  *  
45728  
45729  */
45730
45731  
45732 /**
45733  * @class Roo.form.HtmlEditor.ToolbarContext
45734  * Context Toolbar
45735  * 
45736  * Usage:
45737  *
45738  new Roo.form.HtmlEditor({
45739     ....
45740     toolbars : [
45741         { xtype: 'ToolbarStandard', styles : {} }
45742         { xtype: 'ToolbarContext', disable : {} }
45743     ]
45744 })
45745
45746      
45747  * 
45748  * @config : {Object} disable List of elements to disable.. (not done yet.)
45749  * @config : {Object} styles  Map of styles available.
45750  * 
45751  */
45752
45753 Roo.form.HtmlEditor.ToolbarContext = function(config)
45754 {
45755     
45756     Roo.apply(this, config);
45757     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45758     // dont call parent... till later.
45759     this.styles = this.styles || {};
45760 }
45761
45762  
45763
45764 Roo.form.HtmlEditor.ToolbarContext.types = {
45765     'IMG' : {
45766         width : {
45767             title: "Width",
45768             width: 40
45769         },
45770         height:  {
45771             title: "Height",
45772             width: 40
45773         },
45774         align: {
45775             title: "Align",
45776             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45777             width : 80
45778             
45779         },
45780         border: {
45781             title: "Border",
45782             width: 40
45783         },
45784         alt: {
45785             title: "Alt",
45786             width: 120
45787         },
45788         src : {
45789             title: "Src",
45790             width: 220
45791         }
45792         
45793     },
45794     'A' : {
45795         name : {
45796             title: "Name",
45797             width: 50
45798         },
45799         target:  {
45800             title: "Target",
45801             width: 120
45802         },
45803         href:  {
45804             title: "Href",
45805             width: 220
45806         } // border?
45807         
45808     },
45809     'TABLE' : {
45810         rows : {
45811             title: "Rows",
45812             width: 20
45813         },
45814         cols : {
45815             title: "Cols",
45816             width: 20
45817         },
45818         width : {
45819             title: "Width",
45820             width: 40
45821         },
45822         height : {
45823             title: "Height",
45824             width: 40
45825         },
45826         border : {
45827             title: "Border",
45828             width: 20
45829         }
45830     },
45831     'TD' : {
45832         width : {
45833             title: "Width",
45834             width: 40
45835         },
45836         height : {
45837             title: "Height",
45838             width: 40
45839         },   
45840         align: {
45841             title: "Align",
45842             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45843             width: 80
45844         },
45845         valign: {
45846             title: "Valign",
45847             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45848             width: 80
45849         },
45850         colspan: {
45851             title: "Colspan",
45852             width: 20
45853             
45854         },
45855          'font-family'  : {
45856             title : "Font",
45857             style : 'fontFamily',
45858             displayField: 'display',
45859             optname : 'font-family',
45860             width: 140
45861         }
45862     },
45863     'INPUT' : {
45864         name : {
45865             title: "name",
45866             width: 120
45867         },
45868         value : {
45869             title: "Value",
45870             width: 120
45871         },
45872         width : {
45873             title: "Width",
45874             width: 40
45875         }
45876     },
45877     'LABEL' : {
45878         'for' : {
45879             title: "For",
45880             width: 120
45881         }
45882     },
45883     'TEXTAREA' : {
45884           name : {
45885             title: "name",
45886             width: 120
45887         },
45888         rows : {
45889             title: "Rows",
45890             width: 20
45891         },
45892         cols : {
45893             title: "Cols",
45894             width: 20
45895         }
45896     },
45897     'SELECT' : {
45898         name : {
45899             title: "name",
45900             width: 120
45901         },
45902         selectoptions : {
45903             title: "Options",
45904             width: 200
45905         }
45906     },
45907     
45908     // should we really allow this??
45909     // should this just be 
45910     'BODY' : {
45911         title : {
45912             title: "Title",
45913             width: 200,
45914             disabled : true
45915         }
45916     },
45917     'SPAN' : {
45918         'font-family'  : {
45919             title : "Font",
45920             style : 'fontFamily',
45921             displayField: 'display',
45922             optname : 'font-family',
45923             width: 140
45924         }
45925     },
45926     'DIV' : {
45927         'font-family'  : {
45928             title : "Font",
45929             style : 'fontFamily',
45930             displayField: 'display',
45931             optname : 'font-family',
45932             width: 140
45933         }
45934     },
45935      'P' : {
45936         'font-family'  : {
45937             title : "Font",
45938             style : 'fontFamily',
45939             displayField: 'display',
45940             optname : 'font-family',
45941             width: 140
45942         }
45943     },
45944     
45945     '*' : {
45946         // empty..
45947     }
45948
45949 };
45950
45951 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45952 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45953
45954 Roo.form.HtmlEditor.ToolbarContext.options = {
45955         'font-family'  : [ 
45956                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45957                 [ 'Courier New', 'Courier New'],
45958                 [ 'Tahoma', 'Tahoma'],
45959                 [ 'Times New Roman,serif', 'Times'],
45960                 [ 'Verdana','Verdana' ]
45961         ]
45962 };
45963
45964 // fixme - these need to be configurable..
45965  
45966
45967 //Roo.form.HtmlEditor.ToolbarContext.types
45968
45969
45970 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45971     
45972     tb: false,
45973     
45974     rendered: false,
45975     
45976     editor : false,
45977     editorcore : false,
45978     /**
45979      * @cfg {Object} disable  List of toolbar elements to disable
45980          
45981      */
45982     disable : false,
45983     /**
45984      * @cfg {Object} styles List of styles 
45985      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45986      *
45987      * These must be defined in the page, so they get rendered correctly..
45988      * .headline { }
45989      * TD.underline { }
45990      * 
45991      */
45992     styles : false,
45993     
45994     options: false,
45995     
45996     toolbars : false,
45997     
45998     init : function(editor)
45999     {
46000         this.editor = editor;
46001         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46002         var editorcore = this.editorcore;
46003         
46004         var fid = editorcore.frameId;
46005         var etb = this;
46006         function btn(id, toggle, handler){
46007             var xid = fid + '-'+ id ;
46008             return {
46009                 id : xid,
46010                 cmd : id,
46011                 cls : 'x-btn-icon x-edit-'+id,
46012                 enableToggle:toggle !== false,
46013                 scope: editorcore, // was editor...
46014                 handler:handler||editorcore.relayBtnCmd,
46015                 clickEvent:'mousedown',
46016                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46017                 tabIndex:-1
46018             };
46019         }
46020         // create a new element.
46021         var wdiv = editor.wrap.createChild({
46022                 tag: 'div'
46023             }, editor.wrap.dom.firstChild.nextSibling, true);
46024         
46025         // can we do this more than once??
46026         
46027          // stop form submits
46028       
46029  
46030         // disable everything...
46031         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46032         this.toolbars = {};
46033            
46034         for (var i in  ty) {
46035           
46036             this.toolbars[i] = this.buildToolbar(ty[i],i);
46037         }
46038         this.tb = this.toolbars.BODY;
46039         this.tb.el.show();
46040         this.buildFooter();
46041         this.footer.show();
46042         editor.on('hide', function( ) { this.footer.hide() }, this);
46043         editor.on('show', function( ) { this.footer.show() }, this);
46044         
46045          
46046         this.rendered = true;
46047         
46048         // the all the btns;
46049         editor.on('editorevent', this.updateToolbar, this);
46050         // other toolbars need to implement this..
46051         //editor.on('editmodechange', this.updateToolbar, this);
46052     },
46053     
46054     
46055     
46056     /**
46057      * Protected method that will not generally be called directly. It triggers
46058      * a toolbar update by reading the markup state of the current selection in the editor.
46059      *
46060      * Note you can force an update by calling on('editorevent', scope, false)
46061      */
46062     updateToolbar: function(editor,ev,sel){
46063
46064         //Roo.log(ev);
46065         // capture mouse up - this is handy for selecting images..
46066         // perhaps should go somewhere else...
46067         if(!this.editorcore.activated){
46068              this.editor.onFirstFocus();
46069             return;
46070         }
46071         
46072         
46073         
46074         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46075         // selectNode - might want to handle IE?
46076         if (ev &&
46077             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46078             ev.target && ev.target.tagName == 'IMG') {
46079             // they have click on an image...
46080             // let's see if we can change the selection...
46081             sel = ev.target;
46082          
46083               var nodeRange = sel.ownerDocument.createRange();
46084             try {
46085                 nodeRange.selectNode(sel);
46086             } catch (e) {
46087                 nodeRange.selectNodeContents(sel);
46088             }
46089             //nodeRange.collapse(true);
46090             var s = this.editorcore.win.getSelection();
46091             s.removeAllRanges();
46092             s.addRange(nodeRange);
46093         }  
46094         
46095       
46096         var updateFooter = sel ? false : true;
46097         
46098         
46099         var ans = this.editorcore.getAllAncestors();
46100         
46101         // pick
46102         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46103         
46104         if (!sel) { 
46105             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46106             sel = sel ? sel : this.editorcore.doc.body;
46107             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46108             
46109         }
46110         // pick a menu that exists..
46111         var tn = sel.tagName.toUpperCase();
46112         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46113         
46114         tn = sel.tagName.toUpperCase();
46115         
46116         var lastSel = this.tb.selectedNode;
46117         
46118         this.tb.selectedNode = sel;
46119         
46120         // if current menu does not match..
46121         
46122         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46123                 
46124             this.tb.el.hide();
46125             ///console.log("show: " + tn);
46126             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46127             this.tb.el.show();
46128             // update name
46129             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46130             
46131             
46132             // update attributes
46133             if (this.tb.fields) {
46134                 this.tb.fields.each(function(e) {
46135                     if (e.stylename) {
46136                         e.setValue(sel.style[e.stylename]);
46137                         return;
46138                     } 
46139                    e.setValue(sel.getAttribute(e.attrname));
46140                 });
46141             }
46142             
46143             var hasStyles = false;
46144             for(var i in this.styles) {
46145                 hasStyles = true;
46146                 break;
46147             }
46148             
46149             // update styles
46150             if (hasStyles) { 
46151                 var st = this.tb.fields.item(0);
46152                 
46153                 st.store.removeAll();
46154                
46155                 
46156                 var cn = sel.className.split(/\s+/);
46157                 
46158                 var avs = [];
46159                 if (this.styles['*']) {
46160                     
46161                     Roo.each(this.styles['*'], function(v) {
46162                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46163                     });
46164                 }
46165                 if (this.styles[tn]) { 
46166                     Roo.each(this.styles[tn], function(v) {
46167                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46168                     });
46169                 }
46170                 
46171                 st.store.loadData(avs);
46172                 st.collapse();
46173                 st.setValue(cn);
46174             }
46175             // flag our selected Node.
46176             this.tb.selectedNode = sel;
46177            
46178            
46179             Roo.menu.MenuMgr.hideAll();
46180
46181         }
46182         
46183         if (!updateFooter) {
46184             //this.footDisp.dom.innerHTML = ''; 
46185             return;
46186         }
46187         // update the footer
46188         //
46189         var html = '';
46190         
46191         this.footerEls = ans.reverse();
46192         Roo.each(this.footerEls, function(a,i) {
46193             if (!a) { return; }
46194             html += html.length ? ' &gt; '  :  '';
46195             
46196             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46197             
46198         });
46199        
46200         // 
46201         var sz = this.footDisp.up('td').getSize();
46202         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46203         this.footDisp.dom.style.marginLeft = '5px';
46204         
46205         this.footDisp.dom.style.overflow = 'hidden';
46206         
46207         this.footDisp.dom.innerHTML = html;
46208             
46209         //this.editorsyncValue();
46210     },
46211      
46212     
46213    
46214        
46215     // private
46216     onDestroy : function(){
46217         if(this.rendered){
46218             
46219             this.tb.items.each(function(item){
46220                 if(item.menu){
46221                     item.menu.removeAll();
46222                     if(item.menu.el){
46223                         item.menu.el.destroy();
46224                     }
46225                 }
46226                 item.destroy();
46227             });
46228              
46229         }
46230     },
46231     onFirstFocus: function() {
46232         // need to do this for all the toolbars..
46233         this.tb.items.each(function(item){
46234            item.enable();
46235         });
46236     },
46237     buildToolbar: function(tlist, nm)
46238     {
46239         var editor = this.editor;
46240         var editorcore = this.editorcore;
46241          // create a new element.
46242         var wdiv = editor.wrap.createChild({
46243                 tag: 'div'
46244             }, editor.wrap.dom.firstChild.nextSibling, true);
46245         
46246        
46247         var tb = new Roo.Toolbar(wdiv);
46248         // add the name..
46249         
46250         tb.add(nm+ ":&nbsp;");
46251         
46252         var styles = [];
46253         for(var i in this.styles) {
46254             styles.push(i);
46255         }
46256         
46257         // styles...
46258         if (styles && styles.length) {
46259             
46260             // this needs a multi-select checkbox...
46261             tb.addField( new Roo.form.ComboBox({
46262                 store: new Roo.data.SimpleStore({
46263                     id : 'val',
46264                     fields: ['val', 'selected'],
46265                     data : [] 
46266                 }),
46267                 name : '-roo-edit-className',
46268                 attrname : 'className',
46269                 displayField: 'val',
46270                 typeAhead: false,
46271                 mode: 'local',
46272                 editable : false,
46273                 triggerAction: 'all',
46274                 emptyText:'Select Style',
46275                 selectOnFocus:true,
46276                 width: 130,
46277                 listeners : {
46278                     'select': function(c, r, i) {
46279                         // initial support only for on class per el..
46280                         tb.selectedNode.className =  r ? r.get('val') : '';
46281                         editorcore.syncValue();
46282                     }
46283                 }
46284     
46285             }));
46286         }
46287         
46288         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46289         var tbops = tbc.options;
46290         
46291         for (var i in tlist) {
46292             
46293             var item = tlist[i];
46294             tb.add(item.title + ":&nbsp;");
46295             
46296             
46297             //optname == used so you can configure the options available..
46298             var opts = item.opts ? item.opts : false;
46299             if (item.optname) {
46300                 opts = tbops[item.optname];
46301            
46302             }
46303             
46304             if (opts) {
46305                 // opts == pulldown..
46306                 tb.addField( new Roo.form.ComboBox({
46307                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46308                         id : 'val',
46309                         fields: ['val', 'display'],
46310                         data : opts  
46311                     }),
46312                     name : '-roo-edit-' + i,
46313                     attrname : i,
46314                     stylename : item.style ? item.style : false,
46315                     displayField: item.displayField ? item.displayField : 'val',
46316                     valueField :  'val',
46317                     typeAhead: false,
46318                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46319                     editable : false,
46320                     triggerAction: 'all',
46321                     emptyText:'Select',
46322                     selectOnFocus:true,
46323                     width: item.width ? item.width  : 130,
46324                     listeners : {
46325                         'select': function(c, r, i) {
46326                             if (c.stylename) {
46327                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46328                                 return;
46329                             }
46330                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46331                         }
46332                     }
46333
46334                 }));
46335                 continue;
46336                     
46337                  
46338                 
46339                 tb.addField( new Roo.form.TextField({
46340                     name: i,
46341                     width: 100,
46342                     //allowBlank:false,
46343                     value: ''
46344                 }));
46345                 continue;
46346             }
46347             tb.addField( new Roo.form.TextField({
46348                 name: '-roo-edit-' + i,
46349                 attrname : i,
46350                 
46351                 width: item.width,
46352                 //allowBlank:true,
46353                 value: '',
46354                 listeners: {
46355                     'change' : function(f, nv, ov) {
46356                         tb.selectedNode.setAttribute(f.attrname, nv);
46357                         editorcore.syncValue();
46358                     }
46359                 }
46360             }));
46361              
46362         }
46363         
46364         var _this = this;
46365         
46366         if(nm == 'BODY'){
46367             tb.addSeparator();
46368         
46369             tb.addButton( {
46370                 text: 'Stylesheets',
46371
46372                 listeners : {
46373                     click : function ()
46374                     {
46375                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46376                     }
46377                 }
46378             });
46379         }
46380         
46381         tb.addFill();
46382         tb.addButton( {
46383             text: 'Remove Tag',
46384     
46385             listeners : {
46386                 click : function ()
46387                 {
46388                     // remove
46389                     // undo does not work.
46390                      
46391                     var sn = tb.selectedNode;
46392                     
46393                     var pn = sn.parentNode;
46394                     
46395                     var stn =  sn.childNodes[0];
46396                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46397                     while (sn.childNodes.length) {
46398                         var node = sn.childNodes[0];
46399                         sn.removeChild(node);
46400                         //Roo.log(node);
46401                         pn.insertBefore(node, sn);
46402                         
46403                     }
46404                     pn.removeChild(sn);
46405                     var range = editorcore.createRange();
46406         
46407                     range.setStart(stn,0);
46408                     range.setEnd(en,0); //????
46409                     //range.selectNode(sel);
46410                     
46411                     
46412                     var selection = editorcore.getSelection();
46413                     selection.removeAllRanges();
46414                     selection.addRange(range);
46415                     
46416                     
46417                     
46418                     //_this.updateToolbar(null, null, pn);
46419                     _this.updateToolbar(null, null, null);
46420                     _this.footDisp.dom.innerHTML = ''; 
46421                 }
46422             }
46423             
46424                     
46425                 
46426             
46427         });
46428         
46429         
46430         tb.el.on('click', function(e){
46431             e.preventDefault(); // what does this do?
46432         });
46433         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46434         tb.el.hide();
46435         tb.name = nm;
46436         // dont need to disable them... as they will get hidden
46437         return tb;
46438          
46439         
46440     },
46441     buildFooter : function()
46442     {
46443         
46444         var fel = this.editor.wrap.createChild();
46445         this.footer = new Roo.Toolbar(fel);
46446         // toolbar has scrolly on left / right?
46447         var footDisp= new Roo.Toolbar.Fill();
46448         var _t = this;
46449         this.footer.add(
46450             {
46451                 text : '&lt;',
46452                 xtype: 'Button',
46453                 handler : function() {
46454                     _t.footDisp.scrollTo('left',0,true)
46455                 }
46456             }
46457         );
46458         this.footer.add( footDisp );
46459         this.footer.add( 
46460             {
46461                 text : '&gt;',
46462                 xtype: 'Button',
46463                 handler : function() {
46464                     // no animation..
46465                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46466                 }
46467             }
46468         );
46469         var fel = Roo.get(footDisp.el);
46470         fel.addClass('x-editor-context');
46471         this.footDispWrap = fel; 
46472         this.footDispWrap.overflow  = 'hidden';
46473         
46474         this.footDisp = fel.createChild();
46475         this.footDispWrap.on('click', this.onContextClick, this)
46476         
46477         
46478     },
46479     onContextClick : function (ev,dom)
46480     {
46481         ev.preventDefault();
46482         var  cn = dom.className;
46483         //Roo.log(cn);
46484         if (!cn.match(/x-ed-loc-/)) {
46485             return;
46486         }
46487         var n = cn.split('-').pop();
46488         var ans = this.footerEls;
46489         var sel = ans[n];
46490         
46491          // pick
46492         var range = this.editorcore.createRange();
46493         
46494         range.selectNodeContents(sel);
46495         //range.selectNode(sel);
46496         
46497         
46498         var selection = this.editorcore.getSelection();
46499         selection.removeAllRanges();
46500         selection.addRange(range);
46501         
46502         
46503         
46504         this.updateToolbar(null, null, sel);
46505         
46506         
46507     }
46508     
46509     
46510     
46511     
46512     
46513 });
46514
46515
46516
46517
46518
46519 /*
46520  * Based on:
46521  * Ext JS Library 1.1.1
46522  * Copyright(c) 2006-2007, Ext JS, LLC.
46523  *
46524  * Originally Released Under LGPL - original licence link has changed is not relivant.
46525  *
46526  * Fork - LGPL
46527  * <script type="text/javascript">
46528  */
46529  
46530 /**
46531  * @class Roo.form.BasicForm
46532  * @extends Roo.util.Observable
46533  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46534  * @constructor
46535  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46536  * @param {Object} config Configuration options
46537  */
46538 Roo.form.BasicForm = function(el, config){
46539     this.allItems = [];
46540     this.childForms = [];
46541     Roo.apply(this, config);
46542     /*
46543      * The Roo.form.Field items in this form.
46544      * @type MixedCollection
46545      */
46546      
46547      
46548     this.items = new Roo.util.MixedCollection(false, function(o){
46549         return o.id || (o.id = Roo.id());
46550     });
46551     this.addEvents({
46552         /**
46553          * @event beforeaction
46554          * Fires before any action is performed. Return false to cancel the action.
46555          * @param {Form} this
46556          * @param {Action} action The action to be performed
46557          */
46558         beforeaction: true,
46559         /**
46560          * @event actionfailed
46561          * Fires when an action fails.
46562          * @param {Form} this
46563          * @param {Action} action The action that failed
46564          */
46565         actionfailed : true,
46566         /**
46567          * @event actioncomplete
46568          * Fires when an action is completed.
46569          * @param {Form} this
46570          * @param {Action} action The action that completed
46571          */
46572         actioncomplete : true
46573     });
46574     if(el){
46575         this.initEl(el);
46576     }
46577     Roo.form.BasicForm.superclass.constructor.call(this);
46578 };
46579
46580 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46581     /**
46582      * @cfg {String} method
46583      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46584      */
46585     /**
46586      * @cfg {DataReader} reader
46587      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46588      * This is optional as there is built-in support for processing JSON.
46589      */
46590     /**
46591      * @cfg {DataReader} errorReader
46592      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46593      * This is completely optional as there is built-in support for processing JSON.
46594      */
46595     /**
46596      * @cfg {String} url
46597      * The URL to use for form actions if one isn't supplied in the action options.
46598      */
46599     /**
46600      * @cfg {Boolean} fileUpload
46601      * Set to true if this form is a file upload.
46602      */
46603      
46604     /**
46605      * @cfg {Object} baseParams
46606      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46607      */
46608      /**
46609      
46610     /**
46611      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46612      */
46613     timeout: 30,
46614
46615     // private
46616     activeAction : null,
46617
46618     /**
46619      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46620      * or setValues() data instead of when the form was first created.
46621      */
46622     trackResetOnLoad : false,
46623     
46624     
46625     /**
46626      * childForms - used for multi-tab forms
46627      * @type {Array}
46628      */
46629     childForms : false,
46630     
46631     /**
46632      * allItems - full list of fields.
46633      * @type {Array}
46634      */
46635     allItems : false,
46636     
46637     /**
46638      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46639      * element by passing it or its id or mask the form itself by passing in true.
46640      * @type Mixed
46641      */
46642     waitMsgTarget : false,
46643
46644     // private
46645     initEl : function(el){
46646         this.el = Roo.get(el);
46647         this.id = this.el.id || Roo.id();
46648         this.el.on('submit', this.onSubmit, this);
46649         this.el.addClass('x-form');
46650     },
46651
46652     // private
46653     onSubmit : function(e){
46654         e.stopEvent();
46655     },
46656
46657     /**
46658      * Returns true if client-side validation on the form is successful.
46659      * @return Boolean
46660      */
46661     isValid : function(){
46662         var valid = true;
46663         this.items.each(function(f){
46664            if(!f.validate()){
46665                valid = false;
46666            }
46667         });
46668         return valid;
46669     },
46670
46671     /**
46672      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46673      * @return Boolean
46674      */
46675     isDirty : function(){
46676         var dirty = false;
46677         this.items.each(function(f){
46678            if(f.isDirty()){
46679                dirty = true;
46680                return false;
46681            }
46682         });
46683         return dirty;
46684     },
46685     
46686     /**
46687      * Returns true if any fields in this form have changed since their original load. (New version)
46688      * @return Boolean
46689      */
46690     
46691     hasChanged : function()
46692     {
46693         var dirty = false;
46694         this.items.each(function(f){
46695            if(f.hasChanged()){
46696                dirty = true;
46697                return false;
46698            }
46699         });
46700         return dirty;
46701         
46702     },
46703     /**
46704      * Resets all hasChanged to 'false' -
46705      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46706      * So hasChanged storage is only to be used for this purpose
46707      * @return Boolean
46708      */
46709     resetHasChanged : function()
46710     {
46711         this.items.each(function(f){
46712            f.resetHasChanged();
46713         });
46714         
46715     },
46716     
46717     
46718     /**
46719      * Performs a predefined action (submit or load) or custom actions you define on this form.
46720      * @param {String} actionName The name of the action type
46721      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46722      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46723      * accept other config options):
46724      * <pre>
46725 Property          Type             Description
46726 ----------------  ---------------  ----------------------------------------------------------------------------------
46727 url               String           The url for the action (defaults to the form's url)
46728 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46729 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46730 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46731                                    validate the form on the client (defaults to false)
46732      * </pre>
46733      * @return {BasicForm} this
46734      */
46735     doAction : function(action, options){
46736         if(typeof action == 'string'){
46737             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46738         }
46739         if(this.fireEvent('beforeaction', this, action) !== false){
46740             this.beforeAction(action);
46741             action.run.defer(100, action);
46742         }
46743         return this;
46744     },
46745
46746     /**
46747      * Shortcut to do a submit action.
46748      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46749      * @return {BasicForm} this
46750      */
46751     submit : function(options){
46752         this.doAction('submit', options);
46753         return this;
46754     },
46755
46756     /**
46757      * Shortcut to do a load action.
46758      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46759      * @return {BasicForm} this
46760      */
46761     load : function(options){
46762         this.doAction('load', options);
46763         return this;
46764     },
46765
46766     /**
46767      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46768      * @param {Record} record The record to edit
46769      * @return {BasicForm} this
46770      */
46771     updateRecord : function(record){
46772         record.beginEdit();
46773         var fs = record.fields;
46774         fs.each(function(f){
46775             var field = this.findField(f.name);
46776             if(field){
46777                 record.set(f.name, field.getValue());
46778             }
46779         }, this);
46780         record.endEdit();
46781         return this;
46782     },
46783
46784     /**
46785      * Loads an Roo.data.Record into this form.
46786      * @param {Record} record The record to load
46787      * @return {BasicForm} this
46788      */
46789     loadRecord : function(record){
46790         this.setValues(record.data);
46791         return this;
46792     },
46793
46794     // private
46795     beforeAction : function(action){
46796         var o = action.options;
46797         
46798        
46799         if(this.waitMsgTarget === true){
46800             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46801         }else if(this.waitMsgTarget){
46802             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46803             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46804         }else {
46805             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46806         }
46807          
46808     },
46809
46810     // private
46811     afterAction : function(action, success){
46812         this.activeAction = null;
46813         var o = action.options;
46814         
46815         if(this.waitMsgTarget === true){
46816             this.el.unmask();
46817         }else if(this.waitMsgTarget){
46818             this.waitMsgTarget.unmask();
46819         }else{
46820             Roo.MessageBox.updateProgress(1);
46821             Roo.MessageBox.hide();
46822         }
46823          
46824         if(success){
46825             if(o.reset){
46826                 this.reset();
46827             }
46828             Roo.callback(o.success, o.scope, [this, action]);
46829             this.fireEvent('actioncomplete', this, action);
46830             
46831         }else{
46832             
46833             // failure condition..
46834             // we have a scenario where updates need confirming.
46835             // eg. if a locking scenario exists..
46836             // we look for { errors : { needs_confirm : true }} in the response.
46837             if (
46838                 (typeof(action.result) != 'undefined')  &&
46839                 (typeof(action.result.errors) != 'undefined')  &&
46840                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46841            ){
46842                 var _t = this;
46843                 Roo.MessageBox.confirm(
46844                     "Change requires confirmation",
46845                     action.result.errorMsg,
46846                     function(r) {
46847                         if (r != 'yes') {
46848                             return;
46849                         }
46850                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46851                     }
46852                     
46853                 );
46854                 
46855                 
46856                 
46857                 return;
46858             }
46859             
46860             Roo.callback(o.failure, o.scope, [this, action]);
46861             // show an error message if no failed handler is set..
46862             if (!this.hasListener('actionfailed')) {
46863                 Roo.MessageBox.alert("Error",
46864                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46865                         action.result.errorMsg :
46866                         "Saving Failed, please check your entries or try again"
46867                 );
46868             }
46869             
46870             this.fireEvent('actionfailed', this, action);
46871         }
46872         
46873     },
46874
46875     /**
46876      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46877      * @param {String} id The value to search for
46878      * @return Field
46879      */
46880     findField : function(id){
46881         var field = this.items.get(id);
46882         if(!field){
46883             this.items.each(function(f){
46884                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46885                     field = f;
46886                     return false;
46887                 }
46888             });
46889         }
46890         return field || null;
46891     },
46892
46893     /**
46894      * Add a secondary form to this one, 
46895      * Used to provide tabbed forms. One form is primary, with hidden values 
46896      * which mirror the elements from the other forms.
46897      * 
46898      * @param {Roo.form.Form} form to add.
46899      * 
46900      */
46901     addForm : function(form)
46902     {
46903        
46904         if (this.childForms.indexOf(form) > -1) {
46905             // already added..
46906             return;
46907         }
46908         this.childForms.push(form);
46909         var n = '';
46910         Roo.each(form.allItems, function (fe) {
46911             
46912             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46913             if (this.findField(n)) { // already added..
46914                 return;
46915             }
46916             var add = new Roo.form.Hidden({
46917                 name : n
46918             });
46919             add.render(this.el);
46920             
46921             this.add( add );
46922         }, this);
46923         
46924     },
46925     /**
46926      * Mark fields in this form invalid in bulk.
46927      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46928      * @return {BasicForm} this
46929      */
46930     markInvalid : function(errors){
46931         if(errors instanceof Array){
46932             for(var i = 0, len = errors.length; i < len; i++){
46933                 var fieldError = errors[i];
46934                 var f = this.findField(fieldError.id);
46935                 if(f){
46936                     f.markInvalid(fieldError.msg);
46937                 }
46938             }
46939         }else{
46940             var field, id;
46941             for(id in errors){
46942                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46943                     field.markInvalid(errors[id]);
46944                 }
46945             }
46946         }
46947         Roo.each(this.childForms || [], function (f) {
46948             f.markInvalid(errors);
46949         });
46950         
46951         return this;
46952     },
46953
46954     /**
46955      * Set values for fields in this form in bulk.
46956      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46957      * @return {BasicForm} this
46958      */
46959     setValues : function(values){
46960         if(values instanceof Array){ // array of objects
46961             for(var i = 0, len = values.length; i < len; i++){
46962                 var v = values[i];
46963                 var f = this.findField(v.id);
46964                 if(f){
46965                     f.setValue(v.value);
46966                     if(this.trackResetOnLoad){
46967                         f.originalValue = f.getValue();
46968                     }
46969                 }
46970             }
46971         }else{ // object hash
46972             var field, id;
46973             for(id in values){
46974                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46975                     
46976                     if (field.setFromData && 
46977                         field.valueField && 
46978                         field.displayField &&
46979                         // combos' with local stores can 
46980                         // be queried via setValue()
46981                         // to set their value..
46982                         (field.store && !field.store.isLocal)
46983                         ) {
46984                         // it's a combo
46985                         var sd = { };
46986                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46987                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46988                         field.setFromData(sd);
46989                         
46990                     } else {
46991                         field.setValue(values[id]);
46992                     }
46993                     
46994                     
46995                     if(this.trackResetOnLoad){
46996                         field.originalValue = field.getValue();
46997                     }
46998                 }
46999             }
47000         }
47001         this.resetHasChanged();
47002         
47003         
47004         Roo.each(this.childForms || [], function (f) {
47005             f.setValues(values);
47006             f.resetHasChanged();
47007         });
47008                 
47009         return this;
47010     },
47011
47012     /**
47013      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47014      * they are returned as an array.
47015      * @param {Boolean} asString
47016      * @return {Object}
47017      */
47018     getValues : function(asString){
47019         if (this.childForms) {
47020             // copy values from the child forms
47021             Roo.each(this.childForms, function (f) {
47022                 this.setValues(f.getValues());
47023             }, this);
47024         }
47025         
47026         
47027         
47028         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47029         if(asString === true){
47030             return fs;
47031         }
47032         return Roo.urlDecode(fs);
47033     },
47034     
47035     /**
47036      * Returns the fields in this form as an object with key/value pairs. 
47037      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47038      * @return {Object}
47039      */
47040     getFieldValues : function(with_hidden)
47041     {
47042         if (this.childForms) {
47043             // copy values from the child forms
47044             // should this call getFieldValues - probably not as we do not currently copy
47045             // hidden fields when we generate..
47046             Roo.each(this.childForms, function (f) {
47047                 this.setValues(f.getValues());
47048             }, this);
47049         }
47050         
47051         var ret = {};
47052         this.items.each(function(f){
47053             if (!f.getName()) {
47054                 return;
47055             }
47056             var v = f.getValue();
47057             if (f.inputType =='radio') {
47058                 if (typeof(ret[f.getName()]) == 'undefined') {
47059                     ret[f.getName()] = ''; // empty..
47060                 }
47061                 
47062                 if (!f.el.dom.checked) {
47063                     return;
47064                     
47065                 }
47066                 v = f.el.dom.value;
47067                 
47068             }
47069             
47070             // not sure if this supported any more..
47071             if ((typeof(v) == 'object') && f.getRawValue) {
47072                 v = f.getRawValue() ; // dates..
47073             }
47074             // combo boxes where name != hiddenName...
47075             if (f.name != f.getName()) {
47076                 ret[f.name] = f.getRawValue();
47077             }
47078             ret[f.getName()] = v;
47079         });
47080         
47081         return ret;
47082     },
47083
47084     /**
47085      * Clears all invalid messages in this form.
47086      * @return {BasicForm} this
47087      */
47088     clearInvalid : function(){
47089         this.items.each(function(f){
47090            f.clearInvalid();
47091         });
47092         
47093         Roo.each(this.childForms || [], function (f) {
47094             f.clearInvalid();
47095         });
47096         
47097         
47098         return this;
47099     },
47100
47101     /**
47102      * Resets this form.
47103      * @return {BasicForm} this
47104      */
47105     reset : function(){
47106         this.items.each(function(f){
47107             f.reset();
47108         });
47109         
47110         Roo.each(this.childForms || [], function (f) {
47111             f.reset();
47112         });
47113         this.resetHasChanged();
47114         
47115         return this;
47116     },
47117
47118     /**
47119      * Add Roo.form components to this form.
47120      * @param {Field} field1
47121      * @param {Field} field2 (optional)
47122      * @param {Field} etc (optional)
47123      * @return {BasicForm} this
47124      */
47125     add : function(){
47126         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47127         return this;
47128     },
47129
47130
47131     /**
47132      * Removes a field from the items collection (does NOT remove its markup).
47133      * @param {Field} field
47134      * @return {BasicForm} this
47135      */
47136     remove : function(field){
47137         this.items.remove(field);
47138         return this;
47139     },
47140
47141     /**
47142      * Looks at the fields in this form, checks them for an id attribute,
47143      * and calls applyTo on the existing dom element with that id.
47144      * @return {BasicForm} this
47145      */
47146     render : function(){
47147         this.items.each(function(f){
47148             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47149                 f.applyTo(f.id);
47150             }
47151         });
47152         return this;
47153     },
47154
47155     /**
47156      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47157      * @param {Object} values
47158      * @return {BasicForm} this
47159      */
47160     applyToFields : function(o){
47161         this.items.each(function(f){
47162            Roo.apply(f, o);
47163         });
47164         return this;
47165     },
47166
47167     /**
47168      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47169      * @param {Object} values
47170      * @return {BasicForm} this
47171      */
47172     applyIfToFields : function(o){
47173         this.items.each(function(f){
47174            Roo.applyIf(f, o);
47175         });
47176         return this;
47177     }
47178 });
47179
47180 // back compat
47181 Roo.BasicForm = Roo.form.BasicForm;/*
47182  * Based on:
47183  * Ext JS Library 1.1.1
47184  * Copyright(c) 2006-2007, Ext JS, LLC.
47185  *
47186  * Originally Released Under LGPL - original licence link has changed is not relivant.
47187  *
47188  * Fork - LGPL
47189  * <script type="text/javascript">
47190  */
47191
47192 /**
47193  * @class Roo.form.Form
47194  * @extends Roo.form.BasicForm
47195  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47196  * @constructor
47197  * @param {Object} config Configuration options
47198  */
47199 Roo.form.Form = function(config){
47200     var xitems =  [];
47201     if (config.items) {
47202         xitems = config.items;
47203         delete config.items;
47204     }
47205    
47206     
47207     Roo.form.Form.superclass.constructor.call(this, null, config);
47208     this.url = this.url || this.action;
47209     if(!this.root){
47210         this.root = new Roo.form.Layout(Roo.applyIf({
47211             id: Roo.id()
47212         }, config));
47213     }
47214     this.active = this.root;
47215     /**
47216      * Array of all the buttons that have been added to this form via {@link addButton}
47217      * @type Array
47218      */
47219     this.buttons = [];
47220     this.allItems = [];
47221     this.addEvents({
47222         /**
47223          * @event clientvalidation
47224          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47225          * @param {Form} this
47226          * @param {Boolean} valid true if the form has passed client-side validation
47227          */
47228         clientvalidation: true,
47229         /**
47230          * @event rendered
47231          * Fires when the form is rendered
47232          * @param {Roo.form.Form} form
47233          */
47234         rendered : true
47235     });
47236     
47237     if (this.progressUrl) {
47238             // push a hidden field onto the list of fields..
47239             this.addxtype( {
47240                     xns: Roo.form, 
47241                     xtype : 'Hidden', 
47242                     name : 'UPLOAD_IDENTIFIER' 
47243             });
47244         }
47245         
47246     
47247     Roo.each(xitems, this.addxtype, this);
47248     
47249     
47250     
47251 };
47252
47253 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47254     /**
47255      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47256      */
47257     /**
47258      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47259      */
47260     /**
47261      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47262      */
47263     buttonAlign:'center',
47264
47265     /**
47266      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47267      */
47268     minButtonWidth:75,
47269
47270     /**
47271      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47272      * This property cascades to child containers if not set.
47273      */
47274     labelAlign:'left',
47275
47276     /**
47277      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47278      * fires a looping event with that state. This is required to bind buttons to the valid
47279      * state using the config value formBind:true on the button.
47280      */
47281     monitorValid : false,
47282
47283     /**
47284      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47285      */
47286     monitorPoll : 200,
47287     
47288     /**
47289      * @cfg {String} progressUrl - Url to return progress data 
47290      */
47291     
47292     progressUrl : false,
47293   
47294     /**
47295      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47296      * fields are added and the column is closed. If no fields are passed the column remains open
47297      * until end() is called.
47298      * @param {Object} config The config to pass to the column
47299      * @param {Field} field1 (optional)
47300      * @param {Field} field2 (optional)
47301      * @param {Field} etc (optional)
47302      * @return Column The column container object
47303      */
47304     column : function(c){
47305         var col = new Roo.form.Column(c);
47306         this.start(col);
47307         if(arguments.length > 1){ // duplicate code required because of Opera
47308             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47309             this.end();
47310         }
47311         return col;
47312     },
47313
47314     /**
47315      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47316      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47317      * until end() is called.
47318      * @param {Object} config The config to pass to the fieldset
47319      * @param {Field} field1 (optional)
47320      * @param {Field} field2 (optional)
47321      * @param {Field} etc (optional)
47322      * @return FieldSet The fieldset container object
47323      */
47324     fieldset : function(c){
47325         var fs = new Roo.form.FieldSet(c);
47326         this.start(fs);
47327         if(arguments.length > 1){ // duplicate code required because of Opera
47328             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47329             this.end();
47330         }
47331         return fs;
47332     },
47333
47334     /**
47335      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47336      * fields are added and the container is closed. If no fields are passed the container remains open
47337      * until end() is called.
47338      * @param {Object} config The config to pass to the Layout
47339      * @param {Field} field1 (optional)
47340      * @param {Field} field2 (optional)
47341      * @param {Field} etc (optional)
47342      * @return Layout The container object
47343      */
47344     container : function(c){
47345         var l = new Roo.form.Layout(c);
47346         this.start(l);
47347         if(arguments.length > 1){ // duplicate code required because of Opera
47348             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47349             this.end();
47350         }
47351         return l;
47352     },
47353
47354     /**
47355      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47356      * @param {Object} container A Roo.form.Layout or subclass of Layout
47357      * @return {Form} this
47358      */
47359     start : function(c){
47360         // cascade label info
47361         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47362         this.active.stack.push(c);
47363         c.ownerCt = this.active;
47364         this.active = c;
47365         return this;
47366     },
47367
47368     /**
47369      * Closes the current open container
47370      * @return {Form} this
47371      */
47372     end : function(){
47373         if(this.active == this.root){
47374             return this;
47375         }
47376         this.active = this.active.ownerCt;
47377         return this;
47378     },
47379
47380     /**
47381      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47382      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47383      * as the label of the field.
47384      * @param {Field} field1
47385      * @param {Field} field2 (optional)
47386      * @param {Field} etc. (optional)
47387      * @return {Form} this
47388      */
47389     add : function(){
47390         this.active.stack.push.apply(this.active.stack, arguments);
47391         this.allItems.push.apply(this.allItems,arguments);
47392         var r = [];
47393         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47394             if(a[i].isFormField){
47395                 r.push(a[i]);
47396             }
47397         }
47398         if(r.length > 0){
47399             Roo.form.Form.superclass.add.apply(this, r);
47400         }
47401         return this;
47402     },
47403     
47404
47405     
47406     
47407     
47408      /**
47409      * Find any element that has been added to a form, using it's ID or name
47410      * This can include framesets, columns etc. along with regular fields..
47411      * @param {String} id - id or name to find.
47412      
47413      * @return {Element} e - or false if nothing found.
47414      */
47415     findbyId : function(id)
47416     {
47417         var ret = false;
47418         if (!id) {
47419             return ret;
47420         }
47421         Roo.each(this.allItems, function(f){
47422             if (f.id == id || f.name == id ){
47423                 ret = f;
47424                 return false;
47425             }
47426         });
47427         return ret;
47428     },
47429
47430     
47431     
47432     /**
47433      * Render this form into the passed container. This should only be called once!
47434      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47435      * @return {Form} this
47436      */
47437     render : function(ct)
47438     {
47439         
47440         
47441         
47442         ct = Roo.get(ct);
47443         var o = this.autoCreate || {
47444             tag: 'form',
47445             method : this.method || 'POST',
47446             id : this.id || Roo.id()
47447         };
47448         this.initEl(ct.createChild(o));
47449
47450         this.root.render(this.el);
47451         
47452        
47453              
47454         this.items.each(function(f){
47455             f.render('x-form-el-'+f.id);
47456         });
47457
47458         if(this.buttons.length > 0){
47459             // tables are required to maintain order and for correct IE layout
47460             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47461                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47462                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47463             }}, null, true);
47464             var tr = tb.getElementsByTagName('tr')[0];
47465             for(var i = 0, len = this.buttons.length; i < len; i++) {
47466                 var b = this.buttons[i];
47467                 var td = document.createElement('td');
47468                 td.className = 'x-form-btn-td';
47469                 b.render(tr.appendChild(td));
47470             }
47471         }
47472         if(this.monitorValid){ // initialize after render
47473             this.startMonitoring();
47474         }
47475         this.fireEvent('rendered', this);
47476         return this;
47477     },
47478
47479     /**
47480      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47481      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47482      * object or a valid Roo.DomHelper element config
47483      * @param {Function} handler The function called when the button is clicked
47484      * @param {Object} scope (optional) The scope of the handler function
47485      * @return {Roo.Button}
47486      */
47487     addButton : function(config, handler, scope){
47488         var bc = {
47489             handler: handler,
47490             scope: scope,
47491             minWidth: this.minButtonWidth,
47492             hideParent:true
47493         };
47494         if(typeof config == "string"){
47495             bc.text = config;
47496         }else{
47497             Roo.apply(bc, config);
47498         }
47499         var btn = new Roo.Button(null, bc);
47500         this.buttons.push(btn);
47501         return btn;
47502     },
47503
47504      /**
47505      * Adds a series of form elements (using the xtype property as the factory method.
47506      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47507      * @param {Object} config 
47508      */
47509     
47510     addxtype : function()
47511     {
47512         var ar = Array.prototype.slice.call(arguments, 0);
47513         var ret = false;
47514         for(var i = 0; i < ar.length; i++) {
47515             if (!ar[i]) {
47516                 continue; // skip -- if this happends something invalid got sent, we 
47517                 // should ignore it, as basically that interface element will not show up
47518                 // and that should be pretty obvious!!
47519             }
47520             
47521             if (Roo.form[ar[i].xtype]) {
47522                 ar[i].form = this;
47523                 var fe = Roo.factory(ar[i], Roo.form);
47524                 if (!ret) {
47525                     ret = fe;
47526                 }
47527                 fe.form = this;
47528                 if (fe.store) {
47529                     fe.store.form = this;
47530                 }
47531                 if (fe.isLayout) {  
47532                          
47533                     this.start(fe);
47534                     this.allItems.push(fe);
47535                     if (fe.items && fe.addxtype) {
47536                         fe.addxtype.apply(fe, fe.items);
47537                         delete fe.items;
47538                     }
47539                      this.end();
47540                     continue;
47541                 }
47542                 
47543                 
47544                  
47545                 this.add(fe);
47546               //  console.log('adding ' + ar[i].xtype);
47547             }
47548             if (ar[i].xtype == 'Button') {  
47549                 //console.log('adding button');
47550                 //console.log(ar[i]);
47551                 this.addButton(ar[i]);
47552                 this.allItems.push(fe);
47553                 continue;
47554             }
47555             
47556             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47557                 alert('end is not supported on xtype any more, use items');
47558             //    this.end();
47559             //    //console.log('adding end');
47560             }
47561             
47562         }
47563         return ret;
47564     },
47565     
47566     /**
47567      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47568      * option "monitorValid"
47569      */
47570     startMonitoring : function(){
47571         if(!this.bound){
47572             this.bound = true;
47573             Roo.TaskMgr.start({
47574                 run : this.bindHandler,
47575                 interval : this.monitorPoll || 200,
47576                 scope: this
47577             });
47578         }
47579     },
47580
47581     /**
47582      * Stops monitoring of the valid state of this form
47583      */
47584     stopMonitoring : function(){
47585         this.bound = false;
47586     },
47587
47588     // private
47589     bindHandler : function(){
47590         if(!this.bound){
47591             return false; // stops binding
47592         }
47593         var valid = true;
47594         this.items.each(function(f){
47595             if(!f.isValid(true)){
47596                 valid = false;
47597                 return false;
47598             }
47599         });
47600         for(var i = 0, len = this.buttons.length; i < len; i++){
47601             var btn = this.buttons[i];
47602             if(btn.formBind === true && btn.disabled === valid){
47603                 btn.setDisabled(!valid);
47604             }
47605         }
47606         this.fireEvent('clientvalidation', this, valid);
47607     }
47608     
47609     
47610     
47611     
47612     
47613     
47614     
47615     
47616 });
47617
47618
47619 // back compat
47620 Roo.Form = Roo.form.Form;
47621 /*
47622  * Based on:
47623  * Ext JS Library 1.1.1
47624  * Copyright(c) 2006-2007, Ext JS, LLC.
47625  *
47626  * Originally Released Under LGPL - original licence link has changed is not relivant.
47627  *
47628  * Fork - LGPL
47629  * <script type="text/javascript">
47630  */
47631
47632 // as we use this in bootstrap.
47633 Roo.namespace('Roo.form');
47634  /**
47635  * @class Roo.form.Action
47636  * Internal Class used to handle form actions
47637  * @constructor
47638  * @param {Roo.form.BasicForm} el The form element or its id
47639  * @param {Object} config Configuration options
47640  */
47641
47642  
47643  
47644 // define the action interface
47645 Roo.form.Action = function(form, options){
47646     this.form = form;
47647     this.options = options || {};
47648 };
47649 /**
47650  * Client Validation Failed
47651  * @const 
47652  */
47653 Roo.form.Action.CLIENT_INVALID = 'client';
47654 /**
47655  * Server Validation Failed
47656  * @const 
47657  */
47658 Roo.form.Action.SERVER_INVALID = 'server';
47659  /**
47660  * Connect to Server Failed
47661  * @const 
47662  */
47663 Roo.form.Action.CONNECT_FAILURE = 'connect';
47664 /**
47665  * Reading Data from Server Failed
47666  * @const 
47667  */
47668 Roo.form.Action.LOAD_FAILURE = 'load';
47669
47670 Roo.form.Action.prototype = {
47671     type : 'default',
47672     failureType : undefined,
47673     response : undefined,
47674     result : undefined,
47675
47676     // interface method
47677     run : function(options){
47678
47679     },
47680
47681     // interface method
47682     success : function(response){
47683
47684     },
47685
47686     // interface method
47687     handleResponse : function(response){
47688
47689     },
47690
47691     // default connection failure
47692     failure : function(response){
47693         
47694         this.response = response;
47695         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47696         this.form.afterAction(this, false);
47697     },
47698
47699     processResponse : function(response){
47700         this.response = response;
47701         if(!response.responseText){
47702             return true;
47703         }
47704         this.result = this.handleResponse(response);
47705         return this.result;
47706     },
47707
47708     // utility functions used internally
47709     getUrl : function(appendParams){
47710         var url = this.options.url || this.form.url || this.form.el.dom.action;
47711         if(appendParams){
47712             var p = this.getParams();
47713             if(p){
47714                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47715             }
47716         }
47717         return url;
47718     },
47719
47720     getMethod : function(){
47721         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47722     },
47723
47724     getParams : function(){
47725         var bp = this.form.baseParams;
47726         var p = this.options.params;
47727         if(p){
47728             if(typeof p == "object"){
47729                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47730             }else if(typeof p == 'string' && bp){
47731                 p += '&' + Roo.urlEncode(bp);
47732             }
47733         }else if(bp){
47734             p = Roo.urlEncode(bp);
47735         }
47736         return p;
47737     },
47738
47739     createCallback : function(){
47740         return {
47741             success: this.success,
47742             failure: this.failure,
47743             scope: this,
47744             timeout: (this.form.timeout*1000),
47745             upload: this.form.fileUpload ? this.success : undefined
47746         };
47747     }
47748 };
47749
47750 Roo.form.Action.Submit = function(form, options){
47751     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47752 };
47753
47754 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47755     type : 'submit',
47756
47757     haveProgress : false,
47758     uploadComplete : false,
47759     
47760     // uploadProgress indicator.
47761     uploadProgress : function()
47762     {
47763         if (!this.form.progressUrl) {
47764             return;
47765         }
47766         
47767         if (!this.haveProgress) {
47768             Roo.MessageBox.progress("Uploading", "Uploading");
47769         }
47770         if (this.uploadComplete) {
47771            Roo.MessageBox.hide();
47772            return;
47773         }
47774         
47775         this.haveProgress = true;
47776    
47777         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47778         
47779         var c = new Roo.data.Connection();
47780         c.request({
47781             url : this.form.progressUrl,
47782             params: {
47783                 id : uid
47784             },
47785             method: 'GET',
47786             success : function(req){
47787                //console.log(data);
47788                 var rdata = false;
47789                 var edata;
47790                 try  {
47791                    rdata = Roo.decode(req.responseText)
47792                 } catch (e) {
47793                     Roo.log("Invalid data from server..");
47794                     Roo.log(edata);
47795                     return;
47796                 }
47797                 if (!rdata || !rdata.success) {
47798                     Roo.log(rdata);
47799                     Roo.MessageBox.alert(Roo.encode(rdata));
47800                     return;
47801                 }
47802                 var data = rdata.data;
47803                 
47804                 if (this.uploadComplete) {
47805                    Roo.MessageBox.hide();
47806                    return;
47807                 }
47808                    
47809                 if (data){
47810                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47811                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47812                     );
47813                 }
47814                 this.uploadProgress.defer(2000,this);
47815             },
47816        
47817             failure: function(data) {
47818                 Roo.log('progress url failed ');
47819                 Roo.log(data);
47820             },
47821             scope : this
47822         });
47823            
47824     },
47825     
47826     
47827     run : function()
47828     {
47829         // run get Values on the form, so it syncs any secondary forms.
47830         this.form.getValues();
47831         
47832         var o = this.options;
47833         var method = this.getMethod();
47834         var isPost = method == 'POST';
47835         if(o.clientValidation === false || this.form.isValid()){
47836             
47837             if (this.form.progressUrl) {
47838                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47839                     (new Date() * 1) + '' + Math.random());
47840                     
47841             } 
47842             
47843             
47844             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47845                 form:this.form.el.dom,
47846                 url:this.getUrl(!isPost),
47847                 method: method,
47848                 params:isPost ? this.getParams() : null,
47849                 isUpload: this.form.fileUpload
47850             }));
47851             
47852             this.uploadProgress();
47853
47854         }else if (o.clientValidation !== false){ // client validation failed
47855             this.failureType = Roo.form.Action.CLIENT_INVALID;
47856             this.form.afterAction(this, false);
47857         }
47858     },
47859
47860     success : function(response)
47861     {
47862         this.uploadComplete= true;
47863         if (this.haveProgress) {
47864             Roo.MessageBox.hide();
47865         }
47866         
47867         
47868         var result = this.processResponse(response);
47869         if(result === true || result.success){
47870             this.form.afterAction(this, true);
47871             return;
47872         }
47873         if(result.errors){
47874             this.form.markInvalid(result.errors);
47875             this.failureType = Roo.form.Action.SERVER_INVALID;
47876         }
47877         this.form.afterAction(this, false);
47878     },
47879     failure : function(response)
47880     {
47881         this.uploadComplete= true;
47882         if (this.haveProgress) {
47883             Roo.MessageBox.hide();
47884         }
47885         
47886         this.response = response;
47887         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47888         this.form.afterAction(this, false);
47889     },
47890     
47891     handleResponse : function(response){
47892         if(this.form.errorReader){
47893             var rs = this.form.errorReader.read(response);
47894             var errors = [];
47895             if(rs.records){
47896                 for(var i = 0, len = rs.records.length; i < len; i++) {
47897                     var r = rs.records[i];
47898                     errors[i] = r.data;
47899                 }
47900             }
47901             if(errors.length < 1){
47902                 errors = null;
47903             }
47904             return {
47905                 success : rs.success,
47906                 errors : errors
47907             };
47908         }
47909         var ret = false;
47910         try {
47911             ret = Roo.decode(response.responseText);
47912         } catch (e) {
47913             ret = {
47914                 success: false,
47915                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47916                 errors : []
47917             };
47918         }
47919         return ret;
47920         
47921     }
47922 });
47923
47924
47925 Roo.form.Action.Load = function(form, options){
47926     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47927     this.reader = this.form.reader;
47928 };
47929
47930 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47931     type : 'load',
47932
47933     run : function(){
47934         
47935         Roo.Ajax.request(Roo.apply(
47936                 this.createCallback(), {
47937                     method:this.getMethod(),
47938                     url:this.getUrl(false),
47939                     params:this.getParams()
47940         }));
47941     },
47942
47943     success : function(response){
47944         
47945         var result = this.processResponse(response);
47946         if(result === true || !result.success || !result.data){
47947             this.failureType = Roo.form.Action.LOAD_FAILURE;
47948             this.form.afterAction(this, false);
47949             return;
47950         }
47951         this.form.clearInvalid();
47952         this.form.setValues(result.data);
47953         this.form.afterAction(this, true);
47954     },
47955
47956     handleResponse : function(response){
47957         if(this.form.reader){
47958             var rs = this.form.reader.read(response);
47959             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47960             return {
47961                 success : rs.success,
47962                 data : data
47963             };
47964         }
47965         return Roo.decode(response.responseText);
47966     }
47967 });
47968
47969 Roo.form.Action.ACTION_TYPES = {
47970     'load' : Roo.form.Action.Load,
47971     'submit' : Roo.form.Action.Submit
47972 };/*
47973  * Based on:
47974  * Ext JS Library 1.1.1
47975  * Copyright(c) 2006-2007, Ext JS, LLC.
47976  *
47977  * Originally Released Under LGPL - original licence link has changed is not relivant.
47978  *
47979  * Fork - LGPL
47980  * <script type="text/javascript">
47981  */
47982  
47983 /**
47984  * @class Roo.form.Layout
47985  * @extends Roo.Component
47986  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47987  * @constructor
47988  * @param {Object} config Configuration options
47989  */
47990 Roo.form.Layout = function(config){
47991     var xitems = [];
47992     if (config.items) {
47993         xitems = config.items;
47994         delete config.items;
47995     }
47996     Roo.form.Layout.superclass.constructor.call(this, config);
47997     this.stack = [];
47998     Roo.each(xitems, this.addxtype, this);
47999      
48000 };
48001
48002 Roo.extend(Roo.form.Layout, Roo.Component, {
48003     /**
48004      * @cfg {String/Object} autoCreate
48005      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48006      */
48007     /**
48008      * @cfg {String/Object/Function} style
48009      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48010      * a function which returns such a specification.
48011      */
48012     /**
48013      * @cfg {String} labelAlign
48014      * Valid values are "left," "top" and "right" (defaults to "left")
48015      */
48016     /**
48017      * @cfg {Number} labelWidth
48018      * Fixed width in pixels of all field labels (defaults to undefined)
48019      */
48020     /**
48021      * @cfg {Boolean} clear
48022      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48023      */
48024     clear : true,
48025     /**
48026      * @cfg {String} labelSeparator
48027      * The separator to use after field labels (defaults to ':')
48028      */
48029     labelSeparator : ':',
48030     /**
48031      * @cfg {Boolean} hideLabels
48032      * True to suppress the display of field labels in this layout (defaults to false)
48033      */
48034     hideLabels : false,
48035
48036     // private
48037     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48038     
48039     isLayout : true,
48040     
48041     // private
48042     onRender : function(ct, position){
48043         if(this.el){ // from markup
48044             this.el = Roo.get(this.el);
48045         }else {  // generate
48046             var cfg = this.getAutoCreate();
48047             this.el = ct.createChild(cfg, position);
48048         }
48049         if(this.style){
48050             this.el.applyStyles(this.style);
48051         }
48052         if(this.labelAlign){
48053             this.el.addClass('x-form-label-'+this.labelAlign);
48054         }
48055         if(this.hideLabels){
48056             this.labelStyle = "display:none";
48057             this.elementStyle = "padding-left:0;";
48058         }else{
48059             if(typeof this.labelWidth == 'number'){
48060                 this.labelStyle = "width:"+this.labelWidth+"px;";
48061                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48062             }
48063             if(this.labelAlign == 'top'){
48064                 this.labelStyle = "width:auto;";
48065                 this.elementStyle = "padding-left:0;";
48066             }
48067         }
48068         var stack = this.stack;
48069         var slen = stack.length;
48070         if(slen > 0){
48071             if(!this.fieldTpl){
48072                 var t = new Roo.Template(
48073                     '<div class="x-form-item {5}">',
48074                         '<label for="{0}" style="{2}">{1}{4}</label>',
48075                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48076                         '</div>',
48077                     '</div><div class="x-form-clear-left"></div>'
48078                 );
48079                 t.disableFormats = true;
48080                 t.compile();
48081                 Roo.form.Layout.prototype.fieldTpl = t;
48082             }
48083             for(var i = 0; i < slen; i++) {
48084                 if(stack[i].isFormField){
48085                     this.renderField(stack[i]);
48086                 }else{
48087                     this.renderComponent(stack[i]);
48088                 }
48089             }
48090         }
48091         if(this.clear){
48092             this.el.createChild({cls:'x-form-clear'});
48093         }
48094     },
48095
48096     // private
48097     renderField : function(f){
48098         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48099                f.id, //0
48100                f.fieldLabel, //1
48101                f.labelStyle||this.labelStyle||'', //2
48102                this.elementStyle||'', //3
48103                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48104                f.itemCls||this.itemCls||''  //5
48105        ], true).getPrevSibling());
48106     },
48107
48108     // private
48109     renderComponent : function(c){
48110         c.render(c.isLayout ? this.el : this.el.createChild());    
48111     },
48112     /**
48113      * Adds a object form elements (using the xtype property as the factory method.)
48114      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48115      * @param {Object} config 
48116      */
48117     addxtype : function(o)
48118     {
48119         // create the lement.
48120         o.form = this.form;
48121         var fe = Roo.factory(o, Roo.form);
48122         this.form.allItems.push(fe);
48123         this.stack.push(fe);
48124         
48125         if (fe.isFormField) {
48126             this.form.items.add(fe);
48127         }
48128          
48129         return fe;
48130     }
48131 });
48132
48133 /**
48134  * @class Roo.form.Column
48135  * @extends Roo.form.Layout
48136  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48137  * @constructor
48138  * @param {Object} config Configuration options
48139  */
48140 Roo.form.Column = function(config){
48141     Roo.form.Column.superclass.constructor.call(this, config);
48142 };
48143
48144 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48145     /**
48146      * @cfg {Number/String} width
48147      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48148      */
48149     /**
48150      * @cfg {String/Object} autoCreate
48151      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48152      */
48153
48154     // private
48155     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48156
48157     // private
48158     onRender : function(ct, position){
48159         Roo.form.Column.superclass.onRender.call(this, ct, position);
48160         if(this.width){
48161             this.el.setWidth(this.width);
48162         }
48163     }
48164 });
48165
48166
48167 /**
48168  * @class Roo.form.Row
48169  * @extends Roo.form.Layout
48170  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48171  * @constructor
48172  * @param {Object} config Configuration options
48173  */
48174
48175  
48176 Roo.form.Row = function(config){
48177     Roo.form.Row.superclass.constructor.call(this, config);
48178 };
48179  
48180 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48181       /**
48182      * @cfg {Number/String} width
48183      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48184      */
48185     /**
48186      * @cfg {Number/String} height
48187      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48188      */
48189     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48190     
48191     padWidth : 20,
48192     // private
48193     onRender : function(ct, position){
48194         //console.log('row render');
48195         if(!this.rowTpl){
48196             var t = new Roo.Template(
48197                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48198                     '<label for="{0}" style="{2}">{1}{4}</label>',
48199                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48200                     '</div>',
48201                 '</div>'
48202             );
48203             t.disableFormats = true;
48204             t.compile();
48205             Roo.form.Layout.prototype.rowTpl = t;
48206         }
48207         this.fieldTpl = this.rowTpl;
48208         
48209         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48210         var labelWidth = 100;
48211         
48212         if ((this.labelAlign != 'top')) {
48213             if (typeof this.labelWidth == 'number') {
48214                 labelWidth = this.labelWidth
48215             }
48216             this.padWidth =  20 + labelWidth;
48217             
48218         }
48219         
48220         Roo.form.Column.superclass.onRender.call(this, ct, position);
48221         if(this.width){
48222             this.el.setWidth(this.width);
48223         }
48224         if(this.height){
48225             this.el.setHeight(this.height);
48226         }
48227     },
48228     
48229     // private
48230     renderField : function(f){
48231         f.fieldEl = this.fieldTpl.append(this.el, [
48232                f.id, f.fieldLabel,
48233                f.labelStyle||this.labelStyle||'',
48234                this.elementStyle||'',
48235                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48236                f.itemCls||this.itemCls||'',
48237                f.width ? f.width + this.padWidth : 160 + this.padWidth
48238        ],true);
48239     }
48240 });
48241  
48242
48243 /**
48244  * @class Roo.form.FieldSet
48245  * @extends Roo.form.Layout
48246  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48247  * @constructor
48248  * @param {Object} config Configuration options
48249  */
48250 Roo.form.FieldSet = function(config){
48251     Roo.form.FieldSet.superclass.constructor.call(this, config);
48252 };
48253
48254 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48255     /**
48256      * @cfg {String} legend
48257      * The text to display as the legend for the FieldSet (defaults to '')
48258      */
48259     /**
48260      * @cfg {String/Object} autoCreate
48261      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48262      */
48263
48264     // private
48265     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48266
48267     // private
48268     onRender : function(ct, position){
48269         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48270         if(this.legend){
48271             this.setLegend(this.legend);
48272         }
48273     },
48274
48275     // private
48276     setLegend : function(text){
48277         if(this.rendered){
48278             this.el.child('legend').update(text);
48279         }
48280     }
48281 });/*
48282  * Based on:
48283  * Ext JS Library 1.1.1
48284  * Copyright(c) 2006-2007, Ext JS, LLC.
48285  *
48286  * Originally Released Under LGPL - original licence link has changed is not relivant.
48287  *
48288  * Fork - LGPL
48289  * <script type="text/javascript">
48290  */
48291 /**
48292  * @class Roo.form.VTypes
48293  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48294  * @singleton
48295  */
48296 Roo.form.VTypes = function(){
48297     // closure these in so they are only created once.
48298     var alpha = /^[a-zA-Z_]+$/;
48299     var alphanum = /^[a-zA-Z0-9_]+$/;
48300     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48301     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48302
48303     // All these messages and functions are configurable
48304     return {
48305         /**
48306          * The function used to validate email addresses
48307          * @param {String} value The email address
48308          */
48309         'email' : function(v){
48310             return email.test(v);
48311         },
48312         /**
48313          * The error text to display when the email validation function returns false
48314          * @type String
48315          */
48316         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48317         /**
48318          * The keystroke filter mask to be applied on email input
48319          * @type RegExp
48320          */
48321         'emailMask' : /[a-z0-9_\.\-@]/i,
48322
48323         /**
48324          * The function used to validate URLs
48325          * @param {String} value The URL
48326          */
48327         'url' : function(v){
48328             return url.test(v);
48329         },
48330         /**
48331          * The error text to display when the url validation function returns false
48332          * @type String
48333          */
48334         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48335         
48336         /**
48337          * The function used to validate alpha values
48338          * @param {String} value The value
48339          */
48340         'alpha' : function(v){
48341             return alpha.test(v);
48342         },
48343         /**
48344          * The error text to display when the alpha validation function returns false
48345          * @type String
48346          */
48347         'alphaText' : 'This field should only contain letters and _',
48348         /**
48349          * The keystroke filter mask to be applied on alpha input
48350          * @type RegExp
48351          */
48352         'alphaMask' : /[a-z_]/i,
48353
48354         /**
48355          * The function used to validate alphanumeric values
48356          * @param {String} value The value
48357          */
48358         'alphanum' : function(v){
48359             return alphanum.test(v);
48360         },
48361         /**
48362          * The error text to display when the alphanumeric validation function returns false
48363          * @type String
48364          */
48365         'alphanumText' : 'This field should only contain letters, numbers and _',
48366         /**
48367          * The keystroke filter mask to be applied on alphanumeric input
48368          * @type RegExp
48369          */
48370         'alphanumMask' : /[a-z0-9_]/i
48371     };
48372 }();//<script type="text/javascript">
48373
48374 /**
48375  * @class Roo.form.FCKeditor
48376  * @extends Roo.form.TextArea
48377  * Wrapper around the FCKEditor http://www.fckeditor.net
48378  * @constructor
48379  * Creates a new FCKeditor
48380  * @param {Object} config Configuration options
48381  */
48382 Roo.form.FCKeditor = function(config){
48383     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48384     this.addEvents({
48385          /**
48386          * @event editorinit
48387          * Fired when the editor is initialized - you can add extra handlers here..
48388          * @param {FCKeditor} this
48389          * @param {Object} the FCK object.
48390          */
48391         editorinit : true
48392     });
48393     
48394     
48395 };
48396 Roo.form.FCKeditor.editors = { };
48397 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48398 {
48399     //defaultAutoCreate : {
48400     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48401     //},
48402     // private
48403     /**
48404      * @cfg {Object} fck options - see fck manual for details.
48405      */
48406     fckconfig : false,
48407     
48408     /**
48409      * @cfg {Object} fck toolbar set (Basic or Default)
48410      */
48411     toolbarSet : 'Basic',
48412     /**
48413      * @cfg {Object} fck BasePath
48414      */ 
48415     basePath : '/fckeditor/',
48416     
48417     
48418     frame : false,
48419     
48420     value : '',
48421     
48422    
48423     onRender : function(ct, position)
48424     {
48425         if(!this.el){
48426             this.defaultAutoCreate = {
48427                 tag: "textarea",
48428                 style:"width:300px;height:60px;",
48429                 autocomplete: "new-password"
48430             };
48431         }
48432         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48433         /*
48434         if(this.grow){
48435             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48436             if(this.preventScrollbars){
48437                 this.el.setStyle("overflow", "hidden");
48438             }
48439             this.el.setHeight(this.growMin);
48440         }
48441         */
48442         //console.log('onrender' + this.getId() );
48443         Roo.form.FCKeditor.editors[this.getId()] = this;
48444          
48445
48446         this.replaceTextarea() ;
48447         
48448     },
48449     
48450     getEditor : function() {
48451         return this.fckEditor;
48452     },
48453     /**
48454      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48455      * @param {Mixed} value The value to set
48456      */
48457     
48458     
48459     setValue : function(value)
48460     {
48461         //console.log('setValue: ' + value);
48462         
48463         if(typeof(value) == 'undefined') { // not sure why this is happending...
48464             return;
48465         }
48466         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48467         
48468         //if(!this.el || !this.getEditor()) {
48469         //    this.value = value;
48470             //this.setValue.defer(100,this,[value]);    
48471         //    return;
48472         //} 
48473         
48474         if(!this.getEditor()) {
48475             return;
48476         }
48477         
48478         this.getEditor().SetData(value);
48479         
48480         //
48481
48482     },
48483
48484     /**
48485      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48486      * @return {Mixed} value The field value
48487      */
48488     getValue : function()
48489     {
48490         
48491         if (this.frame && this.frame.dom.style.display == 'none') {
48492             return Roo.form.FCKeditor.superclass.getValue.call(this);
48493         }
48494         
48495         if(!this.el || !this.getEditor()) {
48496            
48497            // this.getValue.defer(100,this); 
48498             return this.value;
48499         }
48500        
48501         
48502         var value=this.getEditor().GetData();
48503         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48504         return Roo.form.FCKeditor.superclass.getValue.call(this);
48505         
48506
48507     },
48508
48509     /**
48510      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48511      * @return {Mixed} value The field value
48512      */
48513     getRawValue : function()
48514     {
48515         if (this.frame && this.frame.dom.style.display == 'none') {
48516             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48517         }
48518         
48519         if(!this.el || !this.getEditor()) {
48520             //this.getRawValue.defer(100,this); 
48521             return this.value;
48522             return;
48523         }
48524         
48525         
48526         
48527         var value=this.getEditor().GetData();
48528         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48529         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48530          
48531     },
48532     
48533     setSize : function(w,h) {
48534         
48535         
48536         
48537         //if (this.frame && this.frame.dom.style.display == 'none') {
48538         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48539         //    return;
48540         //}
48541         //if(!this.el || !this.getEditor()) {
48542         //    this.setSize.defer(100,this, [w,h]); 
48543         //    return;
48544         //}
48545         
48546         
48547         
48548         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48549         
48550         this.frame.dom.setAttribute('width', w);
48551         this.frame.dom.setAttribute('height', h);
48552         this.frame.setSize(w,h);
48553         
48554     },
48555     
48556     toggleSourceEdit : function(value) {
48557         
48558       
48559          
48560         this.el.dom.style.display = value ? '' : 'none';
48561         this.frame.dom.style.display = value ?  'none' : '';
48562         
48563     },
48564     
48565     
48566     focus: function(tag)
48567     {
48568         if (this.frame.dom.style.display == 'none') {
48569             return Roo.form.FCKeditor.superclass.focus.call(this);
48570         }
48571         if(!this.el || !this.getEditor()) {
48572             this.focus.defer(100,this, [tag]); 
48573             return;
48574         }
48575         
48576         
48577         
48578         
48579         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48580         this.getEditor().Focus();
48581         if (tgs.length) {
48582             if (!this.getEditor().Selection.GetSelection()) {
48583                 this.focus.defer(100,this, [tag]); 
48584                 return;
48585             }
48586             
48587             
48588             var r = this.getEditor().EditorDocument.createRange();
48589             r.setStart(tgs[0],0);
48590             r.setEnd(tgs[0],0);
48591             this.getEditor().Selection.GetSelection().removeAllRanges();
48592             this.getEditor().Selection.GetSelection().addRange(r);
48593             this.getEditor().Focus();
48594         }
48595         
48596     },
48597     
48598     
48599     
48600     replaceTextarea : function()
48601     {
48602         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48603             return ;
48604         }
48605         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48606         //{
48607             // We must check the elements firstly using the Id and then the name.
48608         var oTextarea = document.getElementById( this.getId() );
48609         
48610         var colElementsByName = document.getElementsByName( this.getId() ) ;
48611          
48612         oTextarea.style.display = 'none' ;
48613
48614         if ( oTextarea.tabIndex ) {            
48615             this.TabIndex = oTextarea.tabIndex ;
48616         }
48617         
48618         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48619         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48620         this.frame = Roo.get(this.getId() + '___Frame')
48621     },
48622     
48623     _getConfigHtml : function()
48624     {
48625         var sConfig = '' ;
48626
48627         for ( var o in this.fckconfig ) {
48628             sConfig += sConfig.length > 0  ? '&amp;' : '';
48629             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48630         }
48631
48632         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48633     },
48634     
48635     
48636     _getIFrameHtml : function()
48637     {
48638         var sFile = 'fckeditor.html' ;
48639         /* no idea what this is about..
48640         try
48641         {
48642             if ( (/fcksource=true/i).test( window.top.location.search ) )
48643                 sFile = 'fckeditor.original.html' ;
48644         }
48645         catch (e) { 
48646         */
48647
48648         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48649         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48650         
48651         
48652         var html = '<iframe id="' + this.getId() +
48653             '___Frame" src="' + sLink +
48654             '" width="' + this.width +
48655             '" height="' + this.height + '"' +
48656             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48657             ' frameborder="0" scrolling="no"></iframe>' ;
48658
48659         return html ;
48660     },
48661     
48662     _insertHtmlBefore : function( html, element )
48663     {
48664         if ( element.insertAdjacentHTML )       {
48665             // IE
48666             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48667         } else { // Gecko
48668             var oRange = document.createRange() ;
48669             oRange.setStartBefore( element ) ;
48670             var oFragment = oRange.createContextualFragment( html );
48671             element.parentNode.insertBefore( oFragment, element ) ;
48672         }
48673     }
48674     
48675     
48676   
48677     
48678     
48679     
48680     
48681
48682 });
48683
48684 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48685
48686 function FCKeditor_OnComplete(editorInstance){
48687     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48688     f.fckEditor = editorInstance;
48689     //console.log("loaded");
48690     f.fireEvent('editorinit', f, editorInstance);
48691
48692   
48693
48694  
48695
48696
48697
48698
48699
48700
48701
48702
48703
48704
48705
48706
48707
48708
48709
48710 //<script type="text/javascript">
48711 /**
48712  * @class Roo.form.GridField
48713  * @extends Roo.form.Field
48714  * Embed a grid (or editable grid into a form)
48715  * STATUS ALPHA
48716  * 
48717  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48718  * it needs 
48719  * xgrid.store = Roo.data.Store
48720  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48721  * xgrid.store.reader = Roo.data.JsonReader 
48722  * 
48723  * 
48724  * @constructor
48725  * Creates a new GridField
48726  * @param {Object} config Configuration options
48727  */
48728 Roo.form.GridField = function(config){
48729     Roo.form.GridField.superclass.constructor.call(this, config);
48730      
48731 };
48732
48733 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48734     /**
48735      * @cfg {Number} width  - used to restrict width of grid..
48736      */
48737     width : 100,
48738     /**
48739      * @cfg {Number} height - used to restrict height of grid..
48740      */
48741     height : 50,
48742      /**
48743      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48744          * 
48745          *}
48746      */
48747     xgrid : false, 
48748     /**
48749      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48750      * {tag: "input", type: "checkbox", autocomplete: "off"})
48751      */
48752    // defaultAutoCreate : { tag: 'div' },
48753     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48754     /**
48755      * @cfg {String} addTitle Text to include for adding a title.
48756      */
48757     addTitle : false,
48758     //
48759     onResize : function(){
48760         Roo.form.Field.superclass.onResize.apply(this, arguments);
48761     },
48762
48763     initEvents : function(){
48764         // Roo.form.Checkbox.superclass.initEvents.call(this);
48765         // has no events...
48766        
48767     },
48768
48769
48770     getResizeEl : function(){
48771         return this.wrap;
48772     },
48773
48774     getPositionEl : function(){
48775         return this.wrap;
48776     },
48777
48778     // private
48779     onRender : function(ct, position){
48780         
48781         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48782         var style = this.style;
48783         delete this.style;
48784         
48785         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48786         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48787         this.viewEl = this.wrap.createChild({ tag: 'div' });
48788         if (style) {
48789             this.viewEl.applyStyles(style);
48790         }
48791         if (this.width) {
48792             this.viewEl.setWidth(this.width);
48793         }
48794         if (this.height) {
48795             this.viewEl.setHeight(this.height);
48796         }
48797         //if(this.inputValue !== undefined){
48798         //this.setValue(this.value);
48799         
48800         
48801         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48802         
48803         
48804         this.grid.render();
48805         this.grid.getDataSource().on('remove', this.refreshValue, this);
48806         this.grid.getDataSource().on('update', this.refreshValue, this);
48807         this.grid.on('afteredit', this.refreshValue, this);
48808  
48809     },
48810      
48811     
48812     /**
48813      * Sets the value of the item. 
48814      * @param {String} either an object  or a string..
48815      */
48816     setValue : function(v){
48817         //this.value = v;
48818         v = v || []; // empty set..
48819         // this does not seem smart - it really only affects memoryproxy grids..
48820         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48821             var ds = this.grid.getDataSource();
48822             // assumes a json reader..
48823             var data = {}
48824             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48825             ds.loadData( data);
48826         }
48827         // clear selection so it does not get stale.
48828         if (this.grid.sm) { 
48829             this.grid.sm.clearSelections();
48830         }
48831         
48832         Roo.form.GridField.superclass.setValue.call(this, v);
48833         this.refreshValue();
48834         // should load data in the grid really....
48835     },
48836     
48837     // private
48838     refreshValue: function() {
48839          var val = [];
48840         this.grid.getDataSource().each(function(r) {
48841             val.push(r.data);
48842         });
48843         this.el.dom.value = Roo.encode(val);
48844     }
48845     
48846      
48847     
48848     
48849 });/*
48850  * Based on:
48851  * Ext JS Library 1.1.1
48852  * Copyright(c) 2006-2007, Ext JS, LLC.
48853  *
48854  * Originally Released Under LGPL - original licence link has changed is not relivant.
48855  *
48856  * Fork - LGPL
48857  * <script type="text/javascript">
48858  */
48859 /**
48860  * @class Roo.form.DisplayField
48861  * @extends Roo.form.Field
48862  * A generic Field to display non-editable data.
48863  * @cfg {Boolean} closable (true|false) default false
48864  * @constructor
48865  * Creates a new Display Field item.
48866  * @param {Object} config Configuration options
48867  */
48868 Roo.form.DisplayField = function(config){
48869     Roo.form.DisplayField.superclass.constructor.call(this, config);
48870     
48871     this.addEvents({
48872         /**
48873          * @event close
48874          * Fires after the click the close btn
48875              * @param {Roo.form.DisplayField} this
48876              */
48877         close : true
48878     });
48879 };
48880
48881 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48882     inputType:      'hidden',
48883     allowBlank:     true,
48884     readOnly:         true,
48885     
48886  
48887     /**
48888      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48889      */
48890     focusClass : undefined,
48891     /**
48892      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48893      */
48894     fieldClass: 'x-form-field',
48895     
48896      /**
48897      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48898      */
48899     valueRenderer: undefined,
48900     
48901     width: 100,
48902     /**
48903      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48904      * {tag: "input", type: "checkbox", autocomplete: "off"})
48905      */
48906      
48907  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48908  
48909     closable : false,
48910     
48911     onResize : function(){
48912         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48913         
48914     },
48915
48916     initEvents : function(){
48917         // Roo.form.Checkbox.superclass.initEvents.call(this);
48918         // has no events...
48919         
48920         if(this.closable){
48921             this.closeEl.on('click', this.onClose, this);
48922         }
48923        
48924     },
48925
48926
48927     getResizeEl : function(){
48928         return this.wrap;
48929     },
48930
48931     getPositionEl : function(){
48932         return this.wrap;
48933     },
48934
48935     // private
48936     onRender : function(ct, position){
48937         
48938         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48939         //if(this.inputValue !== undefined){
48940         this.wrap = this.el.wrap();
48941         
48942         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48943         
48944         if(this.closable){
48945             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48946         }
48947         
48948         if (this.bodyStyle) {
48949             this.viewEl.applyStyles(this.bodyStyle);
48950         }
48951         //this.viewEl.setStyle('padding', '2px');
48952         
48953         this.setValue(this.value);
48954         
48955     },
48956 /*
48957     // private
48958     initValue : Roo.emptyFn,
48959
48960   */
48961
48962         // private
48963     onClick : function(){
48964         
48965     },
48966
48967     /**
48968      * Sets the checked state of the checkbox.
48969      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48970      */
48971     setValue : function(v){
48972         this.value = v;
48973         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48974         // this might be called before we have a dom element..
48975         if (!this.viewEl) {
48976             return;
48977         }
48978         this.viewEl.dom.innerHTML = html;
48979         Roo.form.DisplayField.superclass.setValue.call(this, v);
48980
48981     },
48982     
48983     onClose : function(e)
48984     {
48985         e.preventDefault();
48986         
48987         this.fireEvent('close', this);
48988     }
48989 });/*
48990  * 
48991  * Licence- LGPL
48992  * 
48993  */
48994
48995 /**
48996  * @class Roo.form.DayPicker
48997  * @extends Roo.form.Field
48998  * A Day picker show [M] [T] [W] ....
48999  * @constructor
49000  * Creates a new Day Picker
49001  * @param {Object} config Configuration options
49002  */
49003 Roo.form.DayPicker= function(config){
49004     Roo.form.DayPicker.superclass.constructor.call(this, config);
49005      
49006 };
49007
49008 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49009     /**
49010      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49011      */
49012     focusClass : undefined,
49013     /**
49014      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49015      */
49016     fieldClass: "x-form-field",
49017    
49018     /**
49019      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49020      * {tag: "input", type: "checkbox", autocomplete: "off"})
49021      */
49022     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49023     
49024    
49025     actionMode : 'viewEl', 
49026     //
49027     // private
49028  
49029     inputType : 'hidden',
49030     
49031      
49032     inputElement: false, // real input element?
49033     basedOn: false, // ????
49034     
49035     isFormField: true, // not sure where this is needed!!!!
49036
49037     onResize : function(){
49038         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49039         if(!this.boxLabel){
49040             this.el.alignTo(this.wrap, 'c-c');
49041         }
49042     },
49043
49044     initEvents : function(){
49045         Roo.form.Checkbox.superclass.initEvents.call(this);
49046         this.el.on("click", this.onClick,  this);
49047         this.el.on("change", this.onClick,  this);
49048     },
49049
49050
49051     getResizeEl : function(){
49052         return this.wrap;
49053     },
49054
49055     getPositionEl : function(){
49056         return this.wrap;
49057     },
49058
49059     
49060     // private
49061     onRender : function(ct, position){
49062         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49063        
49064         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49065         
49066         var r1 = '<table><tr>';
49067         var r2 = '<tr class="x-form-daypick-icons">';
49068         for (var i=0; i < 7; i++) {
49069             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49070             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49071         }
49072         
49073         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49074         viewEl.select('img').on('click', this.onClick, this);
49075         this.viewEl = viewEl;   
49076         
49077         
49078         // this will not work on Chrome!!!
49079         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49080         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49081         
49082         
49083           
49084
49085     },
49086
49087     // private
49088     initValue : Roo.emptyFn,
49089
49090     /**
49091      * Returns the checked state of the checkbox.
49092      * @return {Boolean} True if checked, else false
49093      */
49094     getValue : function(){
49095         return this.el.dom.value;
49096         
49097     },
49098
49099         // private
49100     onClick : function(e){ 
49101         //this.setChecked(!this.checked);
49102         Roo.get(e.target).toggleClass('x-menu-item-checked');
49103         this.refreshValue();
49104         //if(this.el.dom.checked != this.checked){
49105         //    this.setValue(this.el.dom.checked);
49106        // }
49107     },
49108     
49109     // private
49110     refreshValue : function()
49111     {
49112         var val = '';
49113         this.viewEl.select('img',true).each(function(e,i,n)  {
49114             val += e.is(".x-menu-item-checked") ? String(n) : '';
49115         });
49116         this.setValue(val, true);
49117     },
49118
49119     /**
49120      * Sets the checked state of the checkbox.
49121      * On is always based on a string comparison between inputValue and the param.
49122      * @param {Boolean/String} value - the value to set 
49123      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49124      */
49125     setValue : function(v,suppressEvent){
49126         if (!this.el.dom) {
49127             return;
49128         }
49129         var old = this.el.dom.value ;
49130         this.el.dom.value = v;
49131         if (suppressEvent) {
49132             return ;
49133         }
49134          
49135         // update display..
49136         this.viewEl.select('img',true).each(function(e,i,n)  {
49137             
49138             var on = e.is(".x-menu-item-checked");
49139             var newv = v.indexOf(String(n)) > -1;
49140             if (on != newv) {
49141                 e.toggleClass('x-menu-item-checked');
49142             }
49143             
49144         });
49145         
49146         
49147         this.fireEvent('change', this, v, old);
49148         
49149         
49150     },
49151    
49152     // handle setting of hidden value by some other method!!?!?
49153     setFromHidden: function()
49154     {
49155         if(!this.el){
49156             return;
49157         }
49158         //console.log("SET FROM HIDDEN");
49159         //alert('setFrom hidden');
49160         this.setValue(this.el.dom.value);
49161     },
49162     
49163     onDestroy : function()
49164     {
49165         if(this.viewEl){
49166             Roo.get(this.viewEl).remove();
49167         }
49168          
49169         Roo.form.DayPicker.superclass.onDestroy.call(this);
49170     }
49171
49172 });/*
49173  * RooJS Library 1.1.1
49174  * Copyright(c) 2008-2011  Alan Knowles
49175  *
49176  * License - LGPL
49177  */
49178  
49179
49180 /**
49181  * @class Roo.form.ComboCheck
49182  * @extends Roo.form.ComboBox
49183  * A combobox for multiple select items.
49184  *
49185  * FIXME - could do with a reset button..
49186  * 
49187  * @constructor
49188  * Create a new ComboCheck
49189  * @param {Object} config Configuration options
49190  */
49191 Roo.form.ComboCheck = function(config){
49192     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49193     // should verify some data...
49194     // like
49195     // hiddenName = required..
49196     // displayField = required
49197     // valudField == required
49198     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49199     var _t = this;
49200     Roo.each(req, function(e) {
49201         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49202             throw "Roo.form.ComboCheck : missing value for: " + e;
49203         }
49204     });
49205     
49206     
49207 };
49208
49209 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49210      
49211      
49212     editable : false,
49213      
49214     selectedClass: 'x-menu-item-checked', 
49215     
49216     // private
49217     onRender : function(ct, position){
49218         var _t = this;
49219         
49220         
49221         
49222         if(!this.tpl){
49223             var cls = 'x-combo-list';
49224
49225             
49226             this.tpl =  new Roo.Template({
49227                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49228                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49229                    '<span>{' + this.displayField + '}</span>' +
49230                     '</div>' 
49231                 
49232             });
49233         }
49234  
49235         
49236         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49237         this.view.singleSelect = false;
49238         this.view.multiSelect = true;
49239         this.view.toggleSelect = true;
49240         this.pageTb.add(new Roo.Toolbar.Fill(), {
49241             
49242             text: 'Done',
49243             handler: function()
49244             {
49245                 _t.collapse();
49246             }
49247         });
49248     },
49249     
49250     onViewOver : function(e, t){
49251         // do nothing...
49252         return;
49253         
49254     },
49255     
49256     onViewClick : function(doFocus,index){
49257         return;
49258         
49259     },
49260     select: function () {
49261         //Roo.log("SELECT CALLED");
49262     },
49263      
49264     selectByValue : function(xv, scrollIntoView){
49265         var ar = this.getValueArray();
49266         var sels = [];
49267         
49268         Roo.each(ar, function(v) {
49269             if(v === undefined || v === null){
49270                 return;
49271             }
49272             var r = this.findRecord(this.valueField, v);
49273             if(r){
49274                 sels.push(this.store.indexOf(r))
49275                 
49276             }
49277         },this);
49278         this.view.select(sels);
49279         return false;
49280     },
49281     
49282     
49283     
49284     onSelect : function(record, index){
49285        // Roo.log("onselect Called");
49286        // this is only called by the clear button now..
49287         this.view.clearSelections();
49288         this.setValue('[]');
49289         if (this.value != this.valueBefore) {
49290             this.fireEvent('change', this, this.value, this.valueBefore);
49291             this.valueBefore = this.value;
49292         }
49293     },
49294     getValueArray : function()
49295     {
49296         var ar = [] ;
49297         
49298         try {
49299             //Roo.log(this.value);
49300             if (typeof(this.value) == 'undefined') {
49301                 return [];
49302             }
49303             var ar = Roo.decode(this.value);
49304             return  ar instanceof Array ? ar : []; //?? valid?
49305             
49306         } catch(e) {
49307             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49308             return [];
49309         }
49310          
49311     },
49312     expand : function ()
49313     {
49314         
49315         Roo.form.ComboCheck.superclass.expand.call(this);
49316         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49317         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49318         
49319
49320     },
49321     
49322     collapse : function(){
49323         Roo.form.ComboCheck.superclass.collapse.call(this);
49324         var sl = this.view.getSelectedIndexes();
49325         var st = this.store;
49326         var nv = [];
49327         var tv = [];
49328         var r;
49329         Roo.each(sl, function(i) {
49330             r = st.getAt(i);
49331             nv.push(r.get(this.valueField));
49332         },this);
49333         this.setValue(Roo.encode(nv));
49334         if (this.value != this.valueBefore) {
49335
49336             this.fireEvent('change', this, this.value, this.valueBefore);
49337             this.valueBefore = this.value;
49338         }
49339         
49340     },
49341     
49342     setValue : function(v){
49343         // Roo.log(v);
49344         this.value = v;
49345         
49346         var vals = this.getValueArray();
49347         var tv = [];
49348         Roo.each(vals, function(k) {
49349             var r = this.findRecord(this.valueField, k);
49350             if(r){
49351                 tv.push(r.data[this.displayField]);
49352             }else if(this.valueNotFoundText !== undefined){
49353                 tv.push( this.valueNotFoundText );
49354             }
49355         },this);
49356        // Roo.log(tv);
49357         
49358         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49359         this.hiddenField.value = v;
49360         this.value = v;
49361     }
49362     
49363 });/*
49364  * Based on:
49365  * Ext JS Library 1.1.1
49366  * Copyright(c) 2006-2007, Ext JS, LLC.
49367  *
49368  * Originally Released Under LGPL - original licence link has changed is not relivant.
49369  *
49370  * Fork - LGPL
49371  * <script type="text/javascript">
49372  */
49373  
49374 /**
49375  * @class Roo.form.Signature
49376  * @extends Roo.form.Field
49377  * Signature field.  
49378  * @constructor
49379  * 
49380  * @param {Object} config Configuration options
49381  */
49382
49383 Roo.form.Signature = function(config){
49384     Roo.form.Signature.superclass.constructor.call(this, config);
49385     
49386     this.addEvents({// not in used??
49387          /**
49388          * @event confirm
49389          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49390              * @param {Roo.form.Signature} combo This combo box
49391              */
49392         'confirm' : true,
49393         /**
49394          * @event reset
49395          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49396              * @param {Roo.form.ComboBox} combo This combo box
49397              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49398              */
49399         'reset' : true
49400     });
49401 };
49402
49403 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49404     /**
49405      * @cfg {Object} labels Label to use when rendering a form.
49406      * defaults to 
49407      * labels : { 
49408      *      clear : "Clear",
49409      *      confirm : "Confirm"
49410      *  }
49411      */
49412     labels : { 
49413         clear : "Clear",
49414         confirm : "Confirm"
49415     },
49416     /**
49417      * @cfg {Number} width The signature panel width (defaults to 300)
49418      */
49419     width: 300,
49420     /**
49421      * @cfg {Number} height The signature panel height (defaults to 100)
49422      */
49423     height : 100,
49424     /**
49425      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49426      */
49427     allowBlank : false,
49428     
49429     //private
49430     // {Object} signPanel The signature SVG panel element (defaults to {})
49431     signPanel : {},
49432     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49433     isMouseDown : false,
49434     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49435     isConfirmed : false,
49436     // {String} signatureTmp SVG mapping string (defaults to empty string)
49437     signatureTmp : '',
49438     
49439     
49440     defaultAutoCreate : { // modified by initCompnoent..
49441         tag: "input",
49442         type:"hidden"
49443     },
49444
49445     // private
49446     onRender : function(ct, position){
49447         
49448         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49449         
49450         this.wrap = this.el.wrap({
49451             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49452         });
49453         
49454         this.createToolbar(this);
49455         this.signPanel = this.wrap.createChild({
49456                 tag: 'div',
49457                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49458             }, this.el
49459         );
49460             
49461         this.svgID = Roo.id();
49462         this.svgEl = this.signPanel.createChild({
49463               xmlns : 'http://www.w3.org/2000/svg',
49464               tag : 'svg',
49465               id : this.svgID + "-svg",
49466               width: this.width,
49467               height: this.height,
49468               viewBox: '0 0 '+this.width+' '+this.height,
49469               cn : [
49470                 {
49471                     tag: "rect",
49472                     id: this.svgID + "-svg-r",
49473                     width: this.width,
49474                     height: this.height,
49475                     fill: "#ffa"
49476                 },
49477                 {
49478                     tag: "line",
49479                     id: this.svgID + "-svg-l",
49480                     x1: "0", // start
49481                     y1: (this.height*0.8), // start set the line in 80% of height
49482                     x2: this.width, // end
49483                     y2: (this.height*0.8), // end set the line in 80% of height
49484                     'stroke': "#666",
49485                     'stroke-width': "1",
49486                     'stroke-dasharray': "3",
49487                     'shape-rendering': "crispEdges",
49488                     'pointer-events': "none"
49489                 },
49490                 {
49491                     tag: "path",
49492                     id: this.svgID + "-svg-p",
49493                     'stroke': "navy",
49494                     'stroke-width': "3",
49495                     'fill': "none",
49496                     'pointer-events': 'none'
49497                 }
49498               ]
49499         });
49500         this.createSVG();
49501         this.svgBox = this.svgEl.dom.getScreenCTM();
49502     },
49503     createSVG : function(){ 
49504         var svg = this.signPanel;
49505         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49506         var t = this;
49507
49508         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49509         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49510         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49511         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49512         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49513         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49514         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49515         
49516     },
49517     isTouchEvent : function(e){
49518         return e.type.match(/^touch/);
49519     },
49520     getCoords : function (e) {
49521         var pt    = this.svgEl.dom.createSVGPoint();
49522         pt.x = e.clientX; 
49523         pt.y = e.clientY;
49524         if (this.isTouchEvent(e)) {
49525             pt.x =  e.targetTouches[0].clientX;
49526             pt.y = e.targetTouches[0].clientY;
49527         }
49528         var a = this.svgEl.dom.getScreenCTM();
49529         var b = a.inverse();
49530         var mx = pt.matrixTransform(b);
49531         return mx.x + ',' + mx.y;
49532     },
49533     //mouse event headler 
49534     down : function (e) {
49535         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49536         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49537         
49538         this.isMouseDown = true;
49539         
49540         e.preventDefault();
49541     },
49542     move : function (e) {
49543         if (this.isMouseDown) {
49544             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49545             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49546         }
49547         
49548         e.preventDefault();
49549     },
49550     up : function (e) {
49551         this.isMouseDown = false;
49552         var sp = this.signatureTmp.split(' ');
49553         
49554         if(sp.length > 1){
49555             if(!sp[sp.length-2].match(/^L/)){
49556                 sp.pop();
49557                 sp.pop();
49558                 sp.push("");
49559                 this.signatureTmp = sp.join(" ");
49560             }
49561         }
49562         if(this.getValue() != this.signatureTmp){
49563             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49564             this.isConfirmed = false;
49565         }
49566         e.preventDefault();
49567     },
49568     
49569     /**
49570      * Protected method that will not generally be called directly. It
49571      * is called when the editor creates its toolbar. Override this method if you need to
49572      * add custom toolbar buttons.
49573      * @param {HtmlEditor} editor
49574      */
49575     createToolbar : function(editor){
49576          function btn(id, toggle, handler){
49577             var xid = fid + '-'+ id ;
49578             return {
49579                 id : xid,
49580                 cmd : id,
49581                 cls : 'x-btn-icon x-edit-'+id,
49582                 enableToggle:toggle !== false,
49583                 scope: editor, // was editor...
49584                 handler:handler||editor.relayBtnCmd,
49585                 clickEvent:'mousedown',
49586                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49587                 tabIndex:-1
49588             };
49589         }
49590         
49591         
49592         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49593         this.tb = tb;
49594         this.tb.add(
49595            {
49596                 cls : ' x-signature-btn x-signature-'+id,
49597                 scope: editor, // was editor...
49598                 handler: this.reset,
49599                 clickEvent:'mousedown',
49600                 text: this.labels.clear
49601             },
49602             {
49603                  xtype : 'Fill',
49604                  xns: Roo.Toolbar
49605             }, 
49606             {
49607                 cls : '  x-signature-btn x-signature-'+id,
49608                 scope: editor, // was editor...
49609                 handler: this.confirmHandler,
49610                 clickEvent:'mousedown',
49611                 text: this.labels.confirm
49612             }
49613         );
49614     
49615     },
49616     //public
49617     /**
49618      * when user is clicked confirm then show this image.....
49619      * 
49620      * @return {String} Image Data URI
49621      */
49622     getImageDataURI : function(){
49623         var svg = this.svgEl.dom.parentNode.innerHTML;
49624         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49625         return src; 
49626     },
49627     /**
49628      * 
49629      * @return {Boolean} this.isConfirmed
49630      */
49631     getConfirmed : function(){
49632         return this.isConfirmed;
49633     },
49634     /**
49635      * 
49636      * @return {Number} this.width
49637      */
49638     getWidth : function(){
49639         return this.width;
49640     },
49641     /**
49642      * 
49643      * @return {Number} this.height
49644      */
49645     getHeight : function(){
49646         return this.height;
49647     },
49648     // private
49649     getSignature : function(){
49650         return this.signatureTmp;
49651     },
49652     // private
49653     reset : function(){
49654         this.signatureTmp = '';
49655         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49656         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49657         this.isConfirmed = false;
49658         Roo.form.Signature.superclass.reset.call(this);
49659     },
49660     setSignature : function(s){
49661         this.signatureTmp = s;
49662         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49663         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49664         this.setValue(s);
49665         this.isConfirmed = false;
49666         Roo.form.Signature.superclass.reset.call(this);
49667     }, 
49668     test : function(){
49669 //        Roo.log(this.signPanel.dom.contentWindow.up())
49670     },
49671     //private
49672     setConfirmed : function(){
49673         
49674         
49675         
49676 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49677     },
49678     // private
49679     confirmHandler : function(){
49680         if(!this.getSignature()){
49681             return;
49682         }
49683         
49684         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49685         this.setValue(this.getSignature());
49686         this.isConfirmed = true;
49687         
49688         this.fireEvent('confirm', this);
49689     },
49690     // private
49691     // Subclasses should provide the validation implementation by overriding this
49692     validateValue : function(value){
49693         if(this.allowBlank){
49694             return true;
49695         }
49696         
49697         if(this.isConfirmed){
49698             return true;
49699         }
49700         return false;
49701     }
49702 });/*
49703  * Based on:
49704  * Ext JS Library 1.1.1
49705  * Copyright(c) 2006-2007, Ext JS, LLC.
49706  *
49707  * Originally Released Under LGPL - original licence link has changed is not relivant.
49708  *
49709  * Fork - LGPL
49710  * <script type="text/javascript">
49711  */
49712  
49713
49714 /**
49715  * @class Roo.form.ComboBox
49716  * @extends Roo.form.TriggerField
49717  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49718  * @constructor
49719  * Create a new ComboBox.
49720  * @param {Object} config Configuration options
49721  */
49722 Roo.form.Select = function(config){
49723     Roo.form.Select.superclass.constructor.call(this, config);
49724      
49725 };
49726
49727 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49728     /**
49729      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49730      */
49731     /**
49732      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49733      * rendering into an Roo.Editor, defaults to false)
49734      */
49735     /**
49736      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49737      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49738      */
49739     /**
49740      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49741      */
49742     /**
49743      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49744      * the dropdown list (defaults to undefined, with no header element)
49745      */
49746
49747      /**
49748      * @cfg {String/Roo.Template} tpl The template to use to render the output
49749      */
49750      
49751     // private
49752     defaultAutoCreate : {tag: "select"  },
49753     /**
49754      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49755      */
49756     listWidth: undefined,
49757     /**
49758      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49759      * mode = 'remote' or 'text' if mode = 'local')
49760      */
49761     displayField: undefined,
49762     /**
49763      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49764      * mode = 'remote' or 'value' if mode = 'local'). 
49765      * Note: use of a valueField requires the user make a selection
49766      * in order for a value to be mapped.
49767      */
49768     valueField: undefined,
49769     
49770     
49771     /**
49772      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49773      * field's data value (defaults to the underlying DOM element's name)
49774      */
49775     hiddenName: undefined,
49776     /**
49777      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49778      */
49779     listClass: '',
49780     /**
49781      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49782      */
49783     selectedClass: 'x-combo-selected',
49784     /**
49785      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49786      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49787      * which displays a downward arrow icon).
49788      */
49789     triggerClass : 'x-form-arrow-trigger',
49790     /**
49791      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49792      */
49793     shadow:'sides',
49794     /**
49795      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49796      * anchor positions (defaults to 'tl-bl')
49797      */
49798     listAlign: 'tl-bl?',
49799     /**
49800      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49801      */
49802     maxHeight: 300,
49803     /**
49804      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49805      * query specified by the allQuery config option (defaults to 'query')
49806      */
49807     triggerAction: 'query',
49808     /**
49809      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49810      * (defaults to 4, does not apply if editable = false)
49811      */
49812     minChars : 4,
49813     /**
49814      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49815      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49816      */
49817     typeAhead: false,
49818     /**
49819      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49820      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49821      */
49822     queryDelay: 500,
49823     /**
49824      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49825      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49826      */
49827     pageSize: 0,
49828     /**
49829      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49830      * when editable = true (defaults to false)
49831      */
49832     selectOnFocus:false,
49833     /**
49834      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49835      */
49836     queryParam: 'query',
49837     /**
49838      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49839      * when mode = 'remote' (defaults to 'Loading...')
49840      */
49841     loadingText: 'Loading...',
49842     /**
49843      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49844      */
49845     resizable: false,
49846     /**
49847      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49848      */
49849     handleHeight : 8,
49850     /**
49851      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49852      * traditional select (defaults to true)
49853      */
49854     editable: true,
49855     /**
49856      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49857      */
49858     allQuery: '',
49859     /**
49860      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49861      */
49862     mode: 'remote',
49863     /**
49864      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49865      * listWidth has a higher value)
49866      */
49867     minListWidth : 70,
49868     /**
49869      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49870      * allow the user to set arbitrary text into the field (defaults to false)
49871      */
49872     forceSelection:false,
49873     /**
49874      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49875      * if typeAhead = true (defaults to 250)
49876      */
49877     typeAheadDelay : 250,
49878     /**
49879      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49880      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49881      */
49882     valueNotFoundText : undefined,
49883     
49884     /**
49885      * @cfg {String} defaultValue The value displayed after loading the store.
49886      */
49887     defaultValue: '',
49888     
49889     /**
49890      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49891      */
49892     blockFocus : false,
49893     
49894     /**
49895      * @cfg {Boolean} disableClear Disable showing of clear button.
49896      */
49897     disableClear : false,
49898     /**
49899      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49900      */
49901     alwaysQuery : false,
49902     
49903     //private
49904     addicon : false,
49905     editicon: false,
49906     
49907     // element that contains real text value.. (when hidden is used..)
49908      
49909     // private
49910     onRender : function(ct, position){
49911         Roo.form.Field.prototype.onRender.call(this, ct, position);
49912         
49913         if(this.store){
49914             this.store.on('beforeload', this.onBeforeLoad, this);
49915             this.store.on('load', this.onLoad, this);
49916             this.store.on('loadexception', this.onLoadException, this);
49917             this.store.load({});
49918         }
49919         
49920         
49921         
49922     },
49923
49924     // private
49925     initEvents : function(){
49926         //Roo.form.ComboBox.superclass.initEvents.call(this);
49927  
49928     },
49929
49930     onDestroy : function(){
49931        
49932         if(this.store){
49933             this.store.un('beforeload', this.onBeforeLoad, this);
49934             this.store.un('load', this.onLoad, this);
49935             this.store.un('loadexception', this.onLoadException, this);
49936         }
49937         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49938     },
49939
49940     // private
49941     fireKey : function(e){
49942         if(e.isNavKeyPress() && !this.list.isVisible()){
49943             this.fireEvent("specialkey", this, e);
49944         }
49945     },
49946
49947     // private
49948     onResize: function(w, h){
49949         
49950         return; 
49951     
49952         
49953     },
49954
49955     /**
49956      * Allow or prevent the user from directly editing the field text.  If false is passed,
49957      * the user will only be able to select from the items defined in the dropdown list.  This method
49958      * is the runtime equivalent of setting the 'editable' config option at config time.
49959      * @param {Boolean} value True to allow the user to directly edit the field text
49960      */
49961     setEditable : function(value){
49962          
49963     },
49964
49965     // private
49966     onBeforeLoad : function(){
49967         
49968         Roo.log("Select before load");
49969         return;
49970     
49971         this.innerList.update(this.loadingText ?
49972                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49973         //this.restrictHeight();
49974         this.selectedIndex = -1;
49975     },
49976
49977     // private
49978     onLoad : function(){
49979
49980     
49981         var dom = this.el.dom;
49982         dom.innerHTML = '';
49983          var od = dom.ownerDocument;
49984          
49985         if (this.emptyText) {
49986             var op = od.createElement('option');
49987             op.setAttribute('value', '');
49988             op.innerHTML = String.format('{0}', this.emptyText);
49989             dom.appendChild(op);
49990         }
49991         if(this.store.getCount() > 0){
49992            
49993             var vf = this.valueField;
49994             var df = this.displayField;
49995             this.store.data.each(function(r) {
49996                 // which colmsn to use... testing - cdoe / title..
49997                 var op = od.createElement('option');
49998                 op.setAttribute('value', r.data[vf]);
49999                 op.innerHTML = String.format('{0}', r.data[df]);
50000                 dom.appendChild(op);
50001             });
50002             if (typeof(this.defaultValue != 'undefined')) {
50003                 this.setValue(this.defaultValue);
50004             }
50005             
50006              
50007         }else{
50008             //this.onEmptyResults();
50009         }
50010         //this.el.focus();
50011     },
50012     // private
50013     onLoadException : function()
50014     {
50015         dom.innerHTML = '';
50016             
50017         Roo.log("Select on load exception");
50018         return;
50019     
50020         this.collapse();
50021         Roo.log(this.store.reader.jsonData);
50022         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50023             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50024         }
50025         
50026         
50027     },
50028     // private
50029     onTypeAhead : function(){
50030          
50031     },
50032
50033     // private
50034     onSelect : function(record, index){
50035         Roo.log('on select?');
50036         return;
50037         if(this.fireEvent('beforeselect', this, record, index) !== false){
50038             this.setFromData(index > -1 ? record.data : false);
50039             this.collapse();
50040             this.fireEvent('select', this, record, index);
50041         }
50042     },
50043
50044     /**
50045      * Returns the currently selected field value or empty string if no value is set.
50046      * @return {String} value The selected value
50047      */
50048     getValue : function(){
50049         var dom = this.el.dom;
50050         this.value = dom.options[dom.selectedIndex].value;
50051         return this.value;
50052         
50053     },
50054
50055     /**
50056      * Clears any text/value currently set in the field
50057      */
50058     clearValue : function(){
50059         this.value = '';
50060         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50061         
50062     },
50063
50064     /**
50065      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50066      * will be displayed in the field.  If the value does not match the data value of an existing item,
50067      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50068      * Otherwise the field will be blank (although the value will still be set).
50069      * @param {String} value The value to match
50070      */
50071     setValue : function(v){
50072         var d = this.el.dom;
50073         for (var i =0; i < d.options.length;i++) {
50074             if (v == d.options[i].value) {
50075                 d.selectedIndex = i;
50076                 this.value = v;
50077                 return;
50078             }
50079         }
50080         this.clearValue();
50081     },
50082     /**
50083      * @property {Object} the last set data for the element
50084      */
50085     
50086     lastData : false,
50087     /**
50088      * Sets the value of the field based on a object which is related to the record format for the store.
50089      * @param {Object} value the value to set as. or false on reset?
50090      */
50091     setFromData : function(o){
50092         Roo.log('setfrom data?');
50093          
50094         
50095         
50096     },
50097     // private
50098     reset : function(){
50099         this.clearValue();
50100     },
50101     // private
50102     findRecord : function(prop, value){
50103         
50104         return false;
50105     
50106         var record;
50107         if(this.store.getCount() > 0){
50108             this.store.each(function(r){
50109                 if(r.data[prop] == value){
50110                     record = r;
50111                     return false;
50112                 }
50113                 return true;
50114             });
50115         }
50116         return record;
50117     },
50118     
50119     getName: function()
50120     {
50121         // returns hidden if it's set..
50122         if (!this.rendered) {return ''};
50123         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50124         
50125     },
50126      
50127
50128     
50129
50130     // private
50131     onEmptyResults : function(){
50132         Roo.log('empty results');
50133         //this.collapse();
50134     },
50135
50136     /**
50137      * Returns true if the dropdown list is expanded, else false.
50138      */
50139     isExpanded : function(){
50140         return false;
50141     },
50142
50143     /**
50144      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50145      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50146      * @param {String} value The data value of the item to select
50147      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50148      * selected item if it is not currently in view (defaults to true)
50149      * @return {Boolean} True if the value matched an item in the list, else false
50150      */
50151     selectByValue : function(v, scrollIntoView){
50152         Roo.log('select By Value');
50153         return false;
50154     
50155         if(v !== undefined && v !== null){
50156             var r = this.findRecord(this.valueField || this.displayField, v);
50157             if(r){
50158                 this.select(this.store.indexOf(r), scrollIntoView);
50159                 return true;
50160             }
50161         }
50162         return false;
50163     },
50164
50165     /**
50166      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50167      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50168      * @param {Number} index The zero-based index of the list item to select
50169      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50170      * selected item if it is not currently in view (defaults to true)
50171      */
50172     select : function(index, scrollIntoView){
50173         Roo.log('select ');
50174         return  ;
50175         
50176         this.selectedIndex = index;
50177         this.view.select(index);
50178         if(scrollIntoView !== false){
50179             var el = this.view.getNode(index);
50180             if(el){
50181                 this.innerList.scrollChildIntoView(el, false);
50182             }
50183         }
50184     },
50185
50186       
50187
50188     // private
50189     validateBlur : function(){
50190         
50191         return;
50192         
50193     },
50194
50195     // private
50196     initQuery : function(){
50197         this.doQuery(this.getRawValue());
50198     },
50199
50200     // private
50201     doForce : function(){
50202         if(this.el.dom.value.length > 0){
50203             this.el.dom.value =
50204                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50205              
50206         }
50207     },
50208
50209     /**
50210      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50211      * query allowing the query action to be canceled if needed.
50212      * @param {String} query The SQL query to execute
50213      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50214      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50215      * saved in the current store (defaults to false)
50216      */
50217     doQuery : function(q, forceAll){
50218         
50219         Roo.log('doQuery?');
50220         if(q === undefined || q === null){
50221             q = '';
50222         }
50223         var qe = {
50224             query: q,
50225             forceAll: forceAll,
50226             combo: this,
50227             cancel:false
50228         };
50229         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50230             return false;
50231         }
50232         q = qe.query;
50233         forceAll = qe.forceAll;
50234         if(forceAll === true || (q.length >= this.minChars)){
50235             if(this.lastQuery != q || this.alwaysQuery){
50236                 this.lastQuery = q;
50237                 if(this.mode == 'local'){
50238                     this.selectedIndex = -1;
50239                     if(forceAll){
50240                         this.store.clearFilter();
50241                     }else{
50242                         this.store.filter(this.displayField, q);
50243                     }
50244                     this.onLoad();
50245                 }else{
50246                     this.store.baseParams[this.queryParam] = q;
50247                     this.store.load({
50248                         params: this.getParams(q)
50249                     });
50250                     this.expand();
50251                 }
50252             }else{
50253                 this.selectedIndex = -1;
50254                 this.onLoad();   
50255             }
50256         }
50257     },
50258
50259     // private
50260     getParams : function(q){
50261         var p = {};
50262         //p[this.queryParam] = q;
50263         if(this.pageSize){
50264             p.start = 0;
50265             p.limit = this.pageSize;
50266         }
50267         return p;
50268     },
50269
50270     /**
50271      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50272      */
50273     collapse : function(){
50274         
50275     },
50276
50277     // private
50278     collapseIf : function(e){
50279         
50280     },
50281
50282     /**
50283      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50284      */
50285     expand : function(){
50286         
50287     } ,
50288
50289     // private
50290      
50291
50292     /** 
50293     * @cfg {Boolean} grow 
50294     * @hide 
50295     */
50296     /** 
50297     * @cfg {Number} growMin 
50298     * @hide 
50299     */
50300     /** 
50301     * @cfg {Number} growMax 
50302     * @hide 
50303     */
50304     /**
50305      * @hide
50306      * @method autoSize
50307      */
50308     
50309     setWidth : function()
50310     {
50311         
50312     },
50313     getResizeEl : function(){
50314         return this.el;
50315     }
50316 });//<script type="text/javasscript">
50317  
50318
50319 /**
50320  * @class Roo.DDView
50321  * A DnD enabled version of Roo.View.
50322  * @param {Element/String} container The Element in which to create the View.
50323  * @param {String} tpl The template string used to create the markup for each element of the View
50324  * @param {Object} config The configuration properties. These include all the config options of
50325  * {@link Roo.View} plus some specific to this class.<br>
50326  * <p>
50327  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50328  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50329  * <p>
50330  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50331 .x-view-drag-insert-above {
50332         border-top:1px dotted #3366cc;
50333 }
50334 .x-view-drag-insert-below {
50335         border-bottom:1px dotted #3366cc;
50336 }
50337 </code></pre>
50338  * 
50339  */
50340  
50341 Roo.DDView = function(container, tpl, config) {
50342     Roo.DDView.superclass.constructor.apply(this, arguments);
50343     this.getEl().setStyle("outline", "0px none");
50344     this.getEl().unselectable();
50345     if (this.dragGroup) {
50346                 this.setDraggable(this.dragGroup.split(","));
50347     }
50348     if (this.dropGroup) {
50349                 this.setDroppable(this.dropGroup.split(","));
50350     }
50351     if (this.deletable) {
50352         this.setDeletable();
50353     }
50354     this.isDirtyFlag = false;
50355         this.addEvents({
50356                 "drop" : true
50357         });
50358 };
50359
50360 Roo.extend(Roo.DDView, Roo.View, {
50361 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50362 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50363 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50364 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50365
50366         isFormField: true,
50367
50368         reset: Roo.emptyFn,
50369         
50370         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50371
50372         validate: function() {
50373                 return true;
50374         },
50375         
50376         destroy: function() {
50377                 this.purgeListeners();
50378                 this.getEl.removeAllListeners();
50379                 this.getEl().remove();
50380                 if (this.dragZone) {
50381                         if (this.dragZone.destroy) {
50382                                 this.dragZone.destroy();
50383                         }
50384                 }
50385                 if (this.dropZone) {
50386                         if (this.dropZone.destroy) {
50387                                 this.dropZone.destroy();
50388                         }
50389                 }
50390         },
50391
50392 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50393         getName: function() {
50394                 return this.name;
50395         },
50396
50397 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50398         setValue: function(v) {
50399                 if (!this.store) {
50400                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50401                 }
50402                 var data = {};
50403                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50404                 this.store.proxy = new Roo.data.MemoryProxy(data);
50405                 this.store.load();
50406         },
50407
50408 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50409         getValue: function() {
50410                 var result = '(';
50411                 this.store.each(function(rec) {
50412                         result += rec.id + ',';
50413                 });
50414                 return result.substr(0, result.length - 1) + ')';
50415         },
50416         
50417         getIds: function() {
50418                 var i = 0, result = new Array(this.store.getCount());
50419                 this.store.each(function(rec) {
50420                         result[i++] = rec.id;
50421                 });
50422                 return result;
50423         },
50424         
50425         isDirty: function() {
50426                 return this.isDirtyFlag;
50427         },
50428
50429 /**
50430  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50431  *      whole Element becomes the target, and this causes the drop gesture to append.
50432  */
50433     getTargetFromEvent : function(e) {
50434                 var target = e.getTarget();
50435                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50436                 target = target.parentNode;
50437                 }
50438                 if (!target) {
50439                         target = this.el.dom.lastChild || this.el.dom;
50440                 }
50441                 return target;
50442     },
50443
50444 /**
50445  *      Create the drag data which consists of an object which has the property "ddel" as
50446  *      the drag proxy element. 
50447  */
50448     getDragData : function(e) {
50449         var target = this.findItemFromChild(e.getTarget());
50450                 if(target) {
50451                         this.handleSelection(e);
50452                         var selNodes = this.getSelectedNodes();
50453             var dragData = {
50454                 source: this,
50455                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50456                 nodes: selNodes,
50457                 records: []
50458                         };
50459                         var selectedIndices = this.getSelectedIndexes();
50460                         for (var i = 0; i < selectedIndices.length; i++) {
50461                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50462                         }
50463                         if (selNodes.length == 1) {
50464                                 dragData.ddel = target.cloneNode(true); // the div element
50465                         } else {
50466                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50467                                 div.className = 'multi-proxy';
50468                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50469                                         div.appendChild(selNodes[i].cloneNode(true));
50470                                 }
50471                                 dragData.ddel = div;
50472                         }
50473             //console.log(dragData)
50474             //console.log(dragData.ddel.innerHTML)
50475                         return dragData;
50476                 }
50477         //console.log('nodragData')
50478                 return false;
50479     },
50480     
50481 /**     Specify to which ddGroup items in this DDView may be dragged. */
50482     setDraggable: function(ddGroup) {
50483         if (ddGroup instanceof Array) {
50484                 Roo.each(ddGroup, this.setDraggable, this);
50485                 return;
50486         }
50487         if (this.dragZone) {
50488                 this.dragZone.addToGroup(ddGroup);
50489         } else {
50490                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50491                                 containerScroll: true,
50492                                 ddGroup: ddGroup 
50493
50494                         });
50495 //                      Draggability implies selection. DragZone's mousedown selects the element.
50496                         if (!this.multiSelect) { this.singleSelect = true; }
50497
50498 //                      Wire the DragZone's handlers up to methods in *this*
50499                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50500                 }
50501     },
50502
50503 /**     Specify from which ddGroup this DDView accepts drops. */
50504     setDroppable: function(ddGroup) {
50505         if (ddGroup instanceof Array) {
50506                 Roo.each(ddGroup, this.setDroppable, this);
50507                 return;
50508         }
50509         if (this.dropZone) {
50510                 this.dropZone.addToGroup(ddGroup);
50511         } else {
50512                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50513                                 containerScroll: true,
50514                                 ddGroup: ddGroup
50515                         });
50516
50517 //                      Wire the DropZone's handlers up to methods in *this*
50518                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50519                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50520                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50521                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50522                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50523                 }
50524     },
50525
50526 /**     Decide whether to drop above or below a View node. */
50527     getDropPoint : function(e, n, dd){
50528         if (n == this.el.dom) { return "above"; }
50529                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50530                 var c = t + (b - t) / 2;
50531                 var y = Roo.lib.Event.getPageY(e);
50532                 if(y <= c) {
50533                         return "above";
50534                 }else{
50535                         return "below";
50536                 }
50537     },
50538
50539     onNodeEnter : function(n, dd, e, data){
50540                 return false;
50541     },
50542     
50543     onNodeOver : function(n, dd, e, data){
50544                 var pt = this.getDropPoint(e, n, dd);
50545                 // set the insert point style on the target node
50546                 var dragElClass = this.dropNotAllowed;
50547                 if (pt) {
50548                         var targetElClass;
50549                         if (pt == "above"){
50550                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50551                                 targetElClass = "x-view-drag-insert-above";
50552                         } else {
50553                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50554                                 targetElClass = "x-view-drag-insert-below";
50555                         }
50556                         if (this.lastInsertClass != targetElClass){
50557                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50558                                 this.lastInsertClass = targetElClass;
50559                         }
50560                 }
50561                 return dragElClass;
50562         },
50563
50564     onNodeOut : function(n, dd, e, data){
50565                 this.removeDropIndicators(n);
50566     },
50567
50568     onNodeDrop : function(n, dd, e, data){
50569         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50570                 return false;
50571         }
50572         var pt = this.getDropPoint(e, n, dd);
50573                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50574                 if (pt == "below") { insertAt++; }
50575                 for (var i = 0; i < data.records.length; i++) {
50576                         var r = data.records[i];
50577                         var dup = this.store.getById(r.id);
50578                         if (dup && (dd != this.dragZone)) {
50579                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50580                         } else {
50581                                 if (data.copy) {
50582                                         this.store.insert(insertAt++, r.copy());
50583                                 } else {
50584                                         data.source.isDirtyFlag = true;
50585                                         r.store.remove(r);
50586                                         this.store.insert(insertAt++, r);
50587                                 }
50588                                 this.isDirtyFlag = true;
50589                         }
50590                 }
50591                 this.dragZone.cachedTarget = null;
50592                 return true;
50593     },
50594
50595     removeDropIndicators : function(n){
50596                 if(n){
50597                         Roo.fly(n).removeClass([
50598                                 "x-view-drag-insert-above",
50599                                 "x-view-drag-insert-below"]);
50600                         this.lastInsertClass = "_noclass";
50601                 }
50602     },
50603
50604 /**
50605  *      Utility method. Add a delete option to the DDView's context menu.
50606  *      @param {String} imageUrl The URL of the "delete" icon image.
50607  */
50608         setDeletable: function(imageUrl) {
50609                 if (!this.singleSelect && !this.multiSelect) {
50610                         this.singleSelect = true;
50611                 }
50612                 var c = this.getContextMenu();
50613                 this.contextMenu.on("itemclick", function(item) {
50614                         switch (item.id) {
50615                                 case "delete":
50616                                         this.remove(this.getSelectedIndexes());
50617                                         break;
50618                         }
50619                 }, this);
50620                 this.contextMenu.add({
50621                         icon: imageUrl,
50622                         id: "delete",
50623                         text: 'Delete'
50624                 });
50625         },
50626         
50627 /**     Return the context menu for this DDView. */
50628         getContextMenu: function() {
50629                 if (!this.contextMenu) {
50630 //                      Create the View's context menu
50631                         this.contextMenu = new Roo.menu.Menu({
50632                                 id: this.id + "-contextmenu"
50633                         });
50634                         this.el.on("contextmenu", this.showContextMenu, this);
50635                 }
50636                 return this.contextMenu;
50637         },
50638         
50639         disableContextMenu: function() {
50640                 if (this.contextMenu) {
50641                         this.el.un("contextmenu", this.showContextMenu, this);
50642                 }
50643         },
50644
50645         showContextMenu: function(e, item) {
50646         item = this.findItemFromChild(e.getTarget());
50647                 if (item) {
50648                         e.stopEvent();
50649                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50650                         this.contextMenu.showAt(e.getXY());
50651             }
50652     },
50653
50654 /**
50655  *      Remove {@link Roo.data.Record}s at the specified indices.
50656  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50657  */
50658     remove: function(selectedIndices) {
50659                 selectedIndices = [].concat(selectedIndices);
50660                 for (var i = 0; i < selectedIndices.length; i++) {
50661                         var rec = this.store.getAt(selectedIndices[i]);
50662                         this.store.remove(rec);
50663                 }
50664     },
50665
50666 /**
50667  *      Double click fires the event, but also, if this is draggable, and there is only one other
50668  *      related DropZone, it transfers the selected node.
50669  */
50670     onDblClick : function(e){
50671         var item = this.findItemFromChild(e.getTarget());
50672         if(item){
50673             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50674                 return false;
50675             }
50676             if (this.dragGroup) {
50677                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50678                     while (targets.indexOf(this.dropZone) > -1) {
50679                             targets.remove(this.dropZone);
50680                                 }
50681                     if (targets.length == 1) {
50682                                         this.dragZone.cachedTarget = null;
50683                         var el = Roo.get(targets[0].getEl());
50684                         var box = el.getBox(true);
50685                         targets[0].onNodeDrop(el.dom, {
50686                                 target: el.dom,
50687                                 xy: [box.x, box.y + box.height - 1]
50688                         }, null, this.getDragData(e));
50689                     }
50690                 }
50691         }
50692     },
50693     
50694     handleSelection: function(e) {
50695                 this.dragZone.cachedTarget = null;
50696         var item = this.findItemFromChild(e.getTarget());
50697         if (!item) {
50698                 this.clearSelections(true);
50699                 return;
50700         }
50701                 if (item && (this.multiSelect || this.singleSelect)){
50702                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50703                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50704                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50705                                 this.unselect(item);
50706                         } else {
50707                                 this.select(item, this.multiSelect && e.ctrlKey);
50708                                 this.lastSelection = item;
50709                         }
50710                 }
50711     },
50712
50713     onItemClick : function(item, index, e){
50714                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50715                         return false;
50716                 }
50717                 return true;
50718     },
50719
50720     unselect : function(nodeInfo, suppressEvent){
50721                 var node = this.getNode(nodeInfo);
50722                 if(node && this.isSelected(node)){
50723                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50724                                 Roo.fly(node).removeClass(this.selectedClass);
50725                                 this.selections.remove(node);
50726                                 if(!suppressEvent){
50727                                         this.fireEvent("selectionchange", this, this.selections);
50728                                 }
50729                         }
50730                 }
50731     }
50732 });
50733 /*
50734  * Based on:
50735  * Ext JS Library 1.1.1
50736  * Copyright(c) 2006-2007, Ext JS, LLC.
50737  *
50738  * Originally Released Under LGPL - original licence link has changed is not relivant.
50739  *
50740  * Fork - LGPL
50741  * <script type="text/javascript">
50742  */
50743  
50744 /**
50745  * @class Roo.LayoutManager
50746  * @extends Roo.util.Observable
50747  * Base class for layout managers.
50748  */
50749 Roo.LayoutManager = function(container, config){
50750     Roo.LayoutManager.superclass.constructor.call(this);
50751     this.el = Roo.get(container);
50752     // ie scrollbar fix
50753     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50754         document.body.scroll = "no";
50755     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50756         this.el.position('relative');
50757     }
50758     this.id = this.el.id;
50759     this.el.addClass("x-layout-container");
50760     /** false to disable window resize monitoring @type Boolean */
50761     this.monitorWindowResize = true;
50762     this.regions = {};
50763     this.addEvents({
50764         /**
50765          * @event layout
50766          * Fires when a layout is performed. 
50767          * @param {Roo.LayoutManager} this
50768          */
50769         "layout" : true,
50770         /**
50771          * @event regionresized
50772          * Fires when the user resizes a region. 
50773          * @param {Roo.LayoutRegion} region The resized region
50774          * @param {Number} newSize The new size (width for east/west, height for north/south)
50775          */
50776         "regionresized" : true,
50777         /**
50778          * @event regioncollapsed
50779          * Fires when a region is collapsed. 
50780          * @param {Roo.LayoutRegion} region The collapsed region
50781          */
50782         "regioncollapsed" : true,
50783         /**
50784          * @event regionexpanded
50785          * Fires when a region is expanded.  
50786          * @param {Roo.LayoutRegion} region The expanded region
50787          */
50788         "regionexpanded" : true
50789     });
50790     this.updating = false;
50791     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50792 };
50793
50794 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50795     /**
50796      * Returns true if this layout is currently being updated
50797      * @return {Boolean}
50798      */
50799     isUpdating : function(){
50800         return this.updating; 
50801     },
50802     
50803     /**
50804      * Suspend the LayoutManager from doing auto-layouts while
50805      * making multiple add or remove calls
50806      */
50807     beginUpdate : function(){
50808         this.updating = true;    
50809     },
50810     
50811     /**
50812      * Restore auto-layouts and optionally disable the manager from performing a layout
50813      * @param {Boolean} noLayout true to disable a layout update 
50814      */
50815     endUpdate : function(noLayout){
50816         this.updating = false;
50817         if(!noLayout){
50818             this.layout();
50819         }    
50820     },
50821     
50822     layout: function(){
50823         
50824     },
50825     
50826     onRegionResized : function(region, newSize){
50827         this.fireEvent("regionresized", region, newSize);
50828         this.layout();
50829     },
50830     
50831     onRegionCollapsed : function(region){
50832         this.fireEvent("regioncollapsed", region);
50833     },
50834     
50835     onRegionExpanded : function(region){
50836         this.fireEvent("regionexpanded", region);
50837     },
50838         
50839     /**
50840      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50841      * performs box-model adjustments.
50842      * @return {Object} The size as an object {width: (the width), height: (the height)}
50843      */
50844     getViewSize : function(){
50845         var size;
50846         if(this.el.dom != document.body){
50847             size = this.el.getSize();
50848         }else{
50849             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50850         }
50851         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50852         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50853         return size;
50854     },
50855     
50856     /**
50857      * Returns the Element this layout is bound to.
50858      * @return {Roo.Element}
50859      */
50860     getEl : function(){
50861         return this.el;
50862     },
50863     
50864     /**
50865      * Returns the specified region.
50866      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50867      * @return {Roo.LayoutRegion}
50868      */
50869     getRegion : function(target){
50870         return this.regions[target.toLowerCase()];
50871     },
50872     
50873     onWindowResize : function(){
50874         if(this.monitorWindowResize){
50875             this.layout();
50876         }
50877     }
50878 });/*
50879  * Based on:
50880  * Ext JS Library 1.1.1
50881  * Copyright(c) 2006-2007, Ext JS, LLC.
50882  *
50883  * Originally Released Under LGPL - original licence link has changed is not relivant.
50884  *
50885  * Fork - LGPL
50886  * <script type="text/javascript">
50887  */
50888 /**
50889  * @class Roo.BorderLayout
50890  * @extends Roo.LayoutManager
50891  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50892  * please see: <br><br>
50893  * <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>
50894  * <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>
50895  * Example:
50896  <pre><code>
50897  var layout = new Roo.BorderLayout(document.body, {
50898     north: {
50899         initialSize: 25,
50900         titlebar: false
50901     },
50902     west: {
50903         split:true,
50904         initialSize: 200,
50905         minSize: 175,
50906         maxSize: 400,
50907         titlebar: true,
50908         collapsible: true
50909     },
50910     east: {
50911         split:true,
50912         initialSize: 202,
50913         minSize: 175,
50914         maxSize: 400,
50915         titlebar: true,
50916         collapsible: true
50917     },
50918     south: {
50919         split:true,
50920         initialSize: 100,
50921         minSize: 100,
50922         maxSize: 200,
50923         titlebar: true,
50924         collapsible: true
50925     },
50926     center: {
50927         titlebar: true,
50928         autoScroll:true,
50929         resizeTabs: true,
50930         minTabWidth: 50,
50931         preferredTabWidth: 150
50932     }
50933 });
50934
50935 // shorthand
50936 var CP = Roo.ContentPanel;
50937
50938 layout.beginUpdate();
50939 layout.add("north", new CP("north", "North"));
50940 layout.add("south", new CP("south", {title: "South", closable: true}));
50941 layout.add("west", new CP("west", {title: "West"}));
50942 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50943 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50944 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50945 layout.getRegion("center").showPanel("center1");
50946 layout.endUpdate();
50947 </code></pre>
50948
50949 <b>The container the layout is rendered into can be either the body element or any other element.
50950 If it is not the body element, the container needs to either be an absolute positioned element,
50951 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50952 the container size if it is not the body element.</b>
50953
50954 * @constructor
50955 * Create a new BorderLayout
50956 * @param {String/HTMLElement/Element} container The container this layout is bound to
50957 * @param {Object} config Configuration options
50958  */
50959 Roo.BorderLayout = function(container, config){
50960     config = config || {};
50961     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50962     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50963     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50964         var target = this.factory.validRegions[i];
50965         if(config[target]){
50966             this.addRegion(target, config[target]);
50967         }
50968     }
50969 };
50970
50971 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50972     /**
50973      * Creates and adds a new region if it doesn't already exist.
50974      * @param {String} target The target region key (north, south, east, west or center).
50975      * @param {Object} config The regions config object
50976      * @return {BorderLayoutRegion} The new region
50977      */
50978     addRegion : function(target, config){
50979         if(!this.regions[target]){
50980             var r = this.factory.create(target, this, config);
50981             this.bindRegion(target, r);
50982         }
50983         return this.regions[target];
50984     },
50985
50986     // private (kinda)
50987     bindRegion : function(name, r){
50988         this.regions[name] = r;
50989         r.on("visibilitychange", this.layout, this);
50990         r.on("paneladded", this.layout, this);
50991         r.on("panelremoved", this.layout, this);
50992         r.on("invalidated", this.layout, this);
50993         r.on("resized", this.onRegionResized, this);
50994         r.on("collapsed", this.onRegionCollapsed, this);
50995         r.on("expanded", this.onRegionExpanded, this);
50996     },
50997
50998     /**
50999      * Performs a layout update.
51000      */
51001     layout : function(){
51002         if(this.updating) {
51003             return;
51004         }
51005         var size = this.getViewSize();
51006         var w = size.width;
51007         var h = size.height;
51008         var centerW = w;
51009         var centerH = h;
51010         var centerY = 0;
51011         var centerX = 0;
51012         //var x = 0, y = 0;
51013
51014         var rs = this.regions;
51015         var north = rs["north"];
51016         var south = rs["south"]; 
51017         var west = rs["west"];
51018         var east = rs["east"];
51019         var center = rs["center"];
51020         //if(this.hideOnLayout){ // not supported anymore
51021             //c.el.setStyle("display", "none");
51022         //}
51023         if(north && north.isVisible()){
51024             var b = north.getBox();
51025             var m = north.getMargins();
51026             b.width = w - (m.left+m.right);
51027             b.x = m.left;
51028             b.y = m.top;
51029             centerY = b.height + b.y + m.bottom;
51030             centerH -= centerY;
51031             north.updateBox(this.safeBox(b));
51032         }
51033         if(south && south.isVisible()){
51034             var b = south.getBox();
51035             var m = south.getMargins();
51036             b.width = w - (m.left+m.right);
51037             b.x = m.left;
51038             var totalHeight = (b.height + m.top + m.bottom);
51039             b.y = h - totalHeight + m.top;
51040             centerH -= totalHeight;
51041             south.updateBox(this.safeBox(b));
51042         }
51043         if(west && west.isVisible()){
51044             var b = west.getBox();
51045             var m = west.getMargins();
51046             b.height = centerH - (m.top+m.bottom);
51047             b.x = m.left;
51048             b.y = centerY + m.top;
51049             var totalWidth = (b.width + m.left + m.right);
51050             centerX += totalWidth;
51051             centerW -= totalWidth;
51052             west.updateBox(this.safeBox(b));
51053         }
51054         if(east && east.isVisible()){
51055             var b = east.getBox();
51056             var m = east.getMargins();
51057             b.height = centerH - (m.top+m.bottom);
51058             var totalWidth = (b.width + m.left + m.right);
51059             b.x = w - totalWidth + m.left;
51060             b.y = centerY + m.top;
51061             centerW -= totalWidth;
51062             east.updateBox(this.safeBox(b));
51063         }
51064         if(center){
51065             var m = center.getMargins();
51066             var centerBox = {
51067                 x: centerX + m.left,
51068                 y: centerY + m.top,
51069                 width: centerW - (m.left+m.right),
51070                 height: centerH - (m.top+m.bottom)
51071             };
51072             //if(this.hideOnLayout){
51073                 //center.el.setStyle("display", "block");
51074             //}
51075             center.updateBox(this.safeBox(centerBox));
51076         }
51077         this.el.repaint();
51078         this.fireEvent("layout", this);
51079     },
51080
51081     // private
51082     safeBox : function(box){
51083         box.width = Math.max(0, box.width);
51084         box.height = Math.max(0, box.height);
51085         return box;
51086     },
51087
51088     /**
51089      * Adds a ContentPanel (or subclass) to this layout.
51090      * @param {String} target The target region key (north, south, east, west or center).
51091      * @param {Roo.ContentPanel} panel The panel to add
51092      * @return {Roo.ContentPanel} The added panel
51093      */
51094     add : function(target, panel){
51095          
51096         target = target.toLowerCase();
51097         return this.regions[target].add(panel);
51098     },
51099
51100     /**
51101      * Remove a ContentPanel (or subclass) to this layout.
51102      * @param {String} target The target region key (north, south, east, west or center).
51103      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51104      * @return {Roo.ContentPanel} The removed panel
51105      */
51106     remove : function(target, panel){
51107         target = target.toLowerCase();
51108         return this.regions[target].remove(panel);
51109     },
51110
51111     /**
51112      * Searches all regions for a panel with the specified id
51113      * @param {String} panelId
51114      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51115      */
51116     findPanel : function(panelId){
51117         var rs = this.regions;
51118         for(var target in rs){
51119             if(typeof rs[target] != "function"){
51120                 var p = rs[target].getPanel(panelId);
51121                 if(p){
51122                     return p;
51123                 }
51124             }
51125         }
51126         return null;
51127     },
51128
51129     /**
51130      * Searches all regions for a panel with the specified id and activates (shows) it.
51131      * @param {String/ContentPanel} panelId The panels id or the panel itself
51132      * @return {Roo.ContentPanel} The shown panel or null
51133      */
51134     showPanel : function(panelId) {
51135       var rs = this.regions;
51136       for(var target in rs){
51137          var r = rs[target];
51138          if(typeof r != "function"){
51139             if(r.hasPanel(panelId)){
51140                return r.showPanel(panelId);
51141             }
51142          }
51143       }
51144       return null;
51145    },
51146
51147    /**
51148      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51149      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51150      */
51151     restoreState : function(provider){
51152         if(!provider){
51153             provider = Roo.state.Manager;
51154         }
51155         var sm = new Roo.LayoutStateManager();
51156         sm.init(this, provider);
51157     },
51158
51159     /**
51160      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51161      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51162      * a valid ContentPanel config object.  Example:
51163      * <pre><code>
51164 // Create the main layout
51165 var layout = new Roo.BorderLayout('main-ct', {
51166     west: {
51167         split:true,
51168         minSize: 175,
51169         titlebar: true
51170     },
51171     center: {
51172         title:'Components'
51173     }
51174 }, 'main-ct');
51175
51176 // Create and add multiple ContentPanels at once via configs
51177 layout.batchAdd({
51178    west: {
51179        id: 'source-files',
51180        autoCreate:true,
51181        title:'Ext Source Files',
51182        autoScroll:true,
51183        fitToFrame:true
51184    },
51185    center : {
51186        el: cview,
51187        autoScroll:true,
51188        fitToFrame:true,
51189        toolbar: tb,
51190        resizeEl:'cbody'
51191    }
51192 });
51193 </code></pre>
51194      * @param {Object} regions An object containing ContentPanel configs by region name
51195      */
51196     batchAdd : function(regions){
51197         this.beginUpdate();
51198         for(var rname in regions){
51199             var lr = this.regions[rname];
51200             if(lr){
51201                 this.addTypedPanels(lr, regions[rname]);
51202             }
51203         }
51204         this.endUpdate();
51205     },
51206
51207     // private
51208     addTypedPanels : function(lr, ps){
51209         if(typeof ps == 'string'){
51210             lr.add(new Roo.ContentPanel(ps));
51211         }
51212         else if(ps instanceof Array){
51213             for(var i =0, len = ps.length; i < len; i++){
51214                 this.addTypedPanels(lr, ps[i]);
51215             }
51216         }
51217         else if(!ps.events){ // raw config?
51218             var el = ps.el;
51219             delete ps.el; // prevent conflict
51220             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51221         }
51222         else {  // panel object assumed!
51223             lr.add(ps);
51224         }
51225     },
51226     /**
51227      * Adds a xtype elements to the layout.
51228      * <pre><code>
51229
51230 layout.addxtype({
51231        xtype : 'ContentPanel',
51232        region: 'west',
51233        items: [ .... ]
51234    }
51235 );
51236
51237 layout.addxtype({
51238         xtype : 'NestedLayoutPanel',
51239         region: 'west',
51240         layout: {
51241            center: { },
51242            west: { }   
51243         },
51244         items : [ ... list of content panels or nested layout panels.. ]
51245    }
51246 );
51247 </code></pre>
51248      * @param {Object} cfg Xtype definition of item to add.
51249      */
51250     addxtype : function(cfg)
51251     {
51252         // basically accepts a pannel...
51253         // can accept a layout region..!?!?
51254         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51255         
51256         if (!cfg.xtype.match(/Panel$/)) {
51257             return false;
51258         }
51259         var ret = false;
51260         
51261         if (typeof(cfg.region) == 'undefined') {
51262             Roo.log("Failed to add Panel, region was not set");
51263             Roo.log(cfg);
51264             return false;
51265         }
51266         var region = cfg.region;
51267         delete cfg.region;
51268         
51269           
51270         var xitems = [];
51271         if (cfg.items) {
51272             xitems = cfg.items;
51273             delete cfg.items;
51274         }
51275         var nb = false;
51276         
51277         switch(cfg.xtype) 
51278         {
51279             case 'ContentPanel':  // ContentPanel (el, cfg)
51280             case 'ScrollPanel':  // ContentPanel (el, cfg)
51281             case 'ViewPanel': 
51282                 if(cfg.autoCreate) {
51283                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51284                 } else {
51285                     var el = this.el.createChild();
51286                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51287                 }
51288                 
51289                 this.add(region, ret);
51290                 break;
51291             
51292             
51293             case 'TreePanel': // our new panel!
51294                 cfg.el = this.el.createChild();
51295                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51296                 this.add(region, ret);
51297                 break;
51298             
51299             case 'NestedLayoutPanel': 
51300                 // create a new Layout (which is  a Border Layout...
51301                 var el = this.el.createChild();
51302                 var clayout = cfg.layout;
51303                 delete cfg.layout;
51304                 clayout.items   = clayout.items  || [];
51305                 // replace this exitems with the clayout ones..
51306                 xitems = clayout.items;
51307                  
51308                 
51309                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51310                     cfg.background = false;
51311                 }
51312                 var layout = new Roo.BorderLayout(el, clayout);
51313                 
51314                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51315                 //console.log('adding nested layout panel '  + cfg.toSource());
51316                 this.add(region, ret);
51317                 nb = {}; /// find first...
51318                 break;
51319                 
51320             case 'GridPanel': 
51321             
51322                 // needs grid and region
51323                 
51324                 //var el = this.getRegion(region).el.createChild();
51325                 var el = this.el.createChild();
51326                 // create the grid first...
51327                 
51328                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51329                 delete cfg.grid;
51330                 if (region == 'center' && this.active ) {
51331                     cfg.background = false;
51332                 }
51333                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51334                 
51335                 this.add(region, ret);
51336                 if (cfg.background) {
51337                     ret.on('activate', function(gp) {
51338                         if (!gp.grid.rendered) {
51339                             gp.grid.render();
51340                         }
51341                     });
51342                 } else {
51343                     grid.render();
51344                 }
51345                 break;
51346            
51347            
51348            
51349                 
51350                 
51351                 
51352             default:
51353                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51354                     
51355                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51356                     this.add(region, ret);
51357                 } else {
51358                 
51359                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51360                     return null;
51361                 }
51362                 
51363              // GridPanel (grid, cfg)
51364             
51365         }
51366         this.beginUpdate();
51367         // add children..
51368         var region = '';
51369         var abn = {};
51370         Roo.each(xitems, function(i)  {
51371             region = nb && i.region ? i.region : false;
51372             
51373             var add = ret.addxtype(i);
51374            
51375             if (region) {
51376                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51377                 if (!i.background) {
51378                     abn[region] = nb[region] ;
51379                 }
51380             }
51381             
51382         });
51383         this.endUpdate();
51384
51385         // make the last non-background panel active..
51386         //if (nb) { Roo.log(abn); }
51387         if (nb) {
51388             
51389             for(var r in abn) {
51390                 region = this.getRegion(r);
51391                 if (region) {
51392                     // tried using nb[r], but it does not work..
51393                      
51394                     region.showPanel(abn[r]);
51395                    
51396                 }
51397             }
51398         }
51399         return ret;
51400         
51401     }
51402 });
51403
51404 /**
51405  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51406  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51407  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51408  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51409  * <pre><code>
51410 // shorthand
51411 var CP = Roo.ContentPanel;
51412
51413 var layout = Roo.BorderLayout.create({
51414     north: {
51415         initialSize: 25,
51416         titlebar: false,
51417         panels: [new CP("north", "North")]
51418     },
51419     west: {
51420         split:true,
51421         initialSize: 200,
51422         minSize: 175,
51423         maxSize: 400,
51424         titlebar: true,
51425         collapsible: true,
51426         panels: [new CP("west", {title: "West"})]
51427     },
51428     east: {
51429         split:true,
51430         initialSize: 202,
51431         minSize: 175,
51432         maxSize: 400,
51433         titlebar: true,
51434         collapsible: true,
51435         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51436     },
51437     south: {
51438         split:true,
51439         initialSize: 100,
51440         minSize: 100,
51441         maxSize: 200,
51442         titlebar: true,
51443         collapsible: true,
51444         panels: [new CP("south", {title: "South", closable: true})]
51445     },
51446     center: {
51447         titlebar: true,
51448         autoScroll:true,
51449         resizeTabs: true,
51450         minTabWidth: 50,
51451         preferredTabWidth: 150,
51452         panels: [
51453             new CP("center1", {title: "Close Me", closable: true}),
51454             new CP("center2", {title: "Center Panel", closable: false})
51455         ]
51456     }
51457 }, document.body);
51458
51459 layout.getRegion("center").showPanel("center1");
51460 </code></pre>
51461  * @param config
51462  * @param targetEl
51463  */
51464 Roo.BorderLayout.create = function(config, targetEl){
51465     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51466     layout.beginUpdate();
51467     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51468     for(var j = 0, jlen = regions.length; j < jlen; j++){
51469         var lr = regions[j];
51470         if(layout.regions[lr] && config[lr].panels){
51471             var r = layout.regions[lr];
51472             var ps = config[lr].panels;
51473             layout.addTypedPanels(r, ps);
51474         }
51475     }
51476     layout.endUpdate();
51477     return layout;
51478 };
51479
51480 // private
51481 Roo.BorderLayout.RegionFactory = {
51482     // private
51483     validRegions : ["north","south","east","west","center"],
51484
51485     // private
51486     create : function(target, mgr, config){
51487         target = target.toLowerCase();
51488         if(config.lightweight || config.basic){
51489             return new Roo.BasicLayoutRegion(mgr, config, target);
51490         }
51491         switch(target){
51492             case "north":
51493                 return new Roo.NorthLayoutRegion(mgr, config);
51494             case "south":
51495                 return new Roo.SouthLayoutRegion(mgr, config);
51496             case "east":
51497                 return new Roo.EastLayoutRegion(mgr, config);
51498             case "west":
51499                 return new Roo.WestLayoutRegion(mgr, config);
51500             case "center":
51501                 return new Roo.CenterLayoutRegion(mgr, config);
51502         }
51503         throw 'Layout region "'+target+'" not supported.';
51504     }
51505 };/*
51506  * Based on:
51507  * Ext JS Library 1.1.1
51508  * Copyright(c) 2006-2007, Ext JS, LLC.
51509  *
51510  * Originally Released Under LGPL - original licence link has changed is not relivant.
51511  *
51512  * Fork - LGPL
51513  * <script type="text/javascript">
51514  */
51515  
51516 /**
51517  * @class Roo.BasicLayoutRegion
51518  * @extends Roo.util.Observable
51519  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51520  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51521  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51522  */
51523 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51524     this.mgr = mgr;
51525     this.position  = pos;
51526     this.events = {
51527         /**
51528          * @scope Roo.BasicLayoutRegion
51529          */
51530         
51531         /**
51532          * @event beforeremove
51533          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51534          * @param {Roo.LayoutRegion} this
51535          * @param {Roo.ContentPanel} panel The panel
51536          * @param {Object} e The cancel event object
51537          */
51538         "beforeremove" : true,
51539         /**
51540          * @event invalidated
51541          * Fires when the layout for this region is changed.
51542          * @param {Roo.LayoutRegion} this
51543          */
51544         "invalidated" : true,
51545         /**
51546          * @event visibilitychange
51547          * Fires when this region is shown or hidden 
51548          * @param {Roo.LayoutRegion} this
51549          * @param {Boolean} visibility true or false
51550          */
51551         "visibilitychange" : true,
51552         /**
51553          * @event paneladded
51554          * Fires when a panel is added. 
51555          * @param {Roo.LayoutRegion} this
51556          * @param {Roo.ContentPanel} panel The panel
51557          */
51558         "paneladded" : true,
51559         /**
51560          * @event panelremoved
51561          * Fires when a panel is removed. 
51562          * @param {Roo.LayoutRegion} this
51563          * @param {Roo.ContentPanel} panel The panel
51564          */
51565         "panelremoved" : true,
51566         /**
51567          * @event beforecollapse
51568          * Fires when this region before collapse.
51569          * @param {Roo.LayoutRegion} this
51570          */
51571         "beforecollapse" : true,
51572         /**
51573          * @event collapsed
51574          * Fires when this region is collapsed.
51575          * @param {Roo.LayoutRegion} this
51576          */
51577         "collapsed" : true,
51578         /**
51579          * @event expanded
51580          * Fires when this region is expanded.
51581          * @param {Roo.LayoutRegion} this
51582          */
51583         "expanded" : true,
51584         /**
51585          * @event slideshow
51586          * Fires when this region is slid into view.
51587          * @param {Roo.LayoutRegion} this
51588          */
51589         "slideshow" : true,
51590         /**
51591          * @event slidehide
51592          * Fires when this region slides out of view. 
51593          * @param {Roo.LayoutRegion} this
51594          */
51595         "slidehide" : true,
51596         /**
51597          * @event panelactivated
51598          * Fires when a panel is activated. 
51599          * @param {Roo.LayoutRegion} this
51600          * @param {Roo.ContentPanel} panel The activated panel
51601          */
51602         "panelactivated" : true,
51603         /**
51604          * @event resized
51605          * Fires when the user resizes this region. 
51606          * @param {Roo.LayoutRegion} this
51607          * @param {Number} newSize The new size (width for east/west, height for north/south)
51608          */
51609         "resized" : true
51610     };
51611     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51612     this.panels = new Roo.util.MixedCollection();
51613     this.panels.getKey = this.getPanelId.createDelegate(this);
51614     this.box = null;
51615     this.activePanel = null;
51616     // ensure listeners are added...
51617     
51618     if (config.listeners || config.events) {
51619         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51620             listeners : config.listeners || {},
51621             events : config.events || {}
51622         });
51623     }
51624     
51625     if(skipConfig !== true){
51626         this.applyConfig(config);
51627     }
51628 };
51629
51630 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51631     getPanelId : function(p){
51632         return p.getId();
51633     },
51634     
51635     applyConfig : function(config){
51636         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51637         this.config = config;
51638         
51639     },
51640     
51641     /**
51642      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51643      * the width, for horizontal (north, south) the height.
51644      * @param {Number} newSize The new width or height
51645      */
51646     resizeTo : function(newSize){
51647         var el = this.el ? this.el :
51648                  (this.activePanel ? this.activePanel.getEl() : null);
51649         if(el){
51650             switch(this.position){
51651                 case "east":
51652                 case "west":
51653                     el.setWidth(newSize);
51654                     this.fireEvent("resized", this, newSize);
51655                 break;
51656                 case "north":
51657                 case "south":
51658                     el.setHeight(newSize);
51659                     this.fireEvent("resized", this, newSize);
51660                 break;                
51661             }
51662         }
51663     },
51664     
51665     getBox : function(){
51666         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51667     },
51668     
51669     getMargins : function(){
51670         return this.margins;
51671     },
51672     
51673     updateBox : function(box){
51674         this.box = box;
51675         var el = this.activePanel.getEl();
51676         el.dom.style.left = box.x + "px";
51677         el.dom.style.top = box.y + "px";
51678         this.activePanel.setSize(box.width, box.height);
51679     },
51680     
51681     /**
51682      * Returns the container element for this region.
51683      * @return {Roo.Element}
51684      */
51685     getEl : function(){
51686         return this.activePanel;
51687     },
51688     
51689     /**
51690      * Returns true if this region is currently visible.
51691      * @return {Boolean}
51692      */
51693     isVisible : function(){
51694         return this.activePanel ? true : false;
51695     },
51696     
51697     setActivePanel : function(panel){
51698         panel = this.getPanel(panel);
51699         if(this.activePanel && this.activePanel != panel){
51700             this.activePanel.setActiveState(false);
51701             this.activePanel.getEl().setLeftTop(-10000,-10000);
51702         }
51703         this.activePanel = panel;
51704         panel.setActiveState(true);
51705         if(this.box){
51706             panel.setSize(this.box.width, this.box.height);
51707         }
51708         this.fireEvent("panelactivated", this, panel);
51709         this.fireEvent("invalidated");
51710     },
51711     
51712     /**
51713      * Show the specified panel.
51714      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51715      * @return {Roo.ContentPanel} The shown panel or null
51716      */
51717     showPanel : function(panel){
51718         if(panel = this.getPanel(panel)){
51719             this.setActivePanel(panel);
51720         }
51721         return panel;
51722     },
51723     
51724     /**
51725      * Get the active panel for this region.
51726      * @return {Roo.ContentPanel} The active panel or null
51727      */
51728     getActivePanel : function(){
51729         return this.activePanel;
51730     },
51731     
51732     /**
51733      * Add the passed ContentPanel(s)
51734      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51735      * @return {Roo.ContentPanel} The panel added (if only one was added)
51736      */
51737     add : function(panel){
51738         if(arguments.length > 1){
51739             for(var i = 0, len = arguments.length; i < len; i++) {
51740                 this.add(arguments[i]);
51741             }
51742             return null;
51743         }
51744         if(this.hasPanel(panel)){
51745             this.showPanel(panel);
51746             return panel;
51747         }
51748         var el = panel.getEl();
51749         if(el.dom.parentNode != this.mgr.el.dom){
51750             this.mgr.el.dom.appendChild(el.dom);
51751         }
51752         if(panel.setRegion){
51753             panel.setRegion(this);
51754         }
51755         this.panels.add(panel);
51756         el.setStyle("position", "absolute");
51757         if(!panel.background){
51758             this.setActivePanel(panel);
51759             if(this.config.initialSize && this.panels.getCount()==1){
51760                 this.resizeTo(this.config.initialSize);
51761             }
51762         }
51763         this.fireEvent("paneladded", this, panel);
51764         return panel;
51765     },
51766     
51767     /**
51768      * Returns true if the panel is in this region.
51769      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51770      * @return {Boolean}
51771      */
51772     hasPanel : function(panel){
51773         if(typeof panel == "object"){ // must be panel obj
51774             panel = panel.getId();
51775         }
51776         return this.getPanel(panel) ? true : false;
51777     },
51778     
51779     /**
51780      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51781      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51782      * @param {Boolean} preservePanel Overrides the config preservePanel option
51783      * @return {Roo.ContentPanel} The panel that was removed
51784      */
51785     remove : function(panel, preservePanel){
51786         panel = this.getPanel(panel);
51787         if(!panel){
51788             return null;
51789         }
51790         var e = {};
51791         this.fireEvent("beforeremove", this, panel, e);
51792         if(e.cancel === true){
51793             return null;
51794         }
51795         var panelId = panel.getId();
51796         this.panels.removeKey(panelId);
51797         return panel;
51798     },
51799     
51800     /**
51801      * Returns the panel specified or null if it's not in this region.
51802      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51803      * @return {Roo.ContentPanel}
51804      */
51805     getPanel : function(id){
51806         if(typeof id == "object"){ // must be panel obj
51807             return id;
51808         }
51809         return this.panels.get(id);
51810     },
51811     
51812     /**
51813      * Returns this regions position (north/south/east/west/center).
51814      * @return {String} 
51815      */
51816     getPosition: function(){
51817         return this.position;    
51818     }
51819 });/*
51820  * Based on:
51821  * Ext JS Library 1.1.1
51822  * Copyright(c) 2006-2007, Ext JS, LLC.
51823  *
51824  * Originally Released Under LGPL - original licence link has changed is not relivant.
51825  *
51826  * Fork - LGPL
51827  * <script type="text/javascript">
51828  */
51829  
51830 /**
51831  * @class Roo.LayoutRegion
51832  * @extends Roo.BasicLayoutRegion
51833  * This class represents a region in a layout manager.
51834  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51835  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51836  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51837  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51838  * @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})
51839  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51840  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51841  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51842  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51843  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51844  * @cfg {String}    title           The title for the region (overrides panel titles)
51845  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51846  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51847  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51848  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51849  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51850  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51851  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51852  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51853  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51854  * @cfg {Boolean}   showPin         True to show a pin button
51855  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51856  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51857  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51858  * @cfg {Number}    width           For East/West panels
51859  * @cfg {Number}    height          For North/South panels
51860  * @cfg {Boolean}   split           To show the splitter
51861  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51862  */
51863 Roo.LayoutRegion = function(mgr, config, pos){
51864     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51865     var dh = Roo.DomHelper;
51866     /** This region's container element 
51867     * @type Roo.Element */
51868     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51869     /** This region's title element 
51870     * @type Roo.Element */
51871
51872     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51873         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51874         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51875     ]}, true);
51876     this.titleEl.enableDisplayMode();
51877     /** This region's title text element 
51878     * @type HTMLElement */
51879     this.titleTextEl = this.titleEl.dom.firstChild;
51880     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51881     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51882     this.closeBtn.enableDisplayMode();
51883     this.closeBtn.on("click", this.closeClicked, this);
51884     this.closeBtn.hide();
51885
51886     this.createBody(config);
51887     this.visible = true;
51888     this.collapsed = false;
51889
51890     if(config.hideWhenEmpty){
51891         this.hide();
51892         this.on("paneladded", this.validateVisibility, this);
51893         this.on("panelremoved", this.validateVisibility, this);
51894     }
51895     this.applyConfig(config);
51896 };
51897
51898 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51899
51900     createBody : function(){
51901         /** This region's body element 
51902         * @type Roo.Element */
51903         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51904     },
51905
51906     applyConfig : function(c){
51907         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51908             var dh = Roo.DomHelper;
51909             if(c.titlebar !== false){
51910                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51911                 this.collapseBtn.on("click", this.collapse, this);
51912                 this.collapseBtn.enableDisplayMode();
51913
51914                 if(c.showPin === true || this.showPin){
51915                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51916                     this.stickBtn.enableDisplayMode();
51917                     this.stickBtn.on("click", this.expand, this);
51918                     this.stickBtn.hide();
51919                 }
51920             }
51921             /** This region's collapsed element
51922             * @type Roo.Element */
51923             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51924                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51925             ]}, true);
51926             if(c.floatable !== false){
51927                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51928                this.collapsedEl.on("click", this.collapseClick, this);
51929             }
51930
51931             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51932                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51933                    id: "message", unselectable: "on", style:{"float":"left"}});
51934                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51935              }
51936             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51937             this.expandBtn.on("click", this.expand, this);
51938         }
51939         if(this.collapseBtn){
51940             this.collapseBtn.setVisible(c.collapsible == true);
51941         }
51942         this.cmargins = c.cmargins || this.cmargins ||
51943                          (this.position == "west" || this.position == "east" ?
51944                              {top: 0, left: 2, right:2, bottom: 0} :
51945                              {top: 2, left: 0, right:0, bottom: 2});
51946         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51947         this.bottomTabs = c.tabPosition != "top";
51948         this.autoScroll = c.autoScroll || false;
51949         if(this.autoScroll){
51950             this.bodyEl.setStyle("overflow", "auto");
51951         }else{
51952             this.bodyEl.setStyle("overflow", "hidden");
51953         }
51954         //if(c.titlebar !== false){
51955             if((!c.titlebar && !c.title) || c.titlebar === false){
51956                 this.titleEl.hide();
51957             }else{
51958                 this.titleEl.show();
51959                 if(c.title){
51960                     this.titleTextEl.innerHTML = c.title;
51961                 }
51962             }
51963         //}
51964         this.duration = c.duration || .30;
51965         this.slideDuration = c.slideDuration || .45;
51966         this.config = c;
51967         if(c.collapsed){
51968             this.collapse(true);
51969         }
51970         if(c.hidden){
51971             this.hide();
51972         }
51973     },
51974     /**
51975      * Returns true if this region is currently visible.
51976      * @return {Boolean}
51977      */
51978     isVisible : function(){
51979         return this.visible;
51980     },
51981
51982     /**
51983      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51984      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51985      */
51986     setCollapsedTitle : function(title){
51987         title = title || "&#160;";
51988         if(this.collapsedTitleTextEl){
51989             this.collapsedTitleTextEl.innerHTML = title;
51990         }
51991     },
51992
51993     getBox : function(){
51994         var b;
51995         if(!this.collapsed){
51996             b = this.el.getBox(false, true);
51997         }else{
51998             b = this.collapsedEl.getBox(false, true);
51999         }
52000         return b;
52001     },
52002
52003     getMargins : function(){
52004         return this.collapsed ? this.cmargins : this.margins;
52005     },
52006
52007     highlight : function(){
52008         this.el.addClass("x-layout-panel-dragover");
52009     },
52010
52011     unhighlight : function(){
52012         this.el.removeClass("x-layout-panel-dragover");
52013     },
52014
52015     updateBox : function(box){
52016         this.box = box;
52017         if(!this.collapsed){
52018             this.el.dom.style.left = box.x + "px";
52019             this.el.dom.style.top = box.y + "px";
52020             this.updateBody(box.width, box.height);
52021         }else{
52022             this.collapsedEl.dom.style.left = box.x + "px";
52023             this.collapsedEl.dom.style.top = box.y + "px";
52024             this.collapsedEl.setSize(box.width, box.height);
52025         }
52026         if(this.tabs){
52027             this.tabs.autoSizeTabs();
52028         }
52029     },
52030
52031     updateBody : function(w, h){
52032         if(w !== null){
52033             this.el.setWidth(w);
52034             w -= this.el.getBorderWidth("rl");
52035             if(this.config.adjustments){
52036                 w += this.config.adjustments[0];
52037             }
52038         }
52039         if(h !== null){
52040             this.el.setHeight(h);
52041             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52042             h -= this.el.getBorderWidth("tb");
52043             if(this.config.adjustments){
52044                 h += this.config.adjustments[1];
52045             }
52046             this.bodyEl.setHeight(h);
52047             if(this.tabs){
52048                 h = this.tabs.syncHeight(h);
52049             }
52050         }
52051         if(this.panelSize){
52052             w = w !== null ? w : this.panelSize.width;
52053             h = h !== null ? h : this.panelSize.height;
52054         }
52055         if(this.activePanel){
52056             var el = this.activePanel.getEl();
52057             w = w !== null ? w : el.getWidth();
52058             h = h !== null ? h : el.getHeight();
52059             this.panelSize = {width: w, height: h};
52060             this.activePanel.setSize(w, h);
52061         }
52062         if(Roo.isIE && this.tabs){
52063             this.tabs.el.repaint();
52064         }
52065     },
52066
52067     /**
52068      * Returns the container element for this region.
52069      * @return {Roo.Element}
52070      */
52071     getEl : function(){
52072         return this.el;
52073     },
52074
52075     /**
52076      * Hides this region.
52077      */
52078     hide : function(){
52079         if(!this.collapsed){
52080             this.el.dom.style.left = "-2000px";
52081             this.el.hide();
52082         }else{
52083             this.collapsedEl.dom.style.left = "-2000px";
52084             this.collapsedEl.hide();
52085         }
52086         this.visible = false;
52087         this.fireEvent("visibilitychange", this, false);
52088     },
52089
52090     /**
52091      * Shows this region if it was previously hidden.
52092      */
52093     show : function(){
52094         if(!this.collapsed){
52095             this.el.show();
52096         }else{
52097             this.collapsedEl.show();
52098         }
52099         this.visible = true;
52100         this.fireEvent("visibilitychange", this, true);
52101     },
52102
52103     closeClicked : function(){
52104         if(this.activePanel){
52105             this.remove(this.activePanel);
52106         }
52107     },
52108
52109     collapseClick : function(e){
52110         if(this.isSlid){
52111            e.stopPropagation();
52112            this.slideIn();
52113         }else{
52114            e.stopPropagation();
52115            this.slideOut();
52116         }
52117     },
52118
52119     /**
52120      * Collapses this region.
52121      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52122      */
52123     collapse : function(skipAnim, skipCheck = false){
52124         if(this.collapsed) {
52125             return;
52126         }
52127         
52128         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52129             
52130             this.collapsed = true;
52131             if(this.split){
52132                 this.split.el.hide();
52133             }
52134             if(this.config.animate && skipAnim !== true){
52135                 this.fireEvent("invalidated", this);
52136                 this.animateCollapse();
52137             }else{
52138                 this.el.setLocation(-20000,-20000);
52139                 this.el.hide();
52140                 this.collapsedEl.show();
52141                 this.fireEvent("collapsed", this);
52142                 this.fireEvent("invalidated", this);
52143             }
52144         }
52145         
52146     },
52147
52148     animateCollapse : function(){
52149         // overridden
52150     },
52151
52152     /**
52153      * Expands this region if it was previously collapsed.
52154      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52155      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52156      */
52157     expand : function(e, skipAnim){
52158         if(e) {
52159             e.stopPropagation();
52160         }
52161         if(!this.collapsed || this.el.hasActiveFx()) {
52162             return;
52163         }
52164         if(this.isSlid){
52165             this.afterSlideIn();
52166             skipAnim = true;
52167         }
52168         this.collapsed = false;
52169         if(this.config.animate && skipAnim !== true){
52170             this.animateExpand();
52171         }else{
52172             this.el.show();
52173             if(this.split){
52174                 this.split.el.show();
52175             }
52176             this.collapsedEl.setLocation(-2000,-2000);
52177             this.collapsedEl.hide();
52178             this.fireEvent("invalidated", this);
52179             this.fireEvent("expanded", this);
52180         }
52181     },
52182
52183     animateExpand : function(){
52184         // overridden
52185     },
52186
52187     initTabs : function()
52188     {
52189         this.bodyEl.setStyle("overflow", "hidden");
52190         var ts = new Roo.TabPanel(
52191                 this.bodyEl.dom,
52192                 {
52193                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52194                     disableTooltips: this.config.disableTabTips,
52195                     toolbar : this.config.toolbar
52196                 }
52197         );
52198         if(this.config.hideTabs){
52199             ts.stripWrap.setDisplayed(false);
52200         }
52201         this.tabs = ts;
52202         ts.resizeTabs = this.config.resizeTabs === true;
52203         ts.minTabWidth = this.config.minTabWidth || 40;
52204         ts.maxTabWidth = this.config.maxTabWidth || 250;
52205         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52206         ts.monitorResize = false;
52207         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52208         ts.bodyEl.addClass('x-layout-tabs-body');
52209         this.panels.each(this.initPanelAsTab, this);
52210     },
52211
52212     initPanelAsTab : function(panel){
52213         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52214                     this.config.closeOnTab && panel.isClosable());
52215         if(panel.tabTip !== undefined){
52216             ti.setTooltip(panel.tabTip);
52217         }
52218         ti.on("activate", function(){
52219               this.setActivePanel(panel);
52220         }, this);
52221         if(this.config.closeOnTab){
52222             ti.on("beforeclose", function(t, e){
52223                 e.cancel = true;
52224                 this.remove(panel);
52225             }, this);
52226         }
52227         return ti;
52228     },
52229
52230     updatePanelTitle : function(panel, title){
52231         if(this.activePanel == panel){
52232             this.updateTitle(title);
52233         }
52234         if(this.tabs){
52235             var ti = this.tabs.getTab(panel.getEl().id);
52236             ti.setText(title);
52237             if(panel.tabTip !== undefined){
52238                 ti.setTooltip(panel.tabTip);
52239             }
52240         }
52241     },
52242
52243     updateTitle : function(title){
52244         if(this.titleTextEl && !this.config.title){
52245             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52246         }
52247     },
52248
52249     setActivePanel : function(panel){
52250         panel = this.getPanel(panel);
52251         if(this.activePanel && this.activePanel != panel){
52252             this.activePanel.setActiveState(false);
52253         }
52254         this.activePanel = panel;
52255         panel.setActiveState(true);
52256         if(this.panelSize){
52257             panel.setSize(this.panelSize.width, this.panelSize.height);
52258         }
52259         if(this.closeBtn){
52260             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52261         }
52262         this.updateTitle(panel.getTitle());
52263         if(this.tabs){
52264             this.fireEvent("invalidated", this);
52265         }
52266         this.fireEvent("panelactivated", this, panel);
52267     },
52268
52269     /**
52270      * Shows the specified panel.
52271      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52272      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52273      */
52274     showPanel : function(panel)
52275     {
52276         panel = this.getPanel(panel);
52277         if(panel){
52278             if(this.tabs){
52279                 var tab = this.tabs.getTab(panel.getEl().id);
52280                 if(tab.isHidden()){
52281                     this.tabs.unhideTab(tab.id);
52282                 }
52283                 tab.activate();
52284             }else{
52285                 this.setActivePanel(panel);
52286             }
52287         }
52288         return panel;
52289     },
52290
52291     /**
52292      * Get the active panel for this region.
52293      * @return {Roo.ContentPanel} The active panel or null
52294      */
52295     getActivePanel : function(){
52296         return this.activePanel;
52297     },
52298
52299     validateVisibility : function(){
52300         if(this.panels.getCount() < 1){
52301             this.updateTitle("&#160;");
52302             this.closeBtn.hide();
52303             this.hide();
52304         }else{
52305             if(!this.isVisible()){
52306                 this.show();
52307             }
52308         }
52309     },
52310
52311     /**
52312      * Adds the passed ContentPanel(s) to this region.
52313      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52314      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52315      */
52316     add : function(panel){
52317         if(arguments.length > 1){
52318             for(var i = 0, len = arguments.length; i < len; i++) {
52319                 this.add(arguments[i]);
52320             }
52321             return null;
52322         }
52323         if(this.hasPanel(panel)){
52324             this.showPanel(panel);
52325             return panel;
52326         }
52327         panel.setRegion(this);
52328         this.panels.add(panel);
52329         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52330             this.bodyEl.dom.appendChild(panel.getEl().dom);
52331             if(panel.background !== true){
52332                 this.setActivePanel(panel);
52333             }
52334             this.fireEvent("paneladded", this, panel);
52335             return panel;
52336         }
52337         if(!this.tabs){
52338             this.initTabs();
52339         }else{
52340             this.initPanelAsTab(panel);
52341         }
52342         if(panel.background !== true){
52343             this.tabs.activate(panel.getEl().id);
52344         }
52345         this.fireEvent("paneladded", this, panel);
52346         return panel;
52347     },
52348
52349     /**
52350      * Hides the tab for the specified panel.
52351      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52352      */
52353     hidePanel : function(panel){
52354         if(this.tabs && (panel = this.getPanel(panel))){
52355             this.tabs.hideTab(panel.getEl().id);
52356         }
52357     },
52358
52359     /**
52360      * Unhides the tab for a previously hidden panel.
52361      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52362      */
52363     unhidePanel : function(panel){
52364         if(this.tabs && (panel = this.getPanel(panel))){
52365             this.tabs.unhideTab(panel.getEl().id);
52366         }
52367     },
52368
52369     clearPanels : function(){
52370         while(this.panels.getCount() > 0){
52371              this.remove(this.panels.first());
52372         }
52373     },
52374
52375     /**
52376      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52377      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52378      * @param {Boolean} preservePanel Overrides the config preservePanel option
52379      * @return {Roo.ContentPanel} The panel that was removed
52380      */
52381     remove : function(panel, preservePanel){
52382         panel = this.getPanel(panel);
52383         if(!panel){
52384             return null;
52385         }
52386         var e = {};
52387         this.fireEvent("beforeremove", this, panel, e);
52388         if(e.cancel === true){
52389             return null;
52390         }
52391         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52392         var panelId = panel.getId();
52393         this.panels.removeKey(panelId);
52394         if(preservePanel){
52395             document.body.appendChild(panel.getEl().dom);
52396         }
52397         if(this.tabs){
52398             this.tabs.removeTab(panel.getEl().id);
52399         }else if (!preservePanel){
52400             this.bodyEl.dom.removeChild(panel.getEl().dom);
52401         }
52402         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52403             var p = this.panels.first();
52404             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52405             tempEl.appendChild(p.getEl().dom);
52406             this.bodyEl.update("");
52407             this.bodyEl.dom.appendChild(p.getEl().dom);
52408             tempEl = null;
52409             this.updateTitle(p.getTitle());
52410             this.tabs = null;
52411             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52412             this.setActivePanel(p);
52413         }
52414         panel.setRegion(null);
52415         if(this.activePanel == panel){
52416             this.activePanel = null;
52417         }
52418         if(this.config.autoDestroy !== false && preservePanel !== true){
52419             try{panel.destroy();}catch(e){}
52420         }
52421         this.fireEvent("panelremoved", this, panel);
52422         return panel;
52423     },
52424
52425     /**
52426      * Returns the TabPanel component used by this region
52427      * @return {Roo.TabPanel}
52428      */
52429     getTabs : function(){
52430         return this.tabs;
52431     },
52432
52433     createTool : function(parentEl, className){
52434         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52435             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52436         btn.addClassOnOver("x-layout-tools-button-over");
52437         return btn;
52438     }
52439 });/*
52440  * Based on:
52441  * Ext JS Library 1.1.1
52442  * Copyright(c) 2006-2007, Ext JS, LLC.
52443  *
52444  * Originally Released Under LGPL - original licence link has changed is not relivant.
52445  *
52446  * Fork - LGPL
52447  * <script type="text/javascript">
52448  */
52449  
52450
52451
52452 /**
52453  * @class Roo.SplitLayoutRegion
52454  * @extends Roo.LayoutRegion
52455  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52456  */
52457 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52458     this.cursor = cursor;
52459     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52460 };
52461
52462 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52463     splitTip : "Drag to resize.",
52464     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52465     useSplitTips : false,
52466
52467     applyConfig : function(config){
52468         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52469         if(config.split){
52470             if(!this.split){
52471                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52472                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52473                 /** The SplitBar for this region 
52474                 * @type Roo.SplitBar */
52475                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52476                 this.split.on("moved", this.onSplitMove, this);
52477                 this.split.useShim = config.useShim === true;
52478                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52479                 if(this.useSplitTips){
52480                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52481                 }
52482                 if(config.collapsible){
52483                     this.split.el.on("dblclick", this.collapse,  this);
52484                 }
52485             }
52486             if(typeof config.minSize != "undefined"){
52487                 this.split.minSize = config.minSize;
52488             }
52489             if(typeof config.maxSize != "undefined"){
52490                 this.split.maxSize = config.maxSize;
52491             }
52492             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52493                 this.hideSplitter();
52494             }
52495         }
52496     },
52497
52498     getHMaxSize : function(){
52499          var cmax = this.config.maxSize || 10000;
52500          var center = this.mgr.getRegion("center");
52501          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52502     },
52503
52504     getVMaxSize : function(){
52505          var cmax = this.config.maxSize || 10000;
52506          var center = this.mgr.getRegion("center");
52507          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52508     },
52509
52510     onSplitMove : function(split, newSize){
52511         this.fireEvent("resized", this, newSize);
52512     },
52513     
52514     /** 
52515      * Returns the {@link Roo.SplitBar} for this region.
52516      * @return {Roo.SplitBar}
52517      */
52518     getSplitBar : function(){
52519         return this.split;
52520     },
52521     
52522     hide : function(){
52523         this.hideSplitter();
52524         Roo.SplitLayoutRegion.superclass.hide.call(this);
52525     },
52526
52527     hideSplitter : function(){
52528         if(this.split){
52529             this.split.el.setLocation(-2000,-2000);
52530             this.split.el.hide();
52531         }
52532     },
52533
52534     show : function(){
52535         if(this.split){
52536             this.split.el.show();
52537         }
52538         Roo.SplitLayoutRegion.superclass.show.call(this);
52539     },
52540     
52541     beforeSlide: function(){
52542         if(Roo.isGecko){// firefox overflow auto bug workaround
52543             this.bodyEl.clip();
52544             if(this.tabs) {
52545                 this.tabs.bodyEl.clip();
52546             }
52547             if(this.activePanel){
52548                 this.activePanel.getEl().clip();
52549                 
52550                 if(this.activePanel.beforeSlide){
52551                     this.activePanel.beforeSlide();
52552                 }
52553             }
52554         }
52555     },
52556     
52557     afterSlide : function(){
52558         if(Roo.isGecko){// firefox overflow auto bug workaround
52559             this.bodyEl.unclip();
52560             if(this.tabs) {
52561                 this.tabs.bodyEl.unclip();
52562             }
52563             if(this.activePanel){
52564                 this.activePanel.getEl().unclip();
52565                 if(this.activePanel.afterSlide){
52566                     this.activePanel.afterSlide();
52567                 }
52568             }
52569         }
52570     },
52571
52572     initAutoHide : function(){
52573         if(this.autoHide !== false){
52574             if(!this.autoHideHd){
52575                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52576                 this.autoHideHd = {
52577                     "mouseout": function(e){
52578                         if(!e.within(this.el, true)){
52579                             st.delay(500);
52580                         }
52581                     },
52582                     "mouseover" : function(e){
52583                         st.cancel();
52584                     },
52585                     scope : this
52586                 };
52587             }
52588             this.el.on(this.autoHideHd);
52589         }
52590     },
52591
52592     clearAutoHide : function(){
52593         if(this.autoHide !== false){
52594             this.el.un("mouseout", this.autoHideHd.mouseout);
52595             this.el.un("mouseover", this.autoHideHd.mouseover);
52596         }
52597     },
52598
52599     clearMonitor : function(){
52600         Roo.get(document).un("click", this.slideInIf, this);
52601     },
52602
52603     // these names are backwards but not changed for compat
52604     slideOut : function(){
52605         if(this.isSlid || this.el.hasActiveFx()){
52606             return;
52607         }
52608         this.isSlid = true;
52609         if(this.collapseBtn){
52610             this.collapseBtn.hide();
52611         }
52612         this.closeBtnState = this.closeBtn.getStyle('display');
52613         this.closeBtn.hide();
52614         if(this.stickBtn){
52615             this.stickBtn.show();
52616         }
52617         this.el.show();
52618         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52619         this.beforeSlide();
52620         this.el.setStyle("z-index", 10001);
52621         this.el.slideIn(this.getSlideAnchor(), {
52622             callback: function(){
52623                 this.afterSlide();
52624                 this.initAutoHide();
52625                 Roo.get(document).on("click", this.slideInIf, this);
52626                 this.fireEvent("slideshow", this);
52627             },
52628             scope: this,
52629             block: true
52630         });
52631     },
52632
52633     afterSlideIn : function(){
52634         this.clearAutoHide();
52635         this.isSlid = false;
52636         this.clearMonitor();
52637         this.el.setStyle("z-index", "");
52638         if(this.collapseBtn){
52639             this.collapseBtn.show();
52640         }
52641         this.closeBtn.setStyle('display', this.closeBtnState);
52642         if(this.stickBtn){
52643             this.stickBtn.hide();
52644         }
52645         this.fireEvent("slidehide", this);
52646     },
52647
52648     slideIn : function(cb){
52649         if(!this.isSlid || this.el.hasActiveFx()){
52650             Roo.callback(cb);
52651             return;
52652         }
52653         this.isSlid = false;
52654         this.beforeSlide();
52655         this.el.slideOut(this.getSlideAnchor(), {
52656             callback: function(){
52657                 this.el.setLeftTop(-10000, -10000);
52658                 this.afterSlide();
52659                 this.afterSlideIn();
52660                 Roo.callback(cb);
52661             },
52662             scope: this,
52663             block: true
52664         });
52665     },
52666     
52667     slideInIf : function(e){
52668         if(!e.within(this.el)){
52669             this.slideIn();
52670         }
52671     },
52672
52673     animateCollapse : function(){
52674         this.beforeSlide();
52675         this.el.setStyle("z-index", 20000);
52676         var anchor = this.getSlideAnchor();
52677         this.el.slideOut(anchor, {
52678             callback : function(){
52679                 this.el.setStyle("z-index", "");
52680                 this.collapsedEl.slideIn(anchor, {duration:.3});
52681                 this.afterSlide();
52682                 this.el.setLocation(-10000,-10000);
52683                 this.el.hide();
52684                 this.fireEvent("collapsed", this);
52685             },
52686             scope: this,
52687             block: true
52688         });
52689     },
52690
52691     animateExpand : function(){
52692         this.beforeSlide();
52693         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52694         this.el.setStyle("z-index", 20000);
52695         this.collapsedEl.hide({
52696             duration:.1
52697         });
52698         this.el.slideIn(this.getSlideAnchor(), {
52699             callback : function(){
52700                 this.el.setStyle("z-index", "");
52701                 this.afterSlide();
52702                 if(this.split){
52703                     this.split.el.show();
52704                 }
52705                 this.fireEvent("invalidated", this);
52706                 this.fireEvent("expanded", this);
52707             },
52708             scope: this,
52709             block: true
52710         });
52711     },
52712
52713     anchors : {
52714         "west" : "left",
52715         "east" : "right",
52716         "north" : "top",
52717         "south" : "bottom"
52718     },
52719
52720     sanchors : {
52721         "west" : "l",
52722         "east" : "r",
52723         "north" : "t",
52724         "south" : "b"
52725     },
52726
52727     canchors : {
52728         "west" : "tl-tr",
52729         "east" : "tr-tl",
52730         "north" : "tl-bl",
52731         "south" : "bl-tl"
52732     },
52733
52734     getAnchor : function(){
52735         return this.anchors[this.position];
52736     },
52737
52738     getCollapseAnchor : function(){
52739         return this.canchors[this.position];
52740     },
52741
52742     getSlideAnchor : function(){
52743         return this.sanchors[this.position];
52744     },
52745
52746     getAlignAdj : function(){
52747         var cm = this.cmargins;
52748         switch(this.position){
52749             case "west":
52750                 return [0, 0];
52751             break;
52752             case "east":
52753                 return [0, 0];
52754             break;
52755             case "north":
52756                 return [0, 0];
52757             break;
52758             case "south":
52759                 return [0, 0];
52760             break;
52761         }
52762     },
52763
52764     getExpandAdj : function(){
52765         var c = this.collapsedEl, cm = this.cmargins;
52766         switch(this.position){
52767             case "west":
52768                 return [-(cm.right+c.getWidth()+cm.left), 0];
52769             break;
52770             case "east":
52771                 return [cm.right+c.getWidth()+cm.left, 0];
52772             break;
52773             case "north":
52774                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52775             break;
52776             case "south":
52777                 return [0, cm.top+cm.bottom+c.getHeight()];
52778             break;
52779         }
52780     }
52781 });/*
52782  * Based on:
52783  * Ext JS Library 1.1.1
52784  * Copyright(c) 2006-2007, Ext JS, LLC.
52785  *
52786  * Originally Released Under LGPL - original licence link has changed is not relivant.
52787  *
52788  * Fork - LGPL
52789  * <script type="text/javascript">
52790  */
52791 /*
52792  * These classes are private internal classes
52793  */
52794 Roo.CenterLayoutRegion = function(mgr, config){
52795     Roo.LayoutRegion.call(this, mgr, config, "center");
52796     this.visible = true;
52797     this.minWidth = config.minWidth || 20;
52798     this.minHeight = config.minHeight || 20;
52799 };
52800
52801 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52802     hide : function(){
52803         // center panel can't be hidden
52804     },
52805     
52806     show : function(){
52807         // center panel can't be hidden
52808     },
52809     
52810     getMinWidth: function(){
52811         return this.minWidth;
52812     },
52813     
52814     getMinHeight: function(){
52815         return this.minHeight;
52816     }
52817 });
52818
52819
52820 Roo.NorthLayoutRegion = function(mgr, config){
52821     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52822     if(this.split){
52823         this.split.placement = Roo.SplitBar.TOP;
52824         this.split.orientation = Roo.SplitBar.VERTICAL;
52825         this.split.el.addClass("x-layout-split-v");
52826     }
52827     var size = config.initialSize || config.height;
52828     if(typeof size != "undefined"){
52829         this.el.setHeight(size);
52830     }
52831 };
52832 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52833     orientation: Roo.SplitBar.VERTICAL,
52834     getBox : function(){
52835         if(this.collapsed){
52836             return this.collapsedEl.getBox();
52837         }
52838         var box = this.el.getBox();
52839         if(this.split){
52840             box.height += this.split.el.getHeight();
52841         }
52842         return box;
52843     },
52844     
52845     updateBox : function(box){
52846         if(this.split && !this.collapsed){
52847             box.height -= this.split.el.getHeight();
52848             this.split.el.setLeft(box.x);
52849             this.split.el.setTop(box.y+box.height);
52850             this.split.el.setWidth(box.width);
52851         }
52852         if(this.collapsed){
52853             this.updateBody(box.width, null);
52854         }
52855         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52856     }
52857 });
52858
52859 Roo.SouthLayoutRegion = function(mgr, config){
52860     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52861     if(this.split){
52862         this.split.placement = Roo.SplitBar.BOTTOM;
52863         this.split.orientation = Roo.SplitBar.VERTICAL;
52864         this.split.el.addClass("x-layout-split-v");
52865     }
52866     var size = config.initialSize || config.height;
52867     if(typeof size != "undefined"){
52868         this.el.setHeight(size);
52869     }
52870 };
52871 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52872     orientation: Roo.SplitBar.VERTICAL,
52873     getBox : function(){
52874         if(this.collapsed){
52875             return this.collapsedEl.getBox();
52876         }
52877         var box = this.el.getBox();
52878         if(this.split){
52879             var sh = this.split.el.getHeight();
52880             box.height += sh;
52881             box.y -= sh;
52882         }
52883         return box;
52884     },
52885     
52886     updateBox : function(box){
52887         if(this.split && !this.collapsed){
52888             var sh = this.split.el.getHeight();
52889             box.height -= sh;
52890             box.y += sh;
52891             this.split.el.setLeft(box.x);
52892             this.split.el.setTop(box.y-sh);
52893             this.split.el.setWidth(box.width);
52894         }
52895         if(this.collapsed){
52896             this.updateBody(box.width, null);
52897         }
52898         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52899     }
52900 });
52901
52902 Roo.EastLayoutRegion = function(mgr, config){
52903     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52904     if(this.split){
52905         this.split.placement = Roo.SplitBar.RIGHT;
52906         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52907         this.split.el.addClass("x-layout-split-h");
52908     }
52909     var size = config.initialSize || config.width;
52910     if(typeof size != "undefined"){
52911         this.el.setWidth(size);
52912     }
52913 };
52914 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52915     orientation: Roo.SplitBar.HORIZONTAL,
52916     getBox : function(){
52917         if(this.collapsed){
52918             return this.collapsedEl.getBox();
52919         }
52920         var box = this.el.getBox();
52921         if(this.split){
52922             var sw = this.split.el.getWidth();
52923             box.width += sw;
52924             box.x -= sw;
52925         }
52926         return box;
52927     },
52928
52929     updateBox : function(box){
52930         if(this.split && !this.collapsed){
52931             var sw = this.split.el.getWidth();
52932             box.width -= sw;
52933             this.split.el.setLeft(box.x);
52934             this.split.el.setTop(box.y);
52935             this.split.el.setHeight(box.height);
52936             box.x += sw;
52937         }
52938         if(this.collapsed){
52939             this.updateBody(null, box.height);
52940         }
52941         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52942     }
52943 });
52944
52945 Roo.WestLayoutRegion = function(mgr, config){
52946     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52947     if(this.split){
52948         this.split.placement = Roo.SplitBar.LEFT;
52949         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52950         this.split.el.addClass("x-layout-split-h");
52951     }
52952     var size = config.initialSize || config.width;
52953     if(typeof size != "undefined"){
52954         this.el.setWidth(size);
52955     }
52956 };
52957 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52958     orientation: Roo.SplitBar.HORIZONTAL,
52959     getBox : function(){
52960         if(this.collapsed){
52961             return this.collapsedEl.getBox();
52962         }
52963         var box = this.el.getBox();
52964         if(this.split){
52965             box.width += this.split.el.getWidth();
52966         }
52967         return box;
52968     },
52969     
52970     updateBox : function(box){
52971         if(this.split && !this.collapsed){
52972             var sw = this.split.el.getWidth();
52973             box.width -= sw;
52974             this.split.el.setLeft(box.x+box.width);
52975             this.split.el.setTop(box.y);
52976             this.split.el.setHeight(box.height);
52977         }
52978         if(this.collapsed){
52979             this.updateBody(null, box.height);
52980         }
52981         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52982     }
52983 });
52984 /*
52985  * Based on:
52986  * Ext JS Library 1.1.1
52987  * Copyright(c) 2006-2007, Ext JS, LLC.
52988  *
52989  * Originally Released Under LGPL - original licence link has changed is not relivant.
52990  *
52991  * Fork - LGPL
52992  * <script type="text/javascript">
52993  */
52994  
52995  
52996 /*
52997  * Private internal class for reading and applying state
52998  */
52999 Roo.LayoutStateManager = function(layout){
53000      // default empty state
53001      this.state = {
53002         north: {},
53003         south: {},
53004         east: {},
53005         west: {}       
53006     };
53007 };
53008
53009 Roo.LayoutStateManager.prototype = {
53010     init : function(layout, provider){
53011         this.provider = provider;
53012         var state = provider.get(layout.id+"-layout-state");
53013         if(state){
53014             var wasUpdating = layout.isUpdating();
53015             if(!wasUpdating){
53016                 layout.beginUpdate();
53017             }
53018             for(var key in state){
53019                 if(typeof state[key] != "function"){
53020                     var rstate = state[key];
53021                     var r = layout.getRegion(key);
53022                     if(r && rstate){
53023                         if(rstate.size){
53024                             r.resizeTo(rstate.size);
53025                         }
53026                         if(rstate.collapsed == true){
53027                             r.collapse(true);
53028                         }else{
53029                             r.expand(null, true);
53030                         }
53031                     }
53032                 }
53033             }
53034             if(!wasUpdating){
53035                 layout.endUpdate();
53036             }
53037             this.state = state; 
53038         }
53039         this.layout = layout;
53040         layout.on("regionresized", this.onRegionResized, this);
53041         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53042         layout.on("regionexpanded", this.onRegionExpanded, this);
53043     },
53044     
53045     storeState : function(){
53046         this.provider.set(this.layout.id+"-layout-state", this.state);
53047     },
53048     
53049     onRegionResized : function(region, newSize){
53050         this.state[region.getPosition()].size = newSize;
53051         this.storeState();
53052     },
53053     
53054     onRegionCollapsed : function(region){
53055         this.state[region.getPosition()].collapsed = true;
53056         this.storeState();
53057     },
53058     
53059     onRegionExpanded : function(region){
53060         this.state[region.getPosition()].collapsed = false;
53061         this.storeState();
53062     }
53063 };/*
53064  * Based on:
53065  * Ext JS Library 1.1.1
53066  * Copyright(c) 2006-2007, Ext JS, LLC.
53067  *
53068  * Originally Released Under LGPL - original licence link has changed is not relivant.
53069  *
53070  * Fork - LGPL
53071  * <script type="text/javascript">
53072  */
53073 /**
53074  * @class Roo.ContentPanel
53075  * @extends Roo.util.Observable
53076  * A basic ContentPanel element.
53077  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53078  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53079  * @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
53080  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53081  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53082  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53083  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53084  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53085  * @cfg {String} title          The title for this panel
53086  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53087  * @cfg {String} url            Calls {@link #setUrl} with this value
53088  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53089  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53090  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53091  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53092
53093  * @constructor
53094  * Create a new ContentPanel.
53095  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53096  * @param {String/Object} config A string to set only the title or a config object
53097  * @param {String} content (optional) Set the HTML content for this panel
53098  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53099  */
53100 Roo.ContentPanel = function(el, config, content){
53101     
53102      
53103     /*
53104     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53105         config = el;
53106         el = Roo.id();
53107     }
53108     if (config && config.parentLayout) { 
53109         el = config.parentLayout.el.createChild(); 
53110     }
53111     */
53112     if(el.autoCreate){ // xtype is available if this is called from factory
53113         config = el;
53114         el = Roo.id();
53115     }
53116     this.el = Roo.get(el);
53117     if(!this.el && config && config.autoCreate){
53118         if(typeof config.autoCreate == "object"){
53119             if(!config.autoCreate.id){
53120                 config.autoCreate.id = config.id||el;
53121             }
53122             this.el = Roo.DomHelper.append(document.body,
53123                         config.autoCreate, true);
53124         }else{
53125             this.el = Roo.DomHelper.append(document.body,
53126                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53127         }
53128     }
53129     this.closable = false;
53130     this.loaded = false;
53131     this.active = false;
53132     if(typeof config == "string"){
53133         this.title = config;
53134     }else{
53135         Roo.apply(this, config);
53136     }
53137     
53138     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53139         this.wrapEl = this.el.wrap();
53140         this.toolbar.container = this.el.insertSibling(false, 'before');
53141         this.toolbar = new Roo.Toolbar(this.toolbar);
53142     }
53143     
53144     // xtype created footer. - not sure if will work as we normally have to render first..
53145     if (this.footer && !this.footer.el && this.footer.xtype) {
53146         if (!this.wrapEl) {
53147             this.wrapEl = this.el.wrap();
53148         }
53149     
53150         this.footer.container = this.wrapEl.createChild();
53151          
53152         this.footer = Roo.factory(this.footer, Roo);
53153         
53154     }
53155     
53156     if(this.resizeEl){
53157         this.resizeEl = Roo.get(this.resizeEl, true);
53158     }else{
53159         this.resizeEl = this.el;
53160     }
53161     // handle view.xtype
53162     
53163  
53164     
53165     
53166     this.addEvents({
53167         /**
53168          * @event activate
53169          * Fires when this panel is activated. 
53170          * @param {Roo.ContentPanel} this
53171          */
53172         "activate" : true,
53173         /**
53174          * @event deactivate
53175          * Fires when this panel is activated. 
53176          * @param {Roo.ContentPanel} this
53177          */
53178         "deactivate" : true,
53179
53180         /**
53181          * @event resize
53182          * Fires when this panel is resized if fitToFrame is true.
53183          * @param {Roo.ContentPanel} this
53184          * @param {Number} width The width after any component adjustments
53185          * @param {Number} height The height after any component adjustments
53186          */
53187         "resize" : true,
53188         
53189          /**
53190          * @event render
53191          * Fires when this tab is created
53192          * @param {Roo.ContentPanel} this
53193          */
53194         "render" : true
53195         
53196         
53197         
53198     });
53199     
53200
53201     
53202     
53203     if(this.autoScroll){
53204         this.resizeEl.setStyle("overflow", "auto");
53205     } else {
53206         // fix randome scrolling
53207         this.el.on('scroll', function() {
53208             Roo.log('fix random scolling');
53209             this.scrollTo('top',0); 
53210         });
53211     }
53212     content = content || this.content;
53213     if(content){
53214         this.setContent(content);
53215     }
53216     if(config && config.url){
53217         this.setUrl(this.url, this.params, this.loadOnce);
53218     }
53219     
53220     
53221     
53222     Roo.ContentPanel.superclass.constructor.call(this);
53223     
53224     if (this.view && typeof(this.view.xtype) != 'undefined') {
53225         this.view.el = this.el.appendChild(document.createElement("div"));
53226         this.view = Roo.factory(this.view); 
53227         this.view.render  &&  this.view.render(false, '');  
53228     }
53229     
53230     
53231     this.fireEvent('render', this);
53232 };
53233
53234 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53235     tabTip:'',
53236     setRegion : function(region){
53237         this.region = region;
53238         if(region){
53239            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53240         }else{
53241            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53242         } 
53243     },
53244     
53245     /**
53246      * Returns the toolbar for this Panel if one was configured. 
53247      * @return {Roo.Toolbar} 
53248      */
53249     getToolbar : function(){
53250         return this.toolbar;
53251     },
53252     
53253     setActiveState : function(active){
53254         this.active = active;
53255         if(!active){
53256             this.fireEvent("deactivate", this);
53257         }else{
53258             this.fireEvent("activate", this);
53259         }
53260     },
53261     /**
53262      * Updates this panel's element
53263      * @param {String} content The new content
53264      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53265     */
53266     setContent : function(content, loadScripts){
53267         this.el.update(content, loadScripts);
53268     },
53269
53270     ignoreResize : function(w, h){
53271         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53272             return true;
53273         }else{
53274             this.lastSize = {width: w, height: h};
53275             return false;
53276         }
53277     },
53278     /**
53279      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53280      * @return {Roo.UpdateManager} The UpdateManager
53281      */
53282     getUpdateManager : function(){
53283         return this.el.getUpdateManager();
53284     },
53285      /**
53286      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53287      * @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:
53288 <pre><code>
53289 panel.load({
53290     url: "your-url.php",
53291     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53292     callback: yourFunction,
53293     scope: yourObject, //(optional scope)
53294     discardUrl: false,
53295     nocache: false,
53296     text: "Loading...",
53297     timeout: 30,
53298     scripts: false
53299 });
53300 </code></pre>
53301      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53302      * 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.
53303      * @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}
53304      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53305      * @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.
53306      * @return {Roo.ContentPanel} this
53307      */
53308     load : function(){
53309         var um = this.el.getUpdateManager();
53310         um.update.apply(um, arguments);
53311         return this;
53312     },
53313
53314
53315     /**
53316      * 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.
53317      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53318      * @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)
53319      * @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)
53320      * @return {Roo.UpdateManager} The UpdateManager
53321      */
53322     setUrl : function(url, params, loadOnce){
53323         if(this.refreshDelegate){
53324             this.removeListener("activate", this.refreshDelegate);
53325         }
53326         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53327         this.on("activate", this.refreshDelegate);
53328         return this.el.getUpdateManager();
53329     },
53330     
53331     _handleRefresh : function(url, params, loadOnce){
53332         if(!loadOnce || !this.loaded){
53333             var updater = this.el.getUpdateManager();
53334             updater.update(url, params, this._setLoaded.createDelegate(this));
53335         }
53336     },
53337     
53338     _setLoaded : function(){
53339         this.loaded = true;
53340     }, 
53341     
53342     /**
53343      * Returns this panel's id
53344      * @return {String} 
53345      */
53346     getId : function(){
53347         return this.el.id;
53348     },
53349     
53350     /** 
53351      * Returns this panel's element - used by regiosn to add.
53352      * @return {Roo.Element} 
53353      */
53354     getEl : function(){
53355         return this.wrapEl || this.el;
53356     },
53357     
53358     adjustForComponents : function(width, height)
53359     {
53360         //Roo.log('adjustForComponents ');
53361         if(this.resizeEl != this.el){
53362             width -= this.el.getFrameWidth('lr');
53363             height -= this.el.getFrameWidth('tb');
53364         }
53365         if(this.toolbar){
53366             var te = this.toolbar.getEl();
53367             height -= te.getHeight();
53368             te.setWidth(width);
53369         }
53370         if(this.footer){
53371             var te = this.footer.getEl();
53372             Roo.log("footer:" + te.getHeight());
53373             
53374             height -= te.getHeight();
53375             te.setWidth(width);
53376         }
53377         
53378         
53379         if(this.adjustments){
53380             width += this.adjustments[0];
53381             height += this.adjustments[1];
53382         }
53383         return {"width": width, "height": height};
53384     },
53385     
53386     setSize : function(width, height){
53387         if(this.fitToFrame && !this.ignoreResize(width, height)){
53388             if(this.fitContainer && this.resizeEl != this.el){
53389                 this.el.setSize(width, height);
53390             }
53391             var size = this.adjustForComponents(width, height);
53392             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53393             this.fireEvent('resize', this, size.width, size.height);
53394         }
53395     },
53396     
53397     /**
53398      * Returns this panel's title
53399      * @return {String} 
53400      */
53401     getTitle : function(){
53402         return this.title;
53403     },
53404     
53405     /**
53406      * Set this panel's title
53407      * @param {String} title
53408      */
53409     setTitle : function(title){
53410         this.title = title;
53411         if(this.region){
53412             this.region.updatePanelTitle(this, title);
53413         }
53414     },
53415     
53416     /**
53417      * Returns true is this panel was configured to be closable
53418      * @return {Boolean} 
53419      */
53420     isClosable : function(){
53421         return this.closable;
53422     },
53423     
53424     beforeSlide : function(){
53425         this.el.clip();
53426         this.resizeEl.clip();
53427     },
53428     
53429     afterSlide : function(){
53430         this.el.unclip();
53431         this.resizeEl.unclip();
53432     },
53433     
53434     /**
53435      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53436      *   Will fail silently if the {@link #setUrl} method has not been called.
53437      *   This does not activate the panel, just updates its content.
53438      */
53439     refresh : function(){
53440         if(this.refreshDelegate){
53441            this.loaded = false;
53442            this.refreshDelegate();
53443         }
53444     },
53445     
53446     /**
53447      * Destroys this panel
53448      */
53449     destroy : function(){
53450         this.el.removeAllListeners();
53451         var tempEl = document.createElement("span");
53452         tempEl.appendChild(this.el.dom);
53453         tempEl.innerHTML = "";
53454         this.el.remove();
53455         this.el = null;
53456     },
53457     
53458     /**
53459      * form - if the content panel contains a form - this is a reference to it.
53460      * @type {Roo.form.Form}
53461      */
53462     form : false,
53463     /**
53464      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53465      *    This contains a reference to it.
53466      * @type {Roo.View}
53467      */
53468     view : false,
53469     
53470       /**
53471      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53472      * <pre><code>
53473
53474 layout.addxtype({
53475        xtype : 'Form',
53476        items: [ .... ]
53477    }
53478 );
53479
53480 </code></pre>
53481      * @param {Object} cfg Xtype definition of item to add.
53482      */
53483     
53484     addxtype : function(cfg) {
53485         // add form..
53486         if (cfg.xtype.match(/^Form$/)) {
53487             
53488             var el;
53489             //if (this.footer) {
53490             //    el = this.footer.container.insertSibling(false, 'before');
53491             //} else {
53492                 el = this.el.createChild();
53493             //}
53494
53495             this.form = new  Roo.form.Form(cfg);
53496             
53497             
53498             if ( this.form.allItems.length) {
53499                 this.form.render(el.dom);
53500             }
53501             return this.form;
53502         }
53503         // should only have one of theses..
53504         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53505             // views.. should not be just added - used named prop 'view''
53506             
53507             cfg.el = this.el.appendChild(document.createElement("div"));
53508             // factory?
53509             
53510             var ret = new Roo.factory(cfg);
53511              
53512              ret.render && ret.render(false, ''); // render blank..
53513             this.view = ret;
53514             return ret;
53515         }
53516         return false;
53517     }
53518 });
53519
53520 /**
53521  * @class Roo.GridPanel
53522  * @extends Roo.ContentPanel
53523  * @constructor
53524  * Create a new GridPanel.
53525  * @param {Roo.grid.Grid} grid The grid for this panel
53526  * @param {String/Object} config A string to set only the panel's title, or a config object
53527  */
53528 Roo.GridPanel = function(grid, config){
53529     
53530   
53531     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53532         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53533         
53534     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53535     
53536     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53537     
53538     if(this.toolbar){
53539         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53540     }
53541     // xtype created footer. - not sure if will work as we normally have to render first..
53542     if (this.footer && !this.footer.el && this.footer.xtype) {
53543         
53544         this.footer.container = this.grid.getView().getFooterPanel(true);
53545         this.footer.dataSource = this.grid.dataSource;
53546         this.footer = Roo.factory(this.footer, Roo);
53547         
53548     }
53549     
53550     grid.monitorWindowResize = false; // turn off autosizing
53551     grid.autoHeight = false;
53552     grid.autoWidth = false;
53553     this.grid = grid;
53554     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53555 };
53556
53557 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53558     getId : function(){
53559         return this.grid.id;
53560     },
53561     
53562     /**
53563      * Returns the grid for this panel
53564      * @return {Roo.grid.Grid} 
53565      */
53566     getGrid : function(){
53567         return this.grid;    
53568     },
53569     
53570     setSize : function(width, height){
53571         if(!this.ignoreResize(width, height)){
53572             var grid = this.grid;
53573             var size = this.adjustForComponents(width, height);
53574             grid.getGridEl().setSize(size.width, size.height);
53575             grid.autoSize();
53576         }
53577     },
53578     
53579     beforeSlide : function(){
53580         this.grid.getView().scroller.clip();
53581     },
53582     
53583     afterSlide : function(){
53584         this.grid.getView().scroller.unclip();
53585     },
53586     
53587     destroy : function(){
53588         this.grid.destroy();
53589         delete this.grid;
53590         Roo.GridPanel.superclass.destroy.call(this); 
53591     }
53592 });
53593
53594
53595 /**
53596  * @class Roo.NestedLayoutPanel
53597  * @extends Roo.ContentPanel
53598  * @constructor
53599  * Create a new NestedLayoutPanel.
53600  * 
53601  * 
53602  * @param {Roo.BorderLayout} layout The layout for this panel
53603  * @param {String/Object} config A string to set only the title or a config object
53604  */
53605 Roo.NestedLayoutPanel = function(layout, config)
53606 {
53607     // construct with only one argument..
53608     /* FIXME - implement nicer consturctors
53609     if (layout.layout) {
53610         config = layout;
53611         layout = config.layout;
53612         delete config.layout;
53613     }
53614     if (layout.xtype && !layout.getEl) {
53615         // then layout needs constructing..
53616         layout = Roo.factory(layout, Roo);
53617     }
53618     */
53619     
53620     
53621     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53622     
53623     layout.monitorWindowResize = false; // turn off autosizing
53624     this.layout = layout;
53625     this.layout.getEl().addClass("x-layout-nested-layout");
53626     
53627     
53628     
53629     
53630 };
53631
53632 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53633
53634     setSize : function(width, height){
53635         if(!this.ignoreResize(width, height)){
53636             var size = this.adjustForComponents(width, height);
53637             var el = this.layout.getEl();
53638             el.setSize(size.width, size.height);
53639             var touch = el.dom.offsetWidth;
53640             this.layout.layout();
53641             // ie requires a double layout on the first pass
53642             if(Roo.isIE && !this.initialized){
53643                 this.initialized = true;
53644                 this.layout.layout();
53645             }
53646         }
53647     },
53648     
53649     // activate all subpanels if not currently active..
53650     
53651     setActiveState : function(active){
53652         this.active = active;
53653         if(!active){
53654             this.fireEvent("deactivate", this);
53655             return;
53656         }
53657         
53658         this.fireEvent("activate", this);
53659         // not sure if this should happen before or after..
53660         if (!this.layout) {
53661             return; // should not happen..
53662         }
53663         var reg = false;
53664         for (var r in this.layout.regions) {
53665             reg = this.layout.getRegion(r);
53666             if (reg.getActivePanel()) {
53667                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53668                 reg.setActivePanel(reg.getActivePanel());
53669                 continue;
53670             }
53671             if (!reg.panels.length) {
53672                 continue;
53673             }
53674             reg.showPanel(reg.getPanel(0));
53675         }
53676         
53677         
53678         
53679         
53680     },
53681     
53682     /**
53683      * Returns the nested BorderLayout for this panel
53684      * @return {Roo.BorderLayout} 
53685      */
53686     getLayout : function(){
53687         return this.layout;
53688     },
53689     
53690      /**
53691      * Adds a xtype elements to the layout of the nested panel
53692      * <pre><code>
53693
53694 panel.addxtype({
53695        xtype : 'ContentPanel',
53696        region: 'west',
53697        items: [ .... ]
53698    }
53699 );
53700
53701 panel.addxtype({
53702         xtype : 'NestedLayoutPanel',
53703         region: 'west',
53704         layout: {
53705            center: { },
53706            west: { }   
53707         },
53708         items : [ ... list of content panels or nested layout panels.. ]
53709    }
53710 );
53711 </code></pre>
53712      * @param {Object} cfg Xtype definition of item to add.
53713      */
53714     addxtype : function(cfg) {
53715         return this.layout.addxtype(cfg);
53716     
53717     }
53718 });
53719
53720 Roo.ScrollPanel = function(el, config, content){
53721     config = config || {};
53722     config.fitToFrame = true;
53723     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53724     
53725     this.el.dom.style.overflow = "hidden";
53726     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53727     this.el.removeClass("x-layout-inactive-content");
53728     this.el.on("mousewheel", this.onWheel, this);
53729
53730     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53731     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53732     up.unselectable(); down.unselectable();
53733     up.on("click", this.scrollUp, this);
53734     down.on("click", this.scrollDown, this);
53735     up.addClassOnOver("x-scroller-btn-over");
53736     down.addClassOnOver("x-scroller-btn-over");
53737     up.addClassOnClick("x-scroller-btn-click");
53738     down.addClassOnClick("x-scroller-btn-click");
53739     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53740
53741     this.resizeEl = this.el;
53742     this.el = wrap; this.up = up; this.down = down;
53743 };
53744
53745 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53746     increment : 100,
53747     wheelIncrement : 5,
53748     scrollUp : function(){
53749         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53750     },
53751
53752     scrollDown : function(){
53753         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53754     },
53755
53756     afterScroll : function(){
53757         var el = this.resizeEl;
53758         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53759         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53760         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53761     },
53762
53763     setSize : function(){
53764         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53765         this.afterScroll();
53766     },
53767
53768     onWheel : function(e){
53769         var d = e.getWheelDelta();
53770         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53771         this.afterScroll();
53772         e.stopEvent();
53773     },
53774
53775     setContent : function(content, loadScripts){
53776         this.resizeEl.update(content, loadScripts);
53777     }
53778
53779 });
53780
53781
53782
53783
53784
53785
53786
53787
53788
53789 /**
53790  * @class Roo.TreePanel
53791  * @extends Roo.ContentPanel
53792  * @constructor
53793  * Create a new TreePanel. - defaults to fit/scoll contents.
53794  * @param {String/Object} config A string to set only the panel's title, or a config object
53795  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53796  */
53797 Roo.TreePanel = function(config){
53798     var el = config.el;
53799     var tree = config.tree;
53800     delete config.tree; 
53801     delete config.el; // hopefull!
53802     
53803     // wrapper for IE7 strict & safari scroll issue
53804     
53805     var treeEl = el.createChild();
53806     config.resizeEl = treeEl;
53807     
53808     
53809     
53810     Roo.TreePanel.superclass.constructor.call(this, el, config);
53811  
53812  
53813     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53814     //console.log(tree);
53815     this.on('activate', function()
53816     {
53817         if (this.tree.rendered) {
53818             return;
53819         }
53820         //console.log('render tree');
53821         this.tree.render();
53822     });
53823     // this should not be needed.. - it's actually the 'el' that resizes?
53824     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53825     
53826     //this.on('resize',  function (cp, w, h) {
53827     //        this.tree.innerCt.setWidth(w);
53828     //        this.tree.innerCt.setHeight(h);
53829     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53830     //});
53831
53832         
53833     
53834 };
53835
53836 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53837     fitToFrame : true,
53838     autoScroll : true
53839 });
53840
53841
53842
53843
53844
53845
53846
53847
53848
53849
53850
53851 /*
53852  * Based on:
53853  * Ext JS Library 1.1.1
53854  * Copyright(c) 2006-2007, Ext JS, LLC.
53855  *
53856  * Originally Released Under LGPL - original licence link has changed is not relivant.
53857  *
53858  * Fork - LGPL
53859  * <script type="text/javascript">
53860  */
53861  
53862
53863 /**
53864  * @class Roo.ReaderLayout
53865  * @extends Roo.BorderLayout
53866  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53867  * center region containing two nested regions (a top one for a list view and one for item preview below),
53868  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53869  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53870  * expedites the setup of the overall layout and regions for this common application style.
53871  * Example:
53872  <pre><code>
53873 var reader = new Roo.ReaderLayout();
53874 var CP = Roo.ContentPanel;  // shortcut for adding
53875
53876 reader.beginUpdate();
53877 reader.add("north", new CP("north", "North"));
53878 reader.add("west", new CP("west", {title: "West"}));
53879 reader.add("east", new CP("east", {title: "East"}));
53880
53881 reader.regions.listView.add(new CP("listView", "List"));
53882 reader.regions.preview.add(new CP("preview", "Preview"));
53883 reader.endUpdate();
53884 </code></pre>
53885 * @constructor
53886 * Create a new ReaderLayout
53887 * @param {Object} config Configuration options
53888 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53889 * document.body if omitted)
53890 */
53891 Roo.ReaderLayout = function(config, renderTo){
53892     var c = config || {size:{}};
53893     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53894         north: c.north !== false ? Roo.apply({
53895             split:false,
53896             initialSize: 32,
53897             titlebar: false
53898         }, c.north) : false,
53899         west: c.west !== false ? Roo.apply({
53900             split:true,
53901             initialSize: 200,
53902             minSize: 175,
53903             maxSize: 400,
53904             titlebar: true,
53905             collapsible: true,
53906             animate: true,
53907             margins:{left:5,right:0,bottom:5,top:5},
53908             cmargins:{left:5,right:5,bottom:5,top:5}
53909         }, c.west) : false,
53910         east: c.east !== false ? Roo.apply({
53911             split:true,
53912             initialSize: 200,
53913             minSize: 175,
53914             maxSize: 400,
53915             titlebar: true,
53916             collapsible: true,
53917             animate: true,
53918             margins:{left:0,right:5,bottom:5,top:5},
53919             cmargins:{left:5,right:5,bottom:5,top:5}
53920         }, c.east) : false,
53921         center: Roo.apply({
53922             tabPosition: 'top',
53923             autoScroll:false,
53924             closeOnTab: true,
53925             titlebar:false,
53926             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53927         }, c.center)
53928     });
53929
53930     this.el.addClass('x-reader');
53931
53932     this.beginUpdate();
53933
53934     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53935         south: c.preview !== false ? Roo.apply({
53936             split:true,
53937             initialSize: 200,
53938             minSize: 100,
53939             autoScroll:true,
53940             collapsible:true,
53941             titlebar: true,
53942             cmargins:{top:5,left:0, right:0, bottom:0}
53943         }, c.preview) : false,
53944         center: Roo.apply({
53945             autoScroll:false,
53946             titlebar:false,
53947             minHeight:200
53948         }, c.listView)
53949     });
53950     this.add('center', new Roo.NestedLayoutPanel(inner,
53951             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53952
53953     this.endUpdate();
53954
53955     this.regions.preview = inner.getRegion('south');
53956     this.regions.listView = inner.getRegion('center');
53957 };
53958
53959 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53960  * Based on:
53961  * Ext JS Library 1.1.1
53962  * Copyright(c) 2006-2007, Ext JS, LLC.
53963  *
53964  * Originally Released Under LGPL - original licence link has changed is not relivant.
53965  *
53966  * Fork - LGPL
53967  * <script type="text/javascript">
53968  */
53969  
53970 /**
53971  * @class Roo.grid.Grid
53972  * @extends Roo.util.Observable
53973  * This class represents the primary interface of a component based grid control.
53974  * <br><br>Usage:<pre><code>
53975  var grid = new Roo.grid.Grid("my-container-id", {
53976      ds: myDataStore,
53977      cm: myColModel,
53978      selModel: mySelectionModel,
53979      autoSizeColumns: true,
53980      monitorWindowResize: false,
53981      trackMouseOver: true
53982  });
53983  // set any options
53984  grid.render();
53985  * </code></pre>
53986  * <b>Common Problems:</b><br/>
53987  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53988  * element will correct this<br/>
53989  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53990  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53991  * are unpredictable.<br/>
53992  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53993  * grid to calculate dimensions/offsets.<br/>
53994   * @constructor
53995  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53996  * The container MUST have some type of size defined for the grid to fill. The container will be
53997  * automatically set to position relative if it isn't already.
53998  * @param {Object} config A config object that sets properties on this grid.
53999  */
54000 Roo.grid.Grid = function(container, config){
54001         // initialize the container
54002         this.container = Roo.get(container);
54003         this.container.update("");
54004         this.container.setStyle("overflow", "hidden");
54005     this.container.addClass('x-grid-container');
54006
54007     this.id = this.container.id;
54008
54009     Roo.apply(this, config);
54010     // check and correct shorthanded configs
54011     if(this.ds){
54012         this.dataSource = this.ds;
54013         delete this.ds;
54014     }
54015     if(this.cm){
54016         this.colModel = this.cm;
54017         delete this.cm;
54018     }
54019     if(this.sm){
54020         this.selModel = this.sm;
54021         delete this.sm;
54022     }
54023
54024     if (this.selModel) {
54025         this.selModel = Roo.factory(this.selModel, Roo.grid);
54026         this.sm = this.selModel;
54027         this.sm.xmodule = this.xmodule || false;
54028     }
54029     if (typeof(this.colModel.config) == 'undefined') {
54030         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54031         this.cm = this.colModel;
54032         this.cm.xmodule = this.xmodule || false;
54033     }
54034     if (this.dataSource) {
54035         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54036         this.ds = this.dataSource;
54037         this.ds.xmodule = this.xmodule || false;
54038          
54039     }
54040     
54041     
54042     
54043     if(this.width){
54044         this.container.setWidth(this.width);
54045     }
54046
54047     if(this.height){
54048         this.container.setHeight(this.height);
54049     }
54050     /** @private */
54051         this.addEvents({
54052         // raw events
54053         /**
54054          * @event click
54055          * The raw click event for the entire grid.
54056          * @param {Roo.EventObject} e
54057          */
54058         "click" : true,
54059         /**
54060          * @event dblclick
54061          * The raw dblclick event for the entire grid.
54062          * @param {Roo.EventObject} e
54063          */
54064         "dblclick" : true,
54065         /**
54066          * @event contextmenu
54067          * The raw contextmenu event for the entire grid.
54068          * @param {Roo.EventObject} e
54069          */
54070         "contextmenu" : true,
54071         /**
54072          * @event mousedown
54073          * The raw mousedown event for the entire grid.
54074          * @param {Roo.EventObject} e
54075          */
54076         "mousedown" : true,
54077         /**
54078          * @event mouseup
54079          * The raw mouseup event for the entire grid.
54080          * @param {Roo.EventObject} e
54081          */
54082         "mouseup" : true,
54083         /**
54084          * @event mouseover
54085          * The raw mouseover event for the entire grid.
54086          * @param {Roo.EventObject} e
54087          */
54088         "mouseover" : true,
54089         /**
54090          * @event mouseout
54091          * The raw mouseout event for the entire grid.
54092          * @param {Roo.EventObject} e
54093          */
54094         "mouseout" : true,
54095         /**
54096          * @event keypress
54097          * The raw keypress event for the entire grid.
54098          * @param {Roo.EventObject} e
54099          */
54100         "keypress" : true,
54101         /**
54102          * @event keydown
54103          * The raw keydown event for the entire grid.
54104          * @param {Roo.EventObject} e
54105          */
54106         "keydown" : true,
54107
54108         // custom events
54109
54110         /**
54111          * @event cellclick
54112          * Fires when a cell is clicked
54113          * @param {Grid} this
54114          * @param {Number} rowIndex
54115          * @param {Number} columnIndex
54116          * @param {Roo.EventObject} e
54117          */
54118         "cellclick" : true,
54119         /**
54120          * @event celldblclick
54121          * Fires when a cell is double clicked
54122          * @param {Grid} this
54123          * @param {Number} rowIndex
54124          * @param {Number} columnIndex
54125          * @param {Roo.EventObject} e
54126          */
54127         "celldblclick" : true,
54128         /**
54129          * @event rowclick
54130          * Fires when a row is clicked
54131          * @param {Grid} this
54132          * @param {Number} rowIndex
54133          * @param {Roo.EventObject} e
54134          */
54135         "rowclick" : true,
54136         /**
54137          * @event rowdblclick
54138          * Fires when a row is double clicked
54139          * @param {Grid} this
54140          * @param {Number} rowIndex
54141          * @param {Roo.EventObject} e
54142          */
54143         "rowdblclick" : true,
54144         /**
54145          * @event headerclick
54146          * Fires when a header is clicked
54147          * @param {Grid} this
54148          * @param {Number} columnIndex
54149          * @param {Roo.EventObject} e
54150          */
54151         "headerclick" : true,
54152         /**
54153          * @event headerdblclick
54154          * Fires when a header cell is double clicked
54155          * @param {Grid} this
54156          * @param {Number} columnIndex
54157          * @param {Roo.EventObject} e
54158          */
54159         "headerdblclick" : true,
54160         /**
54161          * @event rowcontextmenu
54162          * Fires when a row is right clicked
54163          * @param {Grid} this
54164          * @param {Number} rowIndex
54165          * @param {Roo.EventObject} e
54166          */
54167         "rowcontextmenu" : true,
54168         /**
54169          * @event cellcontextmenu
54170          * Fires when a cell is right clicked
54171          * @param {Grid} this
54172          * @param {Number} rowIndex
54173          * @param {Number} cellIndex
54174          * @param {Roo.EventObject} e
54175          */
54176          "cellcontextmenu" : true,
54177         /**
54178          * @event headercontextmenu
54179          * Fires when a header is right clicked
54180          * @param {Grid} this
54181          * @param {Number} columnIndex
54182          * @param {Roo.EventObject} e
54183          */
54184         "headercontextmenu" : true,
54185         /**
54186          * @event bodyscroll
54187          * Fires when the body element is scrolled
54188          * @param {Number} scrollLeft
54189          * @param {Number} scrollTop
54190          */
54191         "bodyscroll" : true,
54192         /**
54193          * @event columnresize
54194          * Fires when the user resizes a column
54195          * @param {Number} columnIndex
54196          * @param {Number} newSize
54197          */
54198         "columnresize" : true,
54199         /**
54200          * @event columnmove
54201          * Fires when the user moves a column
54202          * @param {Number} oldIndex
54203          * @param {Number} newIndex
54204          */
54205         "columnmove" : true,
54206         /**
54207          * @event startdrag
54208          * Fires when row(s) start being dragged
54209          * @param {Grid} this
54210          * @param {Roo.GridDD} dd The drag drop object
54211          * @param {event} e The raw browser event
54212          */
54213         "startdrag" : true,
54214         /**
54215          * @event enddrag
54216          * Fires when a drag operation is complete
54217          * @param {Grid} this
54218          * @param {Roo.GridDD} dd The drag drop object
54219          * @param {event} e The raw browser event
54220          */
54221         "enddrag" : true,
54222         /**
54223          * @event dragdrop
54224          * Fires when dragged row(s) are dropped on a valid DD target
54225          * @param {Grid} this
54226          * @param {Roo.GridDD} dd The drag drop object
54227          * @param {String} targetId The target drag drop object
54228          * @param {event} e The raw browser event
54229          */
54230         "dragdrop" : true,
54231         /**
54232          * @event dragover
54233          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54234          * @param {Grid} this
54235          * @param {Roo.GridDD} dd The drag drop object
54236          * @param {String} targetId The target drag drop object
54237          * @param {event} e The raw browser event
54238          */
54239         "dragover" : true,
54240         /**
54241          * @event dragenter
54242          *  Fires when the dragged row(s) first cross another DD target while being dragged
54243          * @param {Grid} this
54244          * @param {Roo.GridDD} dd The drag drop object
54245          * @param {String} targetId The target drag drop object
54246          * @param {event} e The raw browser event
54247          */
54248         "dragenter" : true,
54249         /**
54250          * @event dragout
54251          * Fires when the dragged row(s) leave another DD target while being dragged
54252          * @param {Grid} this
54253          * @param {Roo.GridDD} dd The drag drop object
54254          * @param {String} targetId The target drag drop object
54255          * @param {event} e The raw browser event
54256          */
54257         "dragout" : true,
54258         /**
54259          * @event rowclass
54260          * Fires when a row is rendered, so you can change add a style to it.
54261          * @param {GridView} gridview   The grid view
54262          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54263          */
54264         'rowclass' : true,
54265
54266         /**
54267          * @event render
54268          * Fires when the grid is rendered
54269          * @param {Grid} grid
54270          */
54271         'render' : true
54272     });
54273
54274     Roo.grid.Grid.superclass.constructor.call(this);
54275 };
54276 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54277     
54278     /**
54279      * @cfg {String} ddGroup - drag drop group.
54280      */
54281
54282     /**
54283      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54284      */
54285     minColumnWidth : 25,
54286
54287     /**
54288      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54289      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54290      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54291      */
54292     autoSizeColumns : false,
54293
54294     /**
54295      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54296      */
54297     autoSizeHeaders : true,
54298
54299     /**
54300      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54301      */
54302     monitorWindowResize : true,
54303
54304     /**
54305      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54306      * rows measured to get a columns size. Default is 0 (all rows).
54307      */
54308     maxRowsToMeasure : 0,
54309
54310     /**
54311      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54312      */
54313     trackMouseOver : true,
54314
54315     /**
54316     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54317     */
54318     
54319     /**
54320     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54321     */
54322     enableDragDrop : false,
54323     
54324     /**
54325     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54326     */
54327     enableColumnMove : true,
54328     
54329     /**
54330     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54331     */
54332     enableColumnHide : true,
54333     
54334     /**
54335     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54336     */
54337     enableRowHeightSync : false,
54338     
54339     /**
54340     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54341     */
54342     stripeRows : true,
54343     
54344     /**
54345     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54346     */
54347     autoHeight : false,
54348
54349     /**
54350      * @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.
54351      */
54352     autoExpandColumn : false,
54353
54354     /**
54355     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54356     * Default is 50.
54357     */
54358     autoExpandMin : 50,
54359
54360     /**
54361     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54362     */
54363     autoExpandMax : 1000,
54364
54365     /**
54366     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54367     */
54368     view : null,
54369
54370     /**
54371     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54372     */
54373     loadMask : false,
54374     /**
54375     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54376     */
54377     dropTarget: false,
54378     
54379    
54380     
54381     // private
54382     rendered : false,
54383
54384     /**
54385     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54386     * of a fixed width. Default is false.
54387     */
54388     /**
54389     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54390     */
54391     /**
54392      * Called once after all setup has been completed and the grid is ready to be rendered.
54393      * @return {Roo.grid.Grid} this
54394      */
54395     render : function()
54396     {
54397         var c = this.container;
54398         // try to detect autoHeight/width mode
54399         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54400             this.autoHeight = true;
54401         }
54402         var view = this.getView();
54403         view.init(this);
54404
54405         c.on("click", this.onClick, this);
54406         c.on("dblclick", this.onDblClick, this);
54407         c.on("contextmenu", this.onContextMenu, this);
54408         c.on("keydown", this.onKeyDown, this);
54409         if (Roo.isTouch) {
54410             c.on("touchstart", this.onTouchStart, this);
54411         }
54412
54413         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54414
54415         this.getSelectionModel().init(this);
54416
54417         view.render();
54418
54419         if(this.loadMask){
54420             this.loadMask = new Roo.LoadMask(this.container,
54421                     Roo.apply({store:this.dataSource}, this.loadMask));
54422         }
54423         
54424         
54425         if (this.toolbar && this.toolbar.xtype) {
54426             this.toolbar.container = this.getView().getHeaderPanel(true);
54427             this.toolbar = new Roo.Toolbar(this.toolbar);
54428         }
54429         if (this.footer && this.footer.xtype) {
54430             this.footer.dataSource = this.getDataSource();
54431             this.footer.container = this.getView().getFooterPanel(true);
54432             this.footer = Roo.factory(this.footer, Roo);
54433         }
54434         if (this.dropTarget && this.dropTarget.xtype) {
54435             delete this.dropTarget.xtype;
54436             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54437         }
54438         
54439         
54440         this.rendered = true;
54441         this.fireEvent('render', this);
54442         return this;
54443     },
54444
54445         /**
54446          * Reconfigures the grid to use a different Store and Column Model.
54447          * The View will be bound to the new objects and refreshed.
54448          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54449          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54450          */
54451     reconfigure : function(dataSource, colModel){
54452         if(this.loadMask){
54453             this.loadMask.destroy();
54454             this.loadMask = new Roo.LoadMask(this.container,
54455                     Roo.apply({store:dataSource}, this.loadMask));
54456         }
54457         this.view.bind(dataSource, colModel);
54458         this.dataSource = dataSource;
54459         this.colModel = colModel;
54460         this.view.refresh(true);
54461     },
54462
54463     // private
54464     onKeyDown : function(e){
54465         this.fireEvent("keydown", e);
54466     },
54467
54468     /**
54469      * Destroy this grid.
54470      * @param {Boolean} removeEl True to remove the element
54471      */
54472     destroy : function(removeEl, keepListeners){
54473         if(this.loadMask){
54474             this.loadMask.destroy();
54475         }
54476         var c = this.container;
54477         c.removeAllListeners();
54478         this.view.destroy();
54479         this.colModel.purgeListeners();
54480         if(!keepListeners){
54481             this.purgeListeners();
54482         }
54483         c.update("");
54484         if(removeEl === true){
54485             c.remove();
54486         }
54487     },
54488
54489     // private
54490     processEvent : function(name, e){
54491         // does this fire select???
54492         //Roo.log('grid:processEvent '  + name);
54493         
54494         if (name != 'touchstart' ) {
54495             this.fireEvent(name, e);    
54496         }
54497         
54498         var t = e.getTarget();
54499         var v = this.view;
54500         var header = v.findHeaderIndex(t);
54501         if(header !== false){
54502             var ename = name == 'touchstart' ? 'click' : name;
54503              
54504             this.fireEvent("header" + ename, this, header, e);
54505         }else{
54506             var row = v.findRowIndex(t);
54507             var cell = v.findCellIndex(t);
54508             if (name == 'touchstart') {
54509                 // first touch is always a click.
54510                 // hopefull this happens after selection is updated.?
54511                 name = false;
54512                 
54513                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54514                     var cs = this.selModel.getSelectedCell();
54515                     if (row == cs[0] && cell == cs[1]){
54516                         name = 'dblclick';
54517                     }
54518                 }
54519                 if (typeof(this.selModel.getSelections) != 'undefined') {
54520                     var cs = this.selModel.getSelections();
54521                     var ds = this.dataSource;
54522                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54523                         name = 'dblclick';
54524                     }
54525                 }
54526                 if (!name) {
54527                     return;
54528                 }
54529             }
54530             
54531             
54532             if(row !== false){
54533                 this.fireEvent("row" + name, this, row, e);
54534                 if(cell !== false){
54535                     this.fireEvent("cell" + name, this, row, cell, e);
54536                 }
54537             }
54538         }
54539     },
54540
54541     // private
54542     onClick : function(e){
54543         this.processEvent("click", e);
54544     },
54545    // private
54546     onTouchStart : function(e){
54547         this.processEvent("touchstart", e);
54548     },
54549
54550     // private
54551     onContextMenu : function(e, t){
54552         this.processEvent("contextmenu", e);
54553     },
54554
54555     // private
54556     onDblClick : function(e){
54557         this.processEvent("dblclick", e);
54558     },
54559
54560     // private
54561     walkCells : function(row, col, step, fn, scope){
54562         var cm = this.colModel, clen = cm.getColumnCount();
54563         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54564         if(step < 0){
54565             if(col < 0){
54566                 row--;
54567                 first = false;
54568             }
54569             while(row >= 0){
54570                 if(!first){
54571                     col = clen-1;
54572                 }
54573                 first = false;
54574                 while(col >= 0){
54575                     if(fn.call(scope || this, row, col, cm) === true){
54576                         return [row, col];
54577                     }
54578                     col--;
54579                 }
54580                 row--;
54581             }
54582         } else {
54583             if(col >= clen){
54584                 row++;
54585                 first = false;
54586             }
54587             while(row < rlen){
54588                 if(!first){
54589                     col = 0;
54590                 }
54591                 first = false;
54592                 while(col < clen){
54593                     if(fn.call(scope || this, row, col, cm) === true){
54594                         return [row, col];
54595                     }
54596                     col++;
54597                 }
54598                 row++;
54599             }
54600         }
54601         return null;
54602     },
54603
54604     // private
54605     getSelections : function(){
54606         return this.selModel.getSelections();
54607     },
54608
54609     /**
54610      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54611      * but if manual update is required this method will initiate it.
54612      */
54613     autoSize : function(){
54614         if(this.rendered){
54615             this.view.layout();
54616             if(this.view.adjustForScroll){
54617                 this.view.adjustForScroll();
54618             }
54619         }
54620     },
54621
54622     /**
54623      * Returns the grid's underlying element.
54624      * @return {Element} The element
54625      */
54626     getGridEl : function(){
54627         return this.container;
54628     },
54629
54630     // private for compatibility, overridden by editor grid
54631     stopEditing : function(){},
54632
54633     /**
54634      * Returns the grid's SelectionModel.
54635      * @return {SelectionModel}
54636      */
54637     getSelectionModel : function(){
54638         if(!this.selModel){
54639             this.selModel = new Roo.grid.RowSelectionModel();
54640         }
54641         return this.selModel;
54642     },
54643
54644     /**
54645      * Returns the grid's DataSource.
54646      * @return {DataSource}
54647      */
54648     getDataSource : function(){
54649         return this.dataSource;
54650     },
54651
54652     /**
54653      * Returns the grid's ColumnModel.
54654      * @return {ColumnModel}
54655      */
54656     getColumnModel : function(){
54657         return this.colModel;
54658     },
54659
54660     /**
54661      * Returns the grid's GridView object.
54662      * @return {GridView}
54663      */
54664     getView : function(){
54665         if(!this.view){
54666             this.view = new Roo.grid.GridView(this.viewConfig);
54667         }
54668         return this.view;
54669     },
54670     /**
54671      * Called to get grid's drag proxy text, by default returns this.ddText.
54672      * @return {String}
54673      */
54674     getDragDropText : function(){
54675         var count = this.selModel.getCount();
54676         return String.format(this.ddText, count, count == 1 ? '' : 's');
54677     }
54678 });
54679 /**
54680  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54681  * %0 is replaced with the number of selected rows.
54682  * @type String
54683  */
54684 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54685  * Based on:
54686  * Ext JS Library 1.1.1
54687  * Copyright(c) 2006-2007, Ext JS, LLC.
54688  *
54689  * Originally Released Under LGPL - original licence link has changed is not relivant.
54690  *
54691  * Fork - LGPL
54692  * <script type="text/javascript">
54693  */
54694  
54695 Roo.grid.AbstractGridView = function(){
54696         this.grid = null;
54697         
54698         this.events = {
54699             "beforerowremoved" : true,
54700             "beforerowsinserted" : true,
54701             "beforerefresh" : true,
54702             "rowremoved" : true,
54703             "rowsinserted" : true,
54704             "rowupdated" : true,
54705             "refresh" : true
54706         };
54707     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54708 };
54709
54710 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54711     rowClass : "x-grid-row",
54712     cellClass : "x-grid-cell",
54713     tdClass : "x-grid-td",
54714     hdClass : "x-grid-hd",
54715     splitClass : "x-grid-hd-split",
54716     
54717     init: function(grid){
54718         this.grid = grid;
54719                 var cid = this.grid.getGridEl().id;
54720         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54721         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54722         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54723         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54724         },
54725         
54726     getColumnRenderers : function(){
54727         var renderers = [];
54728         var cm = this.grid.colModel;
54729         var colCount = cm.getColumnCount();
54730         for(var i = 0; i < colCount; i++){
54731             renderers[i] = cm.getRenderer(i);
54732         }
54733         return renderers;
54734     },
54735     
54736     getColumnIds : function(){
54737         var ids = [];
54738         var cm = this.grid.colModel;
54739         var colCount = cm.getColumnCount();
54740         for(var i = 0; i < colCount; i++){
54741             ids[i] = cm.getColumnId(i);
54742         }
54743         return ids;
54744     },
54745     
54746     getDataIndexes : function(){
54747         if(!this.indexMap){
54748             this.indexMap = this.buildIndexMap();
54749         }
54750         return this.indexMap.colToData;
54751     },
54752     
54753     getColumnIndexByDataIndex : function(dataIndex){
54754         if(!this.indexMap){
54755             this.indexMap = this.buildIndexMap();
54756         }
54757         return this.indexMap.dataToCol[dataIndex];
54758     },
54759     
54760     /**
54761      * Set a css style for a column dynamically. 
54762      * @param {Number} colIndex The index of the column
54763      * @param {String} name The css property name
54764      * @param {String} value The css value
54765      */
54766     setCSSStyle : function(colIndex, name, value){
54767         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54768         Roo.util.CSS.updateRule(selector, name, value);
54769     },
54770     
54771     generateRules : function(cm){
54772         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54773         Roo.util.CSS.removeStyleSheet(rulesId);
54774         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54775             var cid = cm.getColumnId(i);
54776             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54777                          this.tdSelector, cid, " {\n}\n",
54778                          this.hdSelector, cid, " {\n}\n",
54779                          this.splitSelector, cid, " {\n}\n");
54780         }
54781         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54782     }
54783 });/*
54784  * Based on:
54785  * Ext JS Library 1.1.1
54786  * Copyright(c) 2006-2007, Ext JS, LLC.
54787  *
54788  * Originally Released Under LGPL - original licence link has changed is not relivant.
54789  *
54790  * Fork - LGPL
54791  * <script type="text/javascript">
54792  */
54793
54794 // private
54795 // This is a support class used internally by the Grid components
54796 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54797     this.grid = grid;
54798     this.view = grid.getView();
54799     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54800     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54801     if(hd2){
54802         this.setHandleElId(Roo.id(hd));
54803         this.setOuterHandleElId(Roo.id(hd2));
54804     }
54805     this.scroll = false;
54806 };
54807 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54808     maxDragWidth: 120,
54809     getDragData : function(e){
54810         var t = Roo.lib.Event.getTarget(e);
54811         var h = this.view.findHeaderCell(t);
54812         if(h){
54813             return {ddel: h.firstChild, header:h};
54814         }
54815         return false;
54816     },
54817
54818     onInitDrag : function(e){
54819         this.view.headersDisabled = true;
54820         var clone = this.dragData.ddel.cloneNode(true);
54821         clone.id = Roo.id();
54822         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54823         this.proxy.update(clone);
54824         return true;
54825     },
54826
54827     afterValidDrop : function(){
54828         var v = this.view;
54829         setTimeout(function(){
54830             v.headersDisabled = false;
54831         }, 50);
54832     },
54833
54834     afterInvalidDrop : function(){
54835         var v = this.view;
54836         setTimeout(function(){
54837             v.headersDisabled = false;
54838         }, 50);
54839     }
54840 });
54841 /*
54842  * Based on:
54843  * Ext JS Library 1.1.1
54844  * Copyright(c) 2006-2007, Ext JS, LLC.
54845  *
54846  * Originally Released Under LGPL - original licence link has changed is not relivant.
54847  *
54848  * Fork - LGPL
54849  * <script type="text/javascript">
54850  */
54851 // private
54852 // This is a support class used internally by the Grid components
54853 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54854     this.grid = grid;
54855     this.view = grid.getView();
54856     // split the proxies so they don't interfere with mouse events
54857     this.proxyTop = Roo.DomHelper.append(document.body, {
54858         cls:"col-move-top", html:"&#160;"
54859     }, true);
54860     this.proxyBottom = Roo.DomHelper.append(document.body, {
54861         cls:"col-move-bottom", html:"&#160;"
54862     }, true);
54863     this.proxyTop.hide = this.proxyBottom.hide = function(){
54864         this.setLeftTop(-100,-100);
54865         this.setStyle("visibility", "hidden");
54866     };
54867     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54868     // temporarily disabled
54869     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54870     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54871 };
54872 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54873     proxyOffsets : [-4, -9],
54874     fly: Roo.Element.fly,
54875
54876     getTargetFromEvent : function(e){
54877         var t = Roo.lib.Event.getTarget(e);
54878         var cindex = this.view.findCellIndex(t);
54879         if(cindex !== false){
54880             return this.view.getHeaderCell(cindex);
54881         }
54882         return null;
54883     },
54884
54885     nextVisible : function(h){
54886         var v = this.view, cm = this.grid.colModel;
54887         h = h.nextSibling;
54888         while(h){
54889             if(!cm.isHidden(v.getCellIndex(h))){
54890                 return h;
54891             }
54892             h = h.nextSibling;
54893         }
54894         return null;
54895     },
54896
54897     prevVisible : function(h){
54898         var v = this.view, cm = this.grid.colModel;
54899         h = h.prevSibling;
54900         while(h){
54901             if(!cm.isHidden(v.getCellIndex(h))){
54902                 return h;
54903             }
54904             h = h.prevSibling;
54905         }
54906         return null;
54907     },
54908
54909     positionIndicator : function(h, n, e){
54910         var x = Roo.lib.Event.getPageX(e);
54911         var r = Roo.lib.Dom.getRegion(n.firstChild);
54912         var px, pt, py = r.top + this.proxyOffsets[1];
54913         if((r.right - x) <= (r.right-r.left)/2){
54914             px = r.right+this.view.borderWidth;
54915             pt = "after";
54916         }else{
54917             px = r.left;
54918             pt = "before";
54919         }
54920         var oldIndex = this.view.getCellIndex(h);
54921         var newIndex = this.view.getCellIndex(n);
54922
54923         if(this.grid.colModel.isFixed(newIndex)){
54924             return false;
54925         }
54926
54927         var locked = this.grid.colModel.isLocked(newIndex);
54928
54929         if(pt == "after"){
54930             newIndex++;
54931         }
54932         if(oldIndex < newIndex){
54933             newIndex--;
54934         }
54935         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54936             return false;
54937         }
54938         px +=  this.proxyOffsets[0];
54939         this.proxyTop.setLeftTop(px, py);
54940         this.proxyTop.show();
54941         if(!this.bottomOffset){
54942             this.bottomOffset = this.view.mainHd.getHeight();
54943         }
54944         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54945         this.proxyBottom.show();
54946         return pt;
54947     },
54948
54949     onNodeEnter : function(n, dd, e, data){
54950         if(data.header != n){
54951             this.positionIndicator(data.header, n, e);
54952         }
54953     },
54954
54955     onNodeOver : function(n, dd, e, data){
54956         var result = false;
54957         if(data.header != n){
54958             result = this.positionIndicator(data.header, n, e);
54959         }
54960         if(!result){
54961             this.proxyTop.hide();
54962             this.proxyBottom.hide();
54963         }
54964         return result ? this.dropAllowed : this.dropNotAllowed;
54965     },
54966
54967     onNodeOut : function(n, dd, e, data){
54968         this.proxyTop.hide();
54969         this.proxyBottom.hide();
54970     },
54971
54972     onNodeDrop : function(n, dd, e, data){
54973         var h = data.header;
54974         if(h != n){
54975             var cm = this.grid.colModel;
54976             var x = Roo.lib.Event.getPageX(e);
54977             var r = Roo.lib.Dom.getRegion(n.firstChild);
54978             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54979             var oldIndex = this.view.getCellIndex(h);
54980             var newIndex = this.view.getCellIndex(n);
54981             var locked = cm.isLocked(newIndex);
54982             if(pt == "after"){
54983                 newIndex++;
54984             }
54985             if(oldIndex < newIndex){
54986                 newIndex--;
54987             }
54988             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54989                 return false;
54990             }
54991             cm.setLocked(oldIndex, locked, true);
54992             cm.moveColumn(oldIndex, newIndex);
54993             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54994             return true;
54995         }
54996         return false;
54997     }
54998 });
54999 /*
55000  * Based on:
55001  * Ext JS Library 1.1.1
55002  * Copyright(c) 2006-2007, Ext JS, LLC.
55003  *
55004  * Originally Released Under LGPL - original licence link has changed is not relivant.
55005  *
55006  * Fork - LGPL
55007  * <script type="text/javascript">
55008  */
55009   
55010 /**
55011  * @class Roo.grid.GridView
55012  * @extends Roo.util.Observable
55013  *
55014  * @constructor
55015  * @param {Object} config
55016  */
55017 Roo.grid.GridView = function(config){
55018     Roo.grid.GridView.superclass.constructor.call(this);
55019     this.el = null;
55020
55021     Roo.apply(this, config);
55022 };
55023
55024 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55025
55026     unselectable :  'unselectable="on"',
55027     unselectableCls :  'x-unselectable',
55028     
55029     
55030     rowClass : "x-grid-row",
55031
55032     cellClass : "x-grid-col",
55033
55034     tdClass : "x-grid-td",
55035
55036     hdClass : "x-grid-hd",
55037
55038     splitClass : "x-grid-split",
55039
55040     sortClasses : ["sort-asc", "sort-desc"],
55041
55042     enableMoveAnim : false,
55043
55044     hlColor: "C3DAF9",
55045
55046     dh : Roo.DomHelper,
55047
55048     fly : Roo.Element.fly,
55049
55050     css : Roo.util.CSS,
55051
55052     borderWidth: 1,
55053
55054     splitOffset: 3,
55055
55056     scrollIncrement : 22,
55057
55058     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55059
55060     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55061
55062     bind : function(ds, cm){
55063         if(this.ds){
55064             this.ds.un("load", this.onLoad, this);
55065             this.ds.un("datachanged", this.onDataChange, this);
55066             this.ds.un("add", this.onAdd, this);
55067             this.ds.un("remove", this.onRemove, this);
55068             this.ds.un("update", this.onUpdate, this);
55069             this.ds.un("clear", this.onClear, this);
55070         }
55071         if(ds){
55072             ds.on("load", this.onLoad, this);
55073             ds.on("datachanged", this.onDataChange, this);
55074             ds.on("add", this.onAdd, this);
55075             ds.on("remove", this.onRemove, this);
55076             ds.on("update", this.onUpdate, this);
55077             ds.on("clear", this.onClear, this);
55078         }
55079         this.ds = ds;
55080
55081         if(this.cm){
55082             this.cm.un("widthchange", this.onColWidthChange, this);
55083             this.cm.un("headerchange", this.onHeaderChange, this);
55084             this.cm.un("hiddenchange", this.onHiddenChange, this);
55085             this.cm.un("columnmoved", this.onColumnMove, this);
55086             this.cm.un("columnlockchange", this.onColumnLock, this);
55087         }
55088         if(cm){
55089             this.generateRules(cm);
55090             cm.on("widthchange", this.onColWidthChange, this);
55091             cm.on("headerchange", this.onHeaderChange, this);
55092             cm.on("hiddenchange", this.onHiddenChange, this);
55093             cm.on("columnmoved", this.onColumnMove, this);
55094             cm.on("columnlockchange", this.onColumnLock, this);
55095         }
55096         this.cm = cm;
55097     },
55098
55099     init: function(grid){
55100         Roo.grid.GridView.superclass.init.call(this, grid);
55101
55102         this.bind(grid.dataSource, grid.colModel);
55103
55104         grid.on("headerclick", this.handleHeaderClick, this);
55105
55106         if(grid.trackMouseOver){
55107             grid.on("mouseover", this.onRowOver, this);
55108             grid.on("mouseout", this.onRowOut, this);
55109         }
55110         grid.cancelTextSelection = function(){};
55111         this.gridId = grid.id;
55112
55113         var tpls = this.templates || {};
55114
55115         if(!tpls.master){
55116             tpls.master = new Roo.Template(
55117                '<div class="x-grid" hidefocus="true">',
55118                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55119                   '<div class="x-grid-topbar"></div>',
55120                   '<div class="x-grid-scroller"><div></div></div>',
55121                   '<div class="x-grid-locked">',
55122                       '<div class="x-grid-header">{lockedHeader}</div>',
55123                       '<div class="x-grid-body">{lockedBody}</div>',
55124                   "</div>",
55125                   '<div class="x-grid-viewport">',
55126                       '<div class="x-grid-header">{header}</div>',
55127                       '<div class="x-grid-body">{body}</div>',
55128                   "</div>",
55129                   '<div class="x-grid-bottombar"></div>',
55130                  
55131                   '<div class="x-grid-resize-proxy">&#160;</div>',
55132                "</div>"
55133             );
55134             tpls.master.disableformats = true;
55135         }
55136
55137         if(!tpls.header){
55138             tpls.header = new Roo.Template(
55139                '<table border="0" cellspacing="0" cellpadding="0">',
55140                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55141                "</table>{splits}"
55142             );
55143             tpls.header.disableformats = true;
55144         }
55145         tpls.header.compile();
55146
55147         if(!tpls.hcell){
55148             tpls.hcell = new Roo.Template(
55149                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55150                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55151                 "</div></td>"
55152              );
55153              tpls.hcell.disableFormats = true;
55154         }
55155         tpls.hcell.compile();
55156
55157         if(!tpls.hsplit){
55158             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55159                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55160             tpls.hsplit.disableFormats = true;
55161         }
55162         tpls.hsplit.compile();
55163
55164         if(!tpls.body){
55165             tpls.body = new Roo.Template(
55166                '<table border="0" cellspacing="0" cellpadding="0">',
55167                "<tbody>{rows}</tbody>",
55168                "</table>"
55169             );
55170             tpls.body.disableFormats = true;
55171         }
55172         tpls.body.compile();
55173
55174         if(!tpls.row){
55175             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55176             tpls.row.disableFormats = true;
55177         }
55178         tpls.row.compile();
55179
55180         if(!tpls.cell){
55181             tpls.cell = new Roo.Template(
55182                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55183                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55184                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55185                 "</td>"
55186             );
55187             tpls.cell.disableFormats = true;
55188         }
55189         tpls.cell.compile();
55190
55191         this.templates = tpls;
55192     },
55193
55194     // remap these for backwards compat
55195     onColWidthChange : function(){
55196         this.updateColumns.apply(this, arguments);
55197     },
55198     onHeaderChange : function(){
55199         this.updateHeaders.apply(this, arguments);
55200     }, 
55201     onHiddenChange : function(){
55202         this.handleHiddenChange.apply(this, arguments);
55203     },
55204     onColumnMove : function(){
55205         this.handleColumnMove.apply(this, arguments);
55206     },
55207     onColumnLock : function(){
55208         this.handleLockChange.apply(this, arguments);
55209     },
55210
55211     onDataChange : function(){
55212         this.refresh();
55213         this.updateHeaderSortState();
55214     },
55215
55216     onClear : function(){
55217         this.refresh();
55218     },
55219
55220     onUpdate : function(ds, record){
55221         this.refreshRow(record);
55222     },
55223
55224     refreshRow : function(record){
55225         var ds = this.ds, index;
55226         if(typeof record == 'number'){
55227             index = record;
55228             record = ds.getAt(index);
55229         }else{
55230             index = ds.indexOf(record);
55231         }
55232         this.insertRows(ds, index, index, true);
55233         this.onRemove(ds, record, index+1, true);
55234         this.syncRowHeights(index, index);
55235         this.layout();
55236         this.fireEvent("rowupdated", this, index, record);
55237     },
55238
55239     onAdd : function(ds, records, index){
55240         this.insertRows(ds, index, index + (records.length-1));
55241     },
55242
55243     onRemove : function(ds, record, index, isUpdate){
55244         if(isUpdate !== true){
55245             this.fireEvent("beforerowremoved", this, index, record);
55246         }
55247         var bt = this.getBodyTable(), lt = this.getLockedTable();
55248         if(bt.rows[index]){
55249             bt.firstChild.removeChild(bt.rows[index]);
55250         }
55251         if(lt.rows[index]){
55252             lt.firstChild.removeChild(lt.rows[index]);
55253         }
55254         if(isUpdate !== true){
55255             this.stripeRows(index);
55256             this.syncRowHeights(index, index);
55257             this.layout();
55258             this.fireEvent("rowremoved", this, index, record);
55259         }
55260     },
55261
55262     onLoad : function(){
55263         this.scrollToTop();
55264     },
55265
55266     /**
55267      * Scrolls the grid to the top
55268      */
55269     scrollToTop : function(){
55270         if(this.scroller){
55271             this.scroller.dom.scrollTop = 0;
55272             this.syncScroll();
55273         }
55274     },
55275
55276     /**
55277      * Gets a panel in the header of the grid that can be used for toolbars etc.
55278      * After modifying the contents of this panel a call to grid.autoSize() may be
55279      * required to register any changes in size.
55280      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55281      * @return Roo.Element
55282      */
55283     getHeaderPanel : function(doShow){
55284         if(doShow){
55285             this.headerPanel.show();
55286         }
55287         return this.headerPanel;
55288     },
55289
55290     /**
55291      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55292      * After modifying the contents of this panel a call to grid.autoSize() may be
55293      * required to register any changes in size.
55294      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55295      * @return Roo.Element
55296      */
55297     getFooterPanel : function(doShow){
55298         if(doShow){
55299             this.footerPanel.show();
55300         }
55301         return this.footerPanel;
55302     },
55303
55304     initElements : function(){
55305         var E = Roo.Element;
55306         var el = this.grid.getGridEl().dom.firstChild;
55307         var cs = el.childNodes;
55308
55309         this.el = new E(el);
55310         
55311          this.focusEl = new E(el.firstChild);
55312         this.focusEl.swallowEvent("click", true);
55313         
55314         this.headerPanel = new E(cs[1]);
55315         this.headerPanel.enableDisplayMode("block");
55316
55317         this.scroller = new E(cs[2]);
55318         this.scrollSizer = new E(this.scroller.dom.firstChild);
55319
55320         this.lockedWrap = new E(cs[3]);
55321         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55322         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55323
55324         this.mainWrap = new E(cs[4]);
55325         this.mainHd = new E(this.mainWrap.dom.firstChild);
55326         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55327
55328         this.footerPanel = new E(cs[5]);
55329         this.footerPanel.enableDisplayMode("block");
55330
55331         this.resizeProxy = new E(cs[6]);
55332
55333         this.headerSelector = String.format(
55334            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55335            this.lockedHd.id, this.mainHd.id
55336         );
55337
55338         this.splitterSelector = String.format(
55339            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55340            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55341         );
55342     },
55343     idToCssName : function(s)
55344     {
55345         return s.replace(/[^a-z0-9]+/ig, '-');
55346     },
55347
55348     getHeaderCell : function(index){
55349         return Roo.DomQuery.select(this.headerSelector)[index];
55350     },
55351
55352     getHeaderCellMeasure : function(index){
55353         return this.getHeaderCell(index).firstChild;
55354     },
55355
55356     getHeaderCellText : function(index){
55357         return this.getHeaderCell(index).firstChild.firstChild;
55358     },
55359
55360     getLockedTable : function(){
55361         return this.lockedBody.dom.firstChild;
55362     },
55363
55364     getBodyTable : function(){
55365         return this.mainBody.dom.firstChild;
55366     },
55367
55368     getLockedRow : function(index){
55369         return this.getLockedTable().rows[index];
55370     },
55371
55372     getRow : function(index){
55373         return this.getBodyTable().rows[index];
55374     },
55375
55376     getRowComposite : function(index){
55377         if(!this.rowEl){
55378             this.rowEl = new Roo.CompositeElementLite();
55379         }
55380         var els = [], lrow, mrow;
55381         if(lrow = this.getLockedRow(index)){
55382             els.push(lrow);
55383         }
55384         if(mrow = this.getRow(index)){
55385             els.push(mrow);
55386         }
55387         this.rowEl.elements = els;
55388         return this.rowEl;
55389     },
55390     /**
55391      * Gets the 'td' of the cell
55392      * 
55393      * @param {Integer} rowIndex row to select
55394      * @param {Integer} colIndex column to select
55395      * 
55396      * @return {Object} 
55397      */
55398     getCell : function(rowIndex, colIndex){
55399         var locked = this.cm.getLockedCount();
55400         var source;
55401         if(colIndex < locked){
55402             source = this.lockedBody.dom.firstChild;
55403         }else{
55404             source = this.mainBody.dom.firstChild;
55405             colIndex -= locked;
55406         }
55407         return source.rows[rowIndex].childNodes[colIndex];
55408     },
55409
55410     getCellText : function(rowIndex, colIndex){
55411         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55412     },
55413
55414     getCellBox : function(cell){
55415         var b = this.fly(cell).getBox();
55416         if(Roo.isOpera){ // opera fails to report the Y
55417             b.y = cell.offsetTop + this.mainBody.getY();
55418         }
55419         return b;
55420     },
55421
55422     getCellIndex : function(cell){
55423         var id = String(cell.className).match(this.cellRE);
55424         if(id){
55425             return parseInt(id[1], 10);
55426         }
55427         return 0;
55428     },
55429
55430     findHeaderIndex : function(n){
55431         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55432         return r ? this.getCellIndex(r) : false;
55433     },
55434
55435     findHeaderCell : function(n){
55436         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55437         return r ? r : false;
55438     },
55439
55440     findRowIndex : function(n){
55441         if(!n){
55442             return false;
55443         }
55444         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55445         return r ? r.rowIndex : false;
55446     },
55447
55448     findCellIndex : function(node){
55449         var stop = this.el.dom;
55450         while(node && node != stop){
55451             if(this.findRE.test(node.className)){
55452                 return this.getCellIndex(node);
55453             }
55454             node = node.parentNode;
55455         }
55456         return false;
55457     },
55458
55459     getColumnId : function(index){
55460         return this.cm.getColumnId(index);
55461     },
55462
55463     getSplitters : function()
55464     {
55465         if(this.splitterSelector){
55466            return Roo.DomQuery.select(this.splitterSelector);
55467         }else{
55468             return null;
55469       }
55470     },
55471
55472     getSplitter : function(index){
55473         return this.getSplitters()[index];
55474     },
55475
55476     onRowOver : function(e, t){
55477         var row;
55478         if((row = this.findRowIndex(t)) !== false){
55479             this.getRowComposite(row).addClass("x-grid-row-over");
55480         }
55481     },
55482
55483     onRowOut : function(e, t){
55484         var row;
55485         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55486             this.getRowComposite(row).removeClass("x-grid-row-over");
55487         }
55488     },
55489
55490     renderHeaders : function(){
55491         var cm = this.cm;
55492         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55493         var cb = [], lb = [], sb = [], lsb = [], p = {};
55494         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55495             p.cellId = "x-grid-hd-0-" + i;
55496             p.splitId = "x-grid-csplit-0-" + i;
55497             p.id = cm.getColumnId(i);
55498             p.value = cm.getColumnHeader(i) || "";
55499             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55500             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55501             if(!cm.isLocked(i)){
55502                 cb[cb.length] = ct.apply(p);
55503                 sb[sb.length] = st.apply(p);
55504             }else{
55505                 lb[lb.length] = ct.apply(p);
55506                 lsb[lsb.length] = st.apply(p);
55507             }
55508         }
55509         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55510                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55511     },
55512
55513     updateHeaders : function(){
55514         var html = this.renderHeaders();
55515         this.lockedHd.update(html[0]);
55516         this.mainHd.update(html[1]);
55517     },
55518
55519     /**
55520      * Focuses the specified row.
55521      * @param {Number} row The row index
55522      */
55523     focusRow : function(row)
55524     {
55525         //Roo.log('GridView.focusRow');
55526         var x = this.scroller.dom.scrollLeft;
55527         this.focusCell(row, 0, false);
55528         this.scroller.dom.scrollLeft = x;
55529     },
55530
55531     /**
55532      * Focuses the specified cell.
55533      * @param {Number} row The row index
55534      * @param {Number} col The column index
55535      * @param {Boolean} hscroll false to disable horizontal scrolling
55536      */
55537     focusCell : function(row, col, hscroll)
55538     {
55539         //Roo.log('GridView.focusCell');
55540         var el = this.ensureVisible(row, col, hscroll);
55541         this.focusEl.alignTo(el, "tl-tl");
55542         if(Roo.isGecko){
55543             this.focusEl.focus();
55544         }else{
55545             this.focusEl.focus.defer(1, this.focusEl);
55546         }
55547     },
55548
55549     /**
55550      * Scrolls the specified cell into view
55551      * @param {Number} row The row index
55552      * @param {Number} col The column index
55553      * @param {Boolean} hscroll false to disable horizontal scrolling
55554      */
55555     ensureVisible : function(row, col, hscroll)
55556     {
55557         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55558         //return null; //disable for testing.
55559         if(typeof row != "number"){
55560             row = row.rowIndex;
55561         }
55562         if(row < 0 && row >= this.ds.getCount()){
55563             return  null;
55564         }
55565         col = (col !== undefined ? col : 0);
55566         var cm = this.grid.colModel;
55567         while(cm.isHidden(col)){
55568             col++;
55569         }
55570
55571         var el = this.getCell(row, col);
55572         if(!el){
55573             return null;
55574         }
55575         var c = this.scroller.dom;
55576
55577         var ctop = parseInt(el.offsetTop, 10);
55578         var cleft = parseInt(el.offsetLeft, 10);
55579         var cbot = ctop + el.offsetHeight;
55580         var cright = cleft + el.offsetWidth;
55581         
55582         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55583         var stop = parseInt(c.scrollTop, 10);
55584         var sleft = parseInt(c.scrollLeft, 10);
55585         var sbot = stop + ch;
55586         var sright = sleft + c.clientWidth;
55587         /*
55588         Roo.log('GridView.ensureVisible:' +
55589                 ' ctop:' + ctop +
55590                 ' c.clientHeight:' + c.clientHeight +
55591                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55592                 ' stop:' + stop +
55593                 ' cbot:' + cbot +
55594                 ' sbot:' + sbot +
55595                 ' ch:' + ch  
55596                 );
55597         */
55598         if(ctop < stop){
55599              c.scrollTop = ctop;
55600             //Roo.log("set scrolltop to ctop DISABLE?");
55601         }else if(cbot > sbot){
55602             //Roo.log("set scrolltop to cbot-ch");
55603             c.scrollTop = cbot-ch;
55604         }
55605         
55606         if(hscroll !== false){
55607             if(cleft < sleft){
55608                 c.scrollLeft = cleft;
55609             }else if(cright > sright){
55610                 c.scrollLeft = cright-c.clientWidth;
55611             }
55612         }
55613          
55614         return el;
55615     },
55616
55617     updateColumns : function(){
55618         this.grid.stopEditing();
55619         var cm = this.grid.colModel, colIds = this.getColumnIds();
55620         //var totalWidth = cm.getTotalWidth();
55621         var pos = 0;
55622         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55623             //if(cm.isHidden(i)) continue;
55624             var w = cm.getColumnWidth(i);
55625             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55626             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55627         }
55628         this.updateSplitters();
55629     },
55630
55631     generateRules : function(cm){
55632         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55633         Roo.util.CSS.removeStyleSheet(rulesId);
55634         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55635             var cid = cm.getColumnId(i);
55636             var align = '';
55637             if(cm.config[i].align){
55638                 align = 'text-align:'+cm.config[i].align+';';
55639             }
55640             var hidden = '';
55641             if(cm.isHidden(i)){
55642                 hidden = 'display:none;';
55643             }
55644             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55645             ruleBuf.push(
55646                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55647                     this.hdSelector, cid, " {\n", align, width, "}\n",
55648                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55649                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55650         }
55651         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55652     },
55653
55654     updateSplitters : function(){
55655         var cm = this.cm, s = this.getSplitters();
55656         if(s){ // splitters not created yet
55657             var pos = 0, locked = true;
55658             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55659                 if(cm.isHidden(i)) {
55660                     continue;
55661                 }
55662                 var w = cm.getColumnWidth(i); // make sure it's a number
55663                 if(!cm.isLocked(i) && locked){
55664                     pos = 0;
55665                     locked = false;
55666                 }
55667                 pos += w;
55668                 s[i].style.left = (pos-this.splitOffset) + "px";
55669             }
55670         }
55671     },
55672
55673     handleHiddenChange : function(colModel, colIndex, hidden){
55674         if(hidden){
55675             this.hideColumn(colIndex);
55676         }else{
55677             this.unhideColumn(colIndex);
55678         }
55679     },
55680
55681     hideColumn : function(colIndex){
55682         var cid = this.getColumnId(colIndex);
55683         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55684         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55685         if(Roo.isSafari){
55686             this.updateHeaders();
55687         }
55688         this.updateSplitters();
55689         this.layout();
55690     },
55691
55692     unhideColumn : function(colIndex){
55693         var cid = this.getColumnId(colIndex);
55694         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55695         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55696
55697         if(Roo.isSafari){
55698             this.updateHeaders();
55699         }
55700         this.updateSplitters();
55701         this.layout();
55702     },
55703
55704     insertRows : function(dm, firstRow, lastRow, isUpdate){
55705         if(firstRow == 0 && lastRow == dm.getCount()-1){
55706             this.refresh();
55707         }else{
55708             if(!isUpdate){
55709                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55710             }
55711             var s = this.getScrollState();
55712             var markup = this.renderRows(firstRow, lastRow);
55713             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55714             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55715             this.restoreScroll(s);
55716             if(!isUpdate){
55717                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55718                 this.syncRowHeights(firstRow, lastRow);
55719                 this.stripeRows(firstRow);
55720                 this.layout();
55721             }
55722         }
55723     },
55724
55725     bufferRows : function(markup, target, index){
55726         var before = null, trows = target.rows, tbody = target.tBodies[0];
55727         if(index < trows.length){
55728             before = trows[index];
55729         }
55730         var b = document.createElement("div");
55731         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55732         var rows = b.firstChild.rows;
55733         for(var i = 0, len = rows.length; i < len; i++){
55734             if(before){
55735                 tbody.insertBefore(rows[0], before);
55736             }else{
55737                 tbody.appendChild(rows[0]);
55738             }
55739         }
55740         b.innerHTML = "";
55741         b = null;
55742     },
55743
55744     deleteRows : function(dm, firstRow, lastRow){
55745         if(dm.getRowCount()<1){
55746             this.fireEvent("beforerefresh", this);
55747             this.mainBody.update("");
55748             this.lockedBody.update("");
55749             this.fireEvent("refresh", this);
55750         }else{
55751             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55752             var bt = this.getBodyTable();
55753             var tbody = bt.firstChild;
55754             var rows = bt.rows;
55755             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55756                 tbody.removeChild(rows[firstRow]);
55757             }
55758             this.stripeRows(firstRow);
55759             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55760         }
55761     },
55762
55763     updateRows : function(dataSource, firstRow, lastRow){
55764         var s = this.getScrollState();
55765         this.refresh();
55766         this.restoreScroll(s);
55767     },
55768
55769     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55770         if(!noRefresh){
55771            this.refresh();
55772         }
55773         this.updateHeaderSortState();
55774     },
55775
55776     getScrollState : function(){
55777         
55778         var sb = this.scroller.dom;
55779         return {left: sb.scrollLeft, top: sb.scrollTop};
55780     },
55781
55782     stripeRows : function(startRow){
55783         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55784             return;
55785         }
55786         startRow = startRow || 0;
55787         var rows = this.getBodyTable().rows;
55788         var lrows = this.getLockedTable().rows;
55789         var cls = ' x-grid-row-alt ';
55790         for(var i = startRow, len = rows.length; i < len; i++){
55791             var row = rows[i], lrow = lrows[i];
55792             var isAlt = ((i+1) % 2 == 0);
55793             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55794             if(isAlt == hasAlt){
55795                 continue;
55796             }
55797             if(isAlt){
55798                 row.className += " x-grid-row-alt";
55799             }else{
55800                 row.className = row.className.replace("x-grid-row-alt", "");
55801             }
55802             if(lrow){
55803                 lrow.className = row.className;
55804             }
55805         }
55806     },
55807
55808     restoreScroll : function(state){
55809         //Roo.log('GridView.restoreScroll');
55810         var sb = this.scroller.dom;
55811         sb.scrollLeft = state.left;
55812         sb.scrollTop = state.top;
55813         this.syncScroll();
55814     },
55815
55816     syncScroll : function(){
55817         //Roo.log('GridView.syncScroll');
55818         var sb = this.scroller.dom;
55819         var sh = this.mainHd.dom;
55820         var bs = this.mainBody.dom;
55821         var lv = this.lockedBody.dom;
55822         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55823         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55824     },
55825
55826     handleScroll : function(e){
55827         this.syncScroll();
55828         var sb = this.scroller.dom;
55829         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55830         e.stopEvent();
55831     },
55832
55833     handleWheel : function(e){
55834         var d = e.getWheelDelta();
55835         this.scroller.dom.scrollTop -= d*22;
55836         // set this here to prevent jumpy scrolling on large tables
55837         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55838         e.stopEvent();
55839     },
55840
55841     renderRows : function(startRow, endRow){
55842         // pull in all the crap needed to render rows
55843         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55844         var colCount = cm.getColumnCount();
55845
55846         if(ds.getCount() < 1){
55847             return ["", ""];
55848         }
55849
55850         // build a map for all the columns
55851         var cs = [];
55852         for(var i = 0; i < colCount; i++){
55853             var name = cm.getDataIndex(i);
55854             cs[i] = {
55855                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55856                 renderer : cm.getRenderer(i),
55857                 id : cm.getColumnId(i),
55858                 locked : cm.isLocked(i),
55859                 has_editor : cm.isCellEditable(i)
55860             };
55861         }
55862
55863         startRow = startRow || 0;
55864         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55865
55866         // records to render
55867         var rs = ds.getRange(startRow, endRow);
55868
55869         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55870     },
55871
55872     // As much as I hate to duplicate code, this was branched because FireFox really hates
55873     // [].join("") on strings. The performance difference was substantial enough to
55874     // branch this function
55875     doRender : Roo.isGecko ?
55876             function(cs, rs, ds, startRow, colCount, stripe){
55877                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55878                 // buffers
55879                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55880                 
55881                 var hasListener = this.grid.hasListener('rowclass');
55882                 var rowcfg = {};
55883                 for(var j = 0, len = rs.length; j < len; j++){
55884                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55885                     for(var i = 0; i < colCount; i++){
55886                         c = cs[i];
55887                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55888                         p.id = c.id;
55889                         p.css = p.attr = "";
55890                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55891                         if(p.value == undefined || p.value === "") {
55892                             p.value = "&#160;";
55893                         }
55894                         if(c.has_editor){
55895                             p.css += ' x-grid-editable-cell';
55896                         }
55897                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55898                             p.css +=  ' x-grid-dirty-cell';
55899                         }
55900                         var markup = ct.apply(p);
55901                         if(!c.locked){
55902                             cb+= markup;
55903                         }else{
55904                             lcb+= markup;
55905                         }
55906                     }
55907                     var alt = [];
55908                     if(stripe && ((rowIndex+1) % 2 == 0)){
55909                         alt.push("x-grid-row-alt")
55910                     }
55911                     if(r.dirty){
55912                         alt.push(  " x-grid-dirty-row");
55913                     }
55914                     rp.cells = lcb;
55915                     if(this.getRowClass){
55916                         alt.push(this.getRowClass(r, rowIndex));
55917                     }
55918                     if (hasListener) {
55919                         rowcfg = {
55920                              
55921                             record: r,
55922                             rowIndex : rowIndex,
55923                             rowClass : ''
55924                         };
55925                         this.grid.fireEvent('rowclass', this, rowcfg);
55926                         alt.push(rowcfg.rowClass);
55927                     }
55928                     rp.alt = alt.join(" ");
55929                     lbuf+= rt.apply(rp);
55930                     rp.cells = cb;
55931                     buf+=  rt.apply(rp);
55932                 }
55933                 return [lbuf, buf];
55934             } :
55935             function(cs, rs, ds, startRow, colCount, stripe){
55936                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55937                 // buffers
55938                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55939                 var hasListener = this.grid.hasListener('rowclass');
55940  
55941                 var rowcfg = {};
55942                 for(var j = 0, len = rs.length; j < len; j++){
55943                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55944                     for(var i = 0; i < colCount; i++){
55945                         c = cs[i];
55946                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55947                         p.id = c.id;
55948                         p.css = p.attr = "";
55949                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55950                         if(p.value == undefined || p.value === "") {
55951                             p.value = "&#160;";
55952                         }
55953                         //Roo.log(c);
55954                          if(c.has_editor){
55955                             p.css += ' x-grid-editable-cell';
55956                         }
55957                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55958                             p.css += ' x-grid-dirty-cell' 
55959                         }
55960                         
55961                         var markup = ct.apply(p);
55962                         if(!c.locked){
55963                             cb[cb.length] = markup;
55964                         }else{
55965                             lcb[lcb.length] = markup;
55966                         }
55967                     }
55968                     var alt = [];
55969                     if(stripe && ((rowIndex+1) % 2 == 0)){
55970                         alt.push( "x-grid-row-alt");
55971                     }
55972                     if(r.dirty){
55973                         alt.push(" x-grid-dirty-row");
55974                     }
55975                     rp.cells = lcb;
55976                     if(this.getRowClass){
55977                         alt.push( this.getRowClass(r, rowIndex));
55978                     }
55979                     if (hasListener) {
55980                         rowcfg = {
55981                              
55982                             record: r,
55983                             rowIndex : rowIndex,
55984                             rowClass : ''
55985                         };
55986                         this.grid.fireEvent('rowclass', this, rowcfg);
55987                         alt.push(rowcfg.rowClass);
55988                     }
55989                     
55990                     rp.alt = alt.join(" ");
55991                     rp.cells = lcb.join("");
55992                     lbuf[lbuf.length] = rt.apply(rp);
55993                     rp.cells = cb.join("");
55994                     buf[buf.length] =  rt.apply(rp);
55995                 }
55996                 return [lbuf.join(""), buf.join("")];
55997             },
55998
55999     renderBody : function(){
56000         var markup = this.renderRows();
56001         var bt = this.templates.body;
56002         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56003     },
56004
56005     /**
56006      * Refreshes the grid
56007      * @param {Boolean} headersToo
56008      */
56009     refresh : function(headersToo){
56010         this.fireEvent("beforerefresh", this);
56011         this.grid.stopEditing();
56012         var result = this.renderBody();
56013         this.lockedBody.update(result[0]);
56014         this.mainBody.update(result[1]);
56015         if(headersToo === true){
56016             this.updateHeaders();
56017             this.updateColumns();
56018             this.updateSplitters();
56019             this.updateHeaderSortState();
56020         }
56021         this.syncRowHeights();
56022         this.layout();
56023         this.fireEvent("refresh", this);
56024     },
56025
56026     handleColumnMove : function(cm, oldIndex, newIndex){
56027         this.indexMap = null;
56028         var s = this.getScrollState();
56029         this.refresh(true);
56030         this.restoreScroll(s);
56031         this.afterMove(newIndex);
56032     },
56033
56034     afterMove : function(colIndex){
56035         if(this.enableMoveAnim && Roo.enableFx){
56036             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56037         }
56038         // if multisort - fix sortOrder, and reload..
56039         if (this.grid.dataSource.multiSort) {
56040             // the we can call sort again..
56041             var dm = this.grid.dataSource;
56042             var cm = this.grid.colModel;
56043             var so = [];
56044             for(var i = 0; i < cm.config.length; i++ ) {
56045                 
56046                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56047                     continue; // dont' bother, it's not in sort list or being set.
56048                 }
56049                 
56050                 so.push(cm.config[i].dataIndex);
56051             };
56052             dm.sortOrder = so;
56053             dm.load(dm.lastOptions);
56054             
56055             
56056         }
56057         
56058     },
56059
56060     updateCell : function(dm, rowIndex, dataIndex){
56061         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56062         if(typeof colIndex == "undefined"){ // not present in grid
56063             return;
56064         }
56065         var cm = this.grid.colModel;
56066         var cell = this.getCell(rowIndex, colIndex);
56067         var cellText = this.getCellText(rowIndex, colIndex);
56068
56069         var p = {
56070             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56071             id : cm.getColumnId(colIndex),
56072             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56073         };
56074         var renderer = cm.getRenderer(colIndex);
56075         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56076         if(typeof val == "undefined" || val === "") {
56077             val = "&#160;";
56078         }
56079         cellText.innerHTML = val;
56080         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56081         this.syncRowHeights(rowIndex, rowIndex);
56082     },
56083
56084     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56085         var maxWidth = 0;
56086         if(this.grid.autoSizeHeaders){
56087             var h = this.getHeaderCellMeasure(colIndex);
56088             maxWidth = Math.max(maxWidth, h.scrollWidth);
56089         }
56090         var tb, index;
56091         if(this.cm.isLocked(colIndex)){
56092             tb = this.getLockedTable();
56093             index = colIndex;
56094         }else{
56095             tb = this.getBodyTable();
56096             index = colIndex - this.cm.getLockedCount();
56097         }
56098         if(tb && tb.rows){
56099             var rows = tb.rows;
56100             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56101             for(var i = 0; i < stopIndex; i++){
56102                 var cell = rows[i].childNodes[index].firstChild;
56103                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56104             }
56105         }
56106         return maxWidth + /*margin for error in IE*/ 5;
56107     },
56108     /**
56109      * Autofit a column to its content.
56110      * @param {Number} colIndex
56111      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56112      */
56113      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56114          if(this.cm.isHidden(colIndex)){
56115              return; // can't calc a hidden column
56116          }
56117         if(forceMinSize){
56118             var cid = this.cm.getColumnId(colIndex);
56119             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56120            if(this.grid.autoSizeHeaders){
56121                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56122            }
56123         }
56124         var newWidth = this.calcColumnWidth(colIndex);
56125         this.cm.setColumnWidth(colIndex,
56126             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56127         if(!suppressEvent){
56128             this.grid.fireEvent("columnresize", colIndex, newWidth);
56129         }
56130     },
56131
56132     /**
56133      * Autofits all columns to their content and then expands to fit any extra space in the grid
56134      */
56135      autoSizeColumns : function(){
56136         var cm = this.grid.colModel;
56137         var colCount = cm.getColumnCount();
56138         for(var i = 0; i < colCount; i++){
56139             this.autoSizeColumn(i, true, true);
56140         }
56141         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56142             this.fitColumns();
56143         }else{
56144             this.updateColumns();
56145             this.layout();
56146         }
56147     },
56148
56149     /**
56150      * Autofits all columns to the grid's width proportionate with their current size
56151      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56152      */
56153     fitColumns : function(reserveScrollSpace){
56154         var cm = this.grid.colModel;
56155         var colCount = cm.getColumnCount();
56156         var cols = [];
56157         var width = 0;
56158         var i, w;
56159         for (i = 0; i < colCount; i++){
56160             if(!cm.isHidden(i) && !cm.isFixed(i)){
56161                 w = cm.getColumnWidth(i);
56162                 cols.push(i);
56163                 cols.push(w);
56164                 width += w;
56165             }
56166         }
56167         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56168         if(reserveScrollSpace){
56169             avail -= 17;
56170         }
56171         var frac = (avail - cm.getTotalWidth())/width;
56172         while (cols.length){
56173             w = cols.pop();
56174             i = cols.pop();
56175             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56176         }
56177         this.updateColumns();
56178         this.layout();
56179     },
56180
56181     onRowSelect : function(rowIndex){
56182         var row = this.getRowComposite(rowIndex);
56183         row.addClass("x-grid-row-selected");
56184     },
56185
56186     onRowDeselect : function(rowIndex){
56187         var row = this.getRowComposite(rowIndex);
56188         row.removeClass("x-grid-row-selected");
56189     },
56190
56191     onCellSelect : function(row, col){
56192         var cell = this.getCell(row, col);
56193         if(cell){
56194             Roo.fly(cell).addClass("x-grid-cell-selected");
56195         }
56196     },
56197
56198     onCellDeselect : function(row, col){
56199         var cell = this.getCell(row, col);
56200         if(cell){
56201             Roo.fly(cell).removeClass("x-grid-cell-selected");
56202         }
56203     },
56204
56205     updateHeaderSortState : function(){
56206         
56207         // sort state can be single { field: xxx, direction : yyy}
56208         // or   { xxx=>ASC , yyy : DESC ..... }
56209         
56210         var mstate = {};
56211         if (!this.ds.multiSort) { 
56212             var state = this.ds.getSortState();
56213             if(!state){
56214                 return;
56215             }
56216             mstate[state.field] = state.direction;
56217             // FIXME... - this is not used here.. but might be elsewhere..
56218             this.sortState = state;
56219             
56220         } else {
56221             mstate = this.ds.sortToggle;
56222         }
56223         //remove existing sort classes..
56224         
56225         var sc = this.sortClasses;
56226         var hds = this.el.select(this.headerSelector).removeClass(sc);
56227         
56228         for(var f in mstate) {
56229         
56230             var sortColumn = this.cm.findColumnIndex(f);
56231             
56232             if(sortColumn != -1){
56233                 var sortDir = mstate[f];        
56234                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56235             }
56236         }
56237         
56238          
56239         
56240     },
56241
56242
56243     handleHeaderClick : function(g, index,e){
56244         
56245         Roo.log("header click");
56246         
56247         if (Roo.isTouch) {
56248             // touch events on header are handled by context
56249             this.handleHdCtx(g,index,e);
56250             return;
56251         }
56252         
56253         
56254         if(this.headersDisabled){
56255             return;
56256         }
56257         var dm = g.dataSource, cm = g.colModel;
56258         if(!cm.isSortable(index)){
56259             return;
56260         }
56261         g.stopEditing();
56262         
56263         if (dm.multiSort) {
56264             // update the sortOrder
56265             var so = [];
56266             for(var i = 0; i < cm.config.length; i++ ) {
56267                 
56268                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56269                     continue; // dont' bother, it's not in sort list or being set.
56270                 }
56271                 
56272                 so.push(cm.config[i].dataIndex);
56273             };
56274             dm.sortOrder = so;
56275         }
56276         
56277         
56278         dm.sort(cm.getDataIndex(index));
56279     },
56280
56281
56282     destroy : function(){
56283         if(this.colMenu){
56284             this.colMenu.removeAll();
56285             Roo.menu.MenuMgr.unregister(this.colMenu);
56286             this.colMenu.getEl().remove();
56287             delete this.colMenu;
56288         }
56289         if(this.hmenu){
56290             this.hmenu.removeAll();
56291             Roo.menu.MenuMgr.unregister(this.hmenu);
56292             this.hmenu.getEl().remove();
56293             delete this.hmenu;
56294         }
56295         if(this.grid.enableColumnMove){
56296             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56297             if(dds){
56298                 for(var dd in dds){
56299                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56300                         var elid = dds[dd].dragElId;
56301                         dds[dd].unreg();
56302                         Roo.get(elid).remove();
56303                     } else if(dds[dd].config.isTarget){
56304                         dds[dd].proxyTop.remove();
56305                         dds[dd].proxyBottom.remove();
56306                         dds[dd].unreg();
56307                     }
56308                     if(Roo.dd.DDM.locationCache[dd]){
56309                         delete Roo.dd.DDM.locationCache[dd];
56310                     }
56311                 }
56312                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56313             }
56314         }
56315         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56316         this.bind(null, null);
56317         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56318     },
56319
56320     handleLockChange : function(){
56321         this.refresh(true);
56322     },
56323
56324     onDenyColumnLock : function(){
56325
56326     },
56327
56328     onDenyColumnHide : function(){
56329
56330     },
56331
56332     handleHdMenuClick : function(item){
56333         var index = this.hdCtxIndex;
56334         var cm = this.cm, ds = this.ds;
56335         switch(item.id){
56336             case "asc":
56337                 ds.sort(cm.getDataIndex(index), "ASC");
56338                 break;
56339             case "desc":
56340                 ds.sort(cm.getDataIndex(index), "DESC");
56341                 break;
56342             case "lock":
56343                 var lc = cm.getLockedCount();
56344                 if(cm.getColumnCount(true) <= lc+1){
56345                     this.onDenyColumnLock();
56346                     return;
56347                 }
56348                 if(lc != index){
56349                     cm.setLocked(index, true, true);
56350                     cm.moveColumn(index, lc);
56351                     this.grid.fireEvent("columnmove", index, lc);
56352                 }else{
56353                     cm.setLocked(index, true);
56354                 }
56355             break;
56356             case "unlock":
56357                 var lc = cm.getLockedCount();
56358                 if((lc-1) != index){
56359                     cm.setLocked(index, false, true);
56360                     cm.moveColumn(index, lc-1);
56361                     this.grid.fireEvent("columnmove", index, lc-1);
56362                 }else{
56363                     cm.setLocked(index, false);
56364                 }
56365             break;
56366             case 'wider': // used to expand cols on touch..
56367             case 'narrow':
56368                 var cw = cm.getColumnWidth(index);
56369                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56370                 cw = Math.max(0, cw);
56371                 cw = Math.min(cw,4000);
56372                 cm.setColumnWidth(index, cw);
56373                 break;
56374                 
56375             default:
56376                 index = cm.getIndexById(item.id.substr(4));
56377                 if(index != -1){
56378                     if(item.checked && cm.getColumnCount(true) <= 1){
56379                         this.onDenyColumnHide();
56380                         return false;
56381                     }
56382                     cm.setHidden(index, item.checked);
56383                 }
56384         }
56385         return true;
56386     },
56387
56388     beforeColMenuShow : function(){
56389         var cm = this.cm,  colCount = cm.getColumnCount();
56390         this.colMenu.removeAll();
56391         for(var i = 0; i < colCount; i++){
56392             this.colMenu.add(new Roo.menu.CheckItem({
56393                 id: "col-"+cm.getColumnId(i),
56394                 text: cm.getColumnHeader(i),
56395                 checked: !cm.isHidden(i),
56396                 hideOnClick:false
56397             }));
56398         }
56399     },
56400
56401     handleHdCtx : function(g, index, e){
56402         e.stopEvent();
56403         var hd = this.getHeaderCell(index);
56404         this.hdCtxIndex = index;
56405         var ms = this.hmenu.items, cm = this.cm;
56406         ms.get("asc").setDisabled(!cm.isSortable(index));
56407         ms.get("desc").setDisabled(!cm.isSortable(index));
56408         if(this.grid.enableColLock !== false){
56409             ms.get("lock").setDisabled(cm.isLocked(index));
56410             ms.get("unlock").setDisabled(!cm.isLocked(index));
56411         }
56412         this.hmenu.show(hd, "tl-bl");
56413     },
56414
56415     handleHdOver : function(e){
56416         var hd = this.findHeaderCell(e.getTarget());
56417         if(hd && !this.headersDisabled){
56418             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56419                this.fly(hd).addClass("x-grid-hd-over");
56420             }
56421         }
56422     },
56423
56424     handleHdOut : function(e){
56425         var hd = this.findHeaderCell(e.getTarget());
56426         if(hd){
56427             this.fly(hd).removeClass("x-grid-hd-over");
56428         }
56429     },
56430
56431     handleSplitDblClick : function(e, t){
56432         var i = this.getCellIndex(t);
56433         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56434             this.autoSizeColumn(i, true);
56435             this.layout();
56436         }
56437     },
56438
56439     render : function(){
56440
56441         var cm = this.cm;
56442         var colCount = cm.getColumnCount();
56443
56444         if(this.grid.monitorWindowResize === true){
56445             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56446         }
56447         var header = this.renderHeaders();
56448         var body = this.templates.body.apply({rows:""});
56449         var html = this.templates.master.apply({
56450             lockedBody: body,
56451             body: body,
56452             lockedHeader: header[0],
56453             header: header[1]
56454         });
56455
56456         //this.updateColumns();
56457
56458         this.grid.getGridEl().dom.innerHTML = html;
56459
56460         this.initElements();
56461         
56462         // a kludge to fix the random scolling effect in webkit
56463         this.el.on("scroll", function() {
56464             this.el.dom.scrollTop=0; // hopefully not recursive..
56465         },this);
56466
56467         this.scroller.on("scroll", this.handleScroll, this);
56468         this.lockedBody.on("mousewheel", this.handleWheel, this);
56469         this.mainBody.on("mousewheel", this.handleWheel, this);
56470
56471         this.mainHd.on("mouseover", this.handleHdOver, this);
56472         this.mainHd.on("mouseout", this.handleHdOut, this);
56473         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56474                 {delegate: "."+this.splitClass});
56475
56476         this.lockedHd.on("mouseover", this.handleHdOver, this);
56477         this.lockedHd.on("mouseout", this.handleHdOut, this);
56478         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56479                 {delegate: "."+this.splitClass});
56480
56481         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56482             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56483         }
56484
56485         this.updateSplitters();
56486
56487         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56488             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56489             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56490         }
56491
56492         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56493             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56494             this.hmenu.add(
56495                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56496                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56497             );
56498             if(this.grid.enableColLock !== false){
56499                 this.hmenu.add('-',
56500                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56501                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56502                 );
56503             }
56504             if (Roo.isTouch) {
56505                  this.hmenu.add('-',
56506                     {id:"wider", text: this.columnsWiderText},
56507                     {id:"narrow", text: this.columnsNarrowText }
56508                 );
56509                 
56510                  
56511             }
56512             
56513             if(this.grid.enableColumnHide !== false){
56514
56515                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56516                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56517                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56518
56519                 this.hmenu.add('-',
56520                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56521                 );
56522             }
56523             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56524
56525             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56526         }
56527
56528         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56529             this.dd = new Roo.grid.GridDragZone(this.grid, {
56530                 ddGroup : this.grid.ddGroup || 'GridDD'
56531             });
56532             
56533         }
56534
56535         /*
56536         for(var i = 0; i < colCount; i++){
56537             if(cm.isHidden(i)){
56538                 this.hideColumn(i);
56539             }
56540             if(cm.config[i].align){
56541                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56542                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56543             }
56544         }*/
56545         
56546         this.updateHeaderSortState();
56547
56548         this.beforeInitialResize();
56549         this.layout(true);
56550
56551         // two part rendering gives faster view to the user
56552         this.renderPhase2.defer(1, this);
56553     },
56554
56555     renderPhase2 : function(){
56556         // render the rows now
56557         this.refresh();
56558         if(this.grid.autoSizeColumns){
56559             this.autoSizeColumns();
56560         }
56561     },
56562
56563     beforeInitialResize : function(){
56564
56565     },
56566
56567     onColumnSplitterMoved : function(i, w){
56568         this.userResized = true;
56569         var cm = this.grid.colModel;
56570         cm.setColumnWidth(i, w, true);
56571         var cid = cm.getColumnId(i);
56572         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56573         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56574         this.updateSplitters();
56575         this.layout();
56576         this.grid.fireEvent("columnresize", i, w);
56577     },
56578
56579     syncRowHeights : function(startIndex, endIndex){
56580         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56581             startIndex = startIndex || 0;
56582             var mrows = this.getBodyTable().rows;
56583             var lrows = this.getLockedTable().rows;
56584             var len = mrows.length-1;
56585             endIndex = Math.min(endIndex || len, len);
56586             for(var i = startIndex; i <= endIndex; i++){
56587                 var m = mrows[i], l = lrows[i];
56588                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56589                 m.style.height = l.style.height = h + "px";
56590             }
56591         }
56592     },
56593
56594     layout : function(initialRender, is2ndPass){
56595         var g = this.grid;
56596         var auto = g.autoHeight;
56597         var scrollOffset = 16;
56598         var c = g.getGridEl(), cm = this.cm,
56599                 expandCol = g.autoExpandColumn,
56600                 gv = this;
56601         //c.beginMeasure();
56602
56603         if(!c.dom.offsetWidth){ // display:none?
56604             if(initialRender){
56605                 this.lockedWrap.show();
56606                 this.mainWrap.show();
56607             }
56608             return;
56609         }
56610
56611         var hasLock = this.cm.isLocked(0);
56612
56613         var tbh = this.headerPanel.getHeight();
56614         var bbh = this.footerPanel.getHeight();
56615
56616         if(auto){
56617             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56618             var newHeight = ch + c.getBorderWidth("tb");
56619             if(g.maxHeight){
56620                 newHeight = Math.min(g.maxHeight, newHeight);
56621             }
56622             c.setHeight(newHeight);
56623         }
56624
56625         if(g.autoWidth){
56626             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56627         }
56628
56629         var s = this.scroller;
56630
56631         var csize = c.getSize(true);
56632
56633         this.el.setSize(csize.width, csize.height);
56634
56635         this.headerPanel.setWidth(csize.width);
56636         this.footerPanel.setWidth(csize.width);
56637
56638         var hdHeight = this.mainHd.getHeight();
56639         var vw = csize.width;
56640         var vh = csize.height - (tbh + bbh);
56641
56642         s.setSize(vw, vh);
56643
56644         var bt = this.getBodyTable();
56645         
56646         if(cm.getLockedCount() == cm.config.length){
56647             bt = this.getLockedTable();
56648         }
56649         
56650         var ltWidth = hasLock ?
56651                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56652
56653         var scrollHeight = bt.offsetHeight;
56654         var scrollWidth = ltWidth + bt.offsetWidth;
56655         var vscroll = false, hscroll = false;
56656
56657         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56658
56659         var lw = this.lockedWrap, mw = this.mainWrap;
56660         var lb = this.lockedBody, mb = this.mainBody;
56661
56662         setTimeout(function(){
56663             var t = s.dom.offsetTop;
56664             var w = s.dom.clientWidth,
56665                 h = s.dom.clientHeight;
56666
56667             lw.setTop(t);
56668             lw.setSize(ltWidth, h);
56669
56670             mw.setLeftTop(ltWidth, t);
56671             mw.setSize(w-ltWidth, h);
56672
56673             lb.setHeight(h-hdHeight);
56674             mb.setHeight(h-hdHeight);
56675
56676             if(is2ndPass !== true && !gv.userResized && expandCol){
56677                 // high speed resize without full column calculation
56678                 
56679                 var ci = cm.getIndexById(expandCol);
56680                 if (ci < 0) {
56681                     ci = cm.findColumnIndex(expandCol);
56682                 }
56683                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56684                 var expandId = cm.getColumnId(ci);
56685                 var  tw = cm.getTotalWidth(false);
56686                 var currentWidth = cm.getColumnWidth(ci);
56687                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56688                 if(currentWidth != cw){
56689                     cm.setColumnWidth(ci, cw, true);
56690                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56691                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56692                     gv.updateSplitters();
56693                     gv.layout(false, true);
56694                 }
56695             }
56696
56697             if(initialRender){
56698                 lw.show();
56699                 mw.show();
56700             }
56701             //c.endMeasure();
56702         }, 10);
56703     },
56704
56705     onWindowResize : function(){
56706         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56707             return;
56708         }
56709         this.layout();
56710     },
56711
56712     appendFooter : function(parentEl){
56713         return null;
56714     },
56715
56716     sortAscText : "Sort Ascending",
56717     sortDescText : "Sort Descending",
56718     lockText : "Lock Column",
56719     unlockText : "Unlock Column",
56720     columnsText : "Columns",
56721  
56722     columnsWiderText : "Wider",
56723     columnsNarrowText : "Thinner"
56724 });
56725
56726
56727 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56728     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56729     this.proxy.el.addClass('x-grid3-col-dd');
56730 };
56731
56732 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56733     handleMouseDown : function(e){
56734
56735     },
56736
56737     callHandleMouseDown : function(e){
56738         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56739     }
56740 });
56741 /*
56742  * Based on:
56743  * Ext JS Library 1.1.1
56744  * Copyright(c) 2006-2007, Ext JS, LLC.
56745  *
56746  * Originally Released Under LGPL - original licence link has changed is not relivant.
56747  *
56748  * Fork - LGPL
56749  * <script type="text/javascript">
56750  */
56751  
56752 // private
56753 // This is a support class used internally by the Grid components
56754 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56755     this.grid = grid;
56756     this.view = grid.getView();
56757     this.proxy = this.view.resizeProxy;
56758     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56759         "gridSplitters" + this.grid.getGridEl().id, {
56760         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56761     });
56762     this.setHandleElId(Roo.id(hd));
56763     this.setOuterHandleElId(Roo.id(hd2));
56764     this.scroll = false;
56765 };
56766 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56767     fly: Roo.Element.fly,
56768
56769     b4StartDrag : function(x, y){
56770         this.view.headersDisabled = true;
56771         this.proxy.setHeight(this.view.mainWrap.getHeight());
56772         var w = this.cm.getColumnWidth(this.cellIndex);
56773         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56774         this.resetConstraints();
56775         this.setXConstraint(minw, 1000);
56776         this.setYConstraint(0, 0);
56777         this.minX = x - minw;
56778         this.maxX = x + 1000;
56779         this.startPos = x;
56780         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56781     },
56782
56783
56784     handleMouseDown : function(e){
56785         ev = Roo.EventObject.setEvent(e);
56786         var t = this.fly(ev.getTarget());
56787         if(t.hasClass("x-grid-split")){
56788             this.cellIndex = this.view.getCellIndex(t.dom);
56789             this.split = t.dom;
56790             this.cm = this.grid.colModel;
56791             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56792                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56793             }
56794         }
56795     },
56796
56797     endDrag : function(e){
56798         this.view.headersDisabled = false;
56799         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56800         var diff = endX - this.startPos;
56801         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56802     },
56803
56804     autoOffset : function(){
56805         this.setDelta(0,0);
56806     }
56807 });/*
56808  * Based on:
56809  * Ext JS Library 1.1.1
56810  * Copyright(c) 2006-2007, Ext JS, LLC.
56811  *
56812  * Originally Released Under LGPL - original licence link has changed is not relivant.
56813  *
56814  * Fork - LGPL
56815  * <script type="text/javascript">
56816  */
56817  
56818 // private
56819 // This is a support class used internally by the Grid components
56820 Roo.grid.GridDragZone = function(grid, config){
56821     this.view = grid.getView();
56822     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56823     if(this.view.lockedBody){
56824         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56825         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56826     }
56827     this.scroll = false;
56828     this.grid = grid;
56829     this.ddel = document.createElement('div');
56830     this.ddel.className = 'x-grid-dd-wrap';
56831 };
56832
56833 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56834     ddGroup : "GridDD",
56835
56836     getDragData : function(e){
56837         var t = Roo.lib.Event.getTarget(e);
56838         var rowIndex = this.view.findRowIndex(t);
56839         var sm = this.grid.selModel;
56840             
56841         //Roo.log(rowIndex);
56842         
56843         if (sm.getSelectedCell) {
56844             // cell selection..
56845             if (!sm.getSelectedCell()) {
56846                 return false;
56847             }
56848             if (rowIndex != sm.getSelectedCell()[0]) {
56849                 return false;
56850             }
56851         
56852         }
56853         
56854         if(rowIndex !== false){
56855             
56856             // if editorgrid.. 
56857             
56858             
56859             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56860                
56861             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56862               //  
56863             //}
56864             if (e.hasModifier()){
56865                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56866             }
56867             
56868             Roo.log("getDragData");
56869             
56870             return {
56871                 grid: this.grid,
56872                 ddel: this.ddel,
56873                 rowIndex: rowIndex,
56874                 selections:sm.getSelections ? sm.getSelections() : (
56875                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56876                 )
56877             };
56878         }
56879         return false;
56880     },
56881
56882     onInitDrag : function(e){
56883         var data = this.dragData;
56884         this.ddel.innerHTML = this.grid.getDragDropText();
56885         this.proxy.update(this.ddel);
56886         // fire start drag?
56887     },
56888
56889     afterRepair : function(){
56890         this.dragging = false;
56891     },
56892
56893     getRepairXY : function(e, data){
56894         return false;
56895     },
56896
56897     onEndDrag : function(data, e){
56898         // fire end drag?
56899     },
56900
56901     onValidDrop : function(dd, e, id){
56902         // fire drag drop?
56903         this.hideProxy();
56904     },
56905
56906     beforeInvalidDrop : function(e, id){
56907
56908     }
56909 });/*
56910  * Based on:
56911  * Ext JS Library 1.1.1
56912  * Copyright(c) 2006-2007, Ext JS, LLC.
56913  *
56914  * Originally Released Under LGPL - original licence link has changed is not relivant.
56915  *
56916  * Fork - LGPL
56917  * <script type="text/javascript">
56918  */
56919  
56920
56921 /**
56922  * @class Roo.grid.ColumnModel
56923  * @extends Roo.util.Observable
56924  * This is the default implementation of a ColumnModel used by the Grid. It defines
56925  * the columns in the grid.
56926  * <br>Usage:<br>
56927  <pre><code>
56928  var colModel = new Roo.grid.ColumnModel([
56929         {header: "Ticker", width: 60, sortable: true, locked: true},
56930         {header: "Company Name", width: 150, sortable: true},
56931         {header: "Market Cap.", width: 100, sortable: true},
56932         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56933         {header: "Employees", width: 100, sortable: true, resizable: false}
56934  ]);
56935  </code></pre>
56936  * <p>
56937  
56938  * The config options listed for this class are options which may appear in each
56939  * individual column definition.
56940  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56941  * @constructor
56942  * @param {Object} config An Array of column config objects. See this class's
56943  * config objects for details.
56944 */
56945 Roo.grid.ColumnModel = function(config){
56946         /**
56947      * The config passed into the constructor
56948      */
56949     this.config = config;
56950     this.lookup = {};
56951
56952     // if no id, create one
56953     // if the column does not have a dataIndex mapping,
56954     // map it to the order it is in the config
56955     for(var i = 0, len = config.length; i < len; i++){
56956         var c = config[i];
56957         if(typeof c.dataIndex == "undefined"){
56958             c.dataIndex = i;
56959         }
56960         if(typeof c.renderer == "string"){
56961             c.renderer = Roo.util.Format[c.renderer];
56962         }
56963         if(typeof c.id == "undefined"){
56964             c.id = Roo.id();
56965         }
56966         if(c.editor && c.editor.xtype){
56967             c.editor  = Roo.factory(c.editor, Roo.grid);
56968         }
56969         if(c.editor && c.editor.isFormField){
56970             c.editor = new Roo.grid.GridEditor(c.editor);
56971         }
56972         this.lookup[c.id] = c;
56973     }
56974
56975     /**
56976      * The width of columns which have no width specified (defaults to 100)
56977      * @type Number
56978      */
56979     this.defaultWidth = 100;
56980
56981     /**
56982      * Default sortable of columns which have no sortable specified (defaults to false)
56983      * @type Boolean
56984      */
56985     this.defaultSortable = false;
56986
56987     this.addEvents({
56988         /**
56989              * @event widthchange
56990              * Fires when the width of a column changes.
56991              * @param {ColumnModel} this
56992              * @param {Number} columnIndex The column index
56993              * @param {Number} newWidth The new width
56994              */
56995             "widthchange": true,
56996         /**
56997              * @event headerchange
56998              * Fires when the text of a header changes.
56999              * @param {ColumnModel} this
57000              * @param {Number} columnIndex The column index
57001              * @param {Number} newText The new header text
57002              */
57003             "headerchange": true,
57004         /**
57005              * @event hiddenchange
57006              * Fires when a column is hidden or "unhidden".
57007              * @param {ColumnModel} this
57008              * @param {Number} columnIndex The column index
57009              * @param {Boolean} hidden true if hidden, false otherwise
57010              */
57011             "hiddenchange": true,
57012             /**
57013          * @event columnmoved
57014          * Fires when a column is moved.
57015          * @param {ColumnModel} this
57016          * @param {Number} oldIndex
57017          * @param {Number} newIndex
57018          */
57019         "columnmoved" : true,
57020         /**
57021          * @event columlockchange
57022          * Fires when a column's locked state is changed
57023          * @param {ColumnModel} this
57024          * @param {Number} colIndex
57025          * @param {Boolean} locked true if locked
57026          */
57027         "columnlockchange" : true
57028     });
57029     Roo.grid.ColumnModel.superclass.constructor.call(this);
57030 };
57031 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57032     /**
57033      * @cfg {String} header The header text to display in the Grid view.
57034      */
57035     /**
57036      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57037      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57038      * specified, the column's index is used as an index into the Record's data Array.
57039      */
57040     /**
57041      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57042      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57043      */
57044     /**
57045      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57046      * Defaults to the value of the {@link #defaultSortable} property.
57047      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57048      */
57049     /**
57050      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57051      */
57052     /**
57053      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57054      */
57055     /**
57056      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57057      */
57058     /**
57059      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57060      */
57061     /**
57062      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57063      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57064      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57065      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57066      */
57067        /**
57068      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57069      */
57070     /**
57071      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57072      */
57073     /**
57074      * @cfg {String} cursor (Optional)
57075      */
57076     /**
57077      * @cfg {String} tooltip (Optional)
57078      */
57079     /**
57080      * @cfg {Number} xs (Optional)
57081      */
57082     /**
57083      * @cfg {Number} sm (Optional)
57084      */
57085     /**
57086      * @cfg {Number} md (Optional)
57087      */
57088     /**
57089      * @cfg {Number} lg (Optional)
57090      */
57091     /**
57092      * Returns the id of the column at the specified index.
57093      * @param {Number} index The column index
57094      * @return {String} the id
57095      */
57096     getColumnId : function(index){
57097         return this.config[index].id;
57098     },
57099
57100     /**
57101      * Returns the column for a specified id.
57102      * @param {String} id The column id
57103      * @return {Object} the column
57104      */
57105     getColumnById : function(id){
57106         return this.lookup[id];
57107     },
57108
57109     
57110     /**
57111      * Returns the column for a specified dataIndex.
57112      * @param {String} dataIndex The column dataIndex
57113      * @return {Object|Boolean} the column or false if not found
57114      */
57115     getColumnByDataIndex: function(dataIndex){
57116         var index = this.findColumnIndex(dataIndex);
57117         return index > -1 ? this.config[index] : false;
57118     },
57119     
57120     /**
57121      * Returns the index for a specified column id.
57122      * @param {String} id The column id
57123      * @return {Number} the index, or -1 if not found
57124      */
57125     getIndexById : function(id){
57126         for(var i = 0, len = this.config.length; i < len; i++){
57127             if(this.config[i].id == id){
57128                 return i;
57129             }
57130         }
57131         return -1;
57132     },
57133     
57134     /**
57135      * Returns the index for a specified column dataIndex.
57136      * @param {String} dataIndex The column dataIndex
57137      * @return {Number} the index, or -1 if not found
57138      */
57139     
57140     findColumnIndex : function(dataIndex){
57141         for(var i = 0, len = this.config.length; i < len; i++){
57142             if(this.config[i].dataIndex == dataIndex){
57143                 return i;
57144             }
57145         }
57146         return -1;
57147     },
57148     
57149     
57150     moveColumn : function(oldIndex, newIndex){
57151         var c = this.config[oldIndex];
57152         this.config.splice(oldIndex, 1);
57153         this.config.splice(newIndex, 0, c);
57154         this.dataMap = null;
57155         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57156     },
57157
57158     isLocked : function(colIndex){
57159         return this.config[colIndex].locked === true;
57160     },
57161
57162     setLocked : function(colIndex, value, suppressEvent){
57163         if(this.isLocked(colIndex) == value){
57164             return;
57165         }
57166         this.config[colIndex].locked = value;
57167         if(!suppressEvent){
57168             this.fireEvent("columnlockchange", this, colIndex, value);
57169         }
57170     },
57171
57172     getTotalLockedWidth : function(){
57173         var totalWidth = 0;
57174         for(var i = 0; i < this.config.length; i++){
57175             if(this.isLocked(i) && !this.isHidden(i)){
57176                 this.totalWidth += this.getColumnWidth(i);
57177             }
57178         }
57179         return totalWidth;
57180     },
57181
57182     getLockedCount : function(){
57183         for(var i = 0, len = this.config.length; i < len; i++){
57184             if(!this.isLocked(i)){
57185                 return i;
57186             }
57187         }
57188         
57189         return this.config.length;
57190     },
57191
57192     /**
57193      * Returns the number of columns.
57194      * @return {Number}
57195      */
57196     getColumnCount : function(visibleOnly){
57197         if(visibleOnly === true){
57198             var c = 0;
57199             for(var i = 0, len = this.config.length; i < len; i++){
57200                 if(!this.isHidden(i)){
57201                     c++;
57202                 }
57203             }
57204             return c;
57205         }
57206         return this.config.length;
57207     },
57208
57209     /**
57210      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57211      * @param {Function} fn
57212      * @param {Object} scope (optional)
57213      * @return {Array} result
57214      */
57215     getColumnsBy : function(fn, scope){
57216         var r = [];
57217         for(var i = 0, len = this.config.length; i < len; i++){
57218             var c = this.config[i];
57219             if(fn.call(scope||this, c, i) === true){
57220                 r[r.length] = c;
57221             }
57222         }
57223         return r;
57224     },
57225
57226     /**
57227      * Returns true if the specified column is sortable.
57228      * @param {Number} col The column index
57229      * @return {Boolean}
57230      */
57231     isSortable : function(col){
57232         if(typeof this.config[col].sortable == "undefined"){
57233             return this.defaultSortable;
57234         }
57235         return this.config[col].sortable;
57236     },
57237
57238     /**
57239      * Returns the rendering (formatting) function defined for the column.
57240      * @param {Number} col The column index.
57241      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57242      */
57243     getRenderer : function(col){
57244         if(!this.config[col].renderer){
57245             return Roo.grid.ColumnModel.defaultRenderer;
57246         }
57247         return this.config[col].renderer;
57248     },
57249
57250     /**
57251      * Sets the rendering (formatting) function for a column.
57252      * @param {Number} col The column index
57253      * @param {Function} fn The function to use to process the cell's raw data
57254      * to return HTML markup for the grid view. The render function is called with
57255      * the following parameters:<ul>
57256      * <li>Data value.</li>
57257      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57258      * <li>css A CSS style string to apply to the table cell.</li>
57259      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57260      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57261      * <li>Row index</li>
57262      * <li>Column index</li>
57263      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57264      */
57265     setRenderer : function(col, fn){
57266         this.config[col].renderer = fn;
57267     },
57268
57269     /**
57270      * Returns the width for the specified column.
57271      * @param {Number} col The column index
57272      * @return {Number}
57273      */
57274     getColumnWidth : function(col){
57275         return this.config[col].width * 1 || this.defaultWidth;
57276     },
57277
57278     /**
57279      * Sets the width for a column.
57280      * @param {Number} col The column index
57281      * @param {Number} width The new width
57282      */
57283     setColumnWidth : function(col, width, suppressEvent){
57284         this.config[col].width = width;
57285         this.totalWidth = null;
57286         if(!suppressEvent){
57287              this.fireEvent("widthchange", this, col, width);
57288         }
57289     },
57290
57291     /**
57292      * Returns the total width of all columns.
57293      * @param {Boolean} includeHidden True to include hidden column widths
57294      * @return {Number}
57295      */
57296     getTotalWidth : function(includeHidden){
57297         if(!this.totalWidth){
57298             this.totalWidth = 0;
57299             for(var i = 0, len = this.config.length; i < len; i++){
57300                 if(includeHidden || !this.isHidden(i)){
57301                     this.totalWidth += this.getColumnWidth(i);
57302                 }
57303             }
57304         }
57305         return this.totalWidth;
57306     },
57307
57308     /**
57309      * Returns the header for the specified column.
57310      * @param {Number} col The column index
57311      * @return {String}
57312      */
57313     getColumnHeader : function(col){
57314         return this.config[col].header;
57315     },
57316
57317     /**
57318      * Sets the header for a column.
57319      * @param {Number} col The column index
57320      * @param {String} header The new header
57321      */
57322     setColumnHeader : function(col, header){
57323         this.config[col].header = header;
57324         this.fireEvent("headerchange", this, col, header);
57325     },
57326
57327     /**
57328      * Returns the tooltip for the specified column.
57329      * @param {Number} col The column index
57330      * @return {String}
57331      */
57332     getColumnTooltip : function(col){
57333             return this.config[col].tooltip;
57334     },
57335     /**
57336      * Sets the tooltip for a column.
57337      * @param {Number} col The column index
57338      * @param {String} tooltip The new tooltip
57339      */
57340     setColumnTooltip : function(col, tooltip){
57341             this.config[col].tooltip = tooltip;
57342     },
57343
57344     /**
57345      * Returns the dataIndex for the specified column.
57346      * @param {Number} col The column index
57347      * @return {Number}
57348      */
57349     getDataIndex : function(col){
57350         return this.config[col].dataIndex;
57351     },
57352
57353     /**
57354      * Sets the dataIndex for a column.
57355      * @param {Number} col The column index
57356      * @param {Number} dataIndex The new dataIndex
57357      */
57358     setDataIndex : function(col, dataIndex){
57359         this.config[col].dataIndex = dataIndex;
57360     },
57361
57362     
57363     
57364     /**
57365      * Returns true if the cell is editable.
57366      * @param {Number} colIndex The column index
57367      * @param {Number} rowIndex The row index - this is nto actually used..?
57368      * @return {Boolean}
57369      */
57370     isCellEditable : function(colIndex, rowIndex){
57371         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57372     },
57373
57374     /**
57375      * Returns the editor defined for the cell/column.
57376      * return false or null to disable editing.
57377      * @param {Number} colIndex The column index
57378      * @param {Number} rowIndex The row index
57379      * @return {Object}
57380      */
57381     getCellEditor : function(colIndex, rowIndex){
57382         return this.config[colIndex].editor;
57383     },
57384
57385     /**
57386      * Sets if a column is editable.
57387      * @param {Number} col The column index
57388      * @param {Boolean} editable True if the column is editable
57389      */
57390     setEditable : function(col, editable){
57391         this.config[col].editable = editable;
57392     },
57393
57394
57395     /**
57396      * Returns true if the column is hidden.
57397      * @param {Number} colIndex The column index
57398      * @return {Boolean}
57399      */
57400     isHidden : function(colIndex){
57401         return this.config[colIndex].hidden;
57402     },
57403
57404
57405     /**
57406      * Returns true if the column width cannot be changed
57407      */
57408     isFixed : function(colIndex){
57409         return this.config[colIndex].fixed;
57410     },
57411
57412     /**
57413      * Returns true if the column can be resized
57414      * @return {Boolean}
57415      */
57416     isResizable : function(colIndex){
57417         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57418     },
57419     /**
57420      * Sets if a column is hidden.
57421      * @param {Number} colIndex The column index
57422      * @param {Boolean} hidden True if the column is hidden
57423      */
57424     setHidden : function(colIndex, hidden){
57425         this.config[colIndex].hidden = hidden;
57426         this.totalWidth = null;
57427         this.fireEvent("hiddenchange", this, colIndex, hidden);
57428     },
57429
57430     /**
57431      * Sets the editor for a column.
57432      * @param {Number} col The column index
57433      * @param {Object} editor The editor object
57434      */
57435     setEditor : function(col, editor){
57436         this.config[col].editor = editor;
57437     }
57438 });
57439
57440 Roo.grid.ColumnModel.defaultRenderer = function(value)
57441 {
57442     if(typeof value == "object") {
57443         return value;
57444     }
57445         if(typeof value == "string" && value.length < 1){
57446             return "&#160;";
57447         }
57448     
57449         return String.format("{0}", value);
57450 };
57451
57452 // Alias for backwards compatibility
57453 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57454 /*
57455  * Based on:
57456  * Ext JS Library 1.1.1
57457  * Copyright(c) 2006-2007, Ext JS, LLC.
57458  *
57459  * Originally Released Under LGPL - original licence link has changed is not relivant.
57460  *
57461  * Fork - LGPL
57462  * <script type="text/javascript">
57463  */
57464
57465 /**
57466  * @class Roo.grid.AbstractSelectionModel
57467  * @extends Roo.util.Observable
57468  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57469  * implemented by descendant classes.  This class should not be directly instantiated.
57470  * @constructor
57471  */
57472 Roo.grid.AbstractSelectionModel = function(){
57473     this.locked = false;
57474     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57475 };
57476
57477 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57478     /** @ignore Called by the grid automatically. Do not call directly. */
57479     init : function(grid){
57480         this.grid = grid;
57481         this.initEvents();
57482     },
57483
57484     /**
57485      * Locks the selections.
57486      */
57487     lock : function(){
57488         this.locked = true;
57489     },
57490
57491     /**
57492      * Unlocks the selections.
57493      */
57494     unlock : function(){
57495         this.locked = false;
57496     },
57497
57498     /**
57499      * Returns true if the selections are locked.
57500      * @return {Boolean}
57501      */
57502     isLocked : function(){
57503         return this.locked;
57504     }
57505 });/*
57506  * Based on:
57507  * Ext JS Library 1.1.1
57508  * Copyright(c) 2006-2007, Ext JS, LLC.
57509  *
57510  * Originally Released Under LGPL - original licence link has changed is not relivant.
57511  *
57512  * Fork - LGPL
57513  * <script type="text/javascript">
57514  */
57515 /**
57516  * @extends Roo.grid.AbstractSelectionModel
57517  * @class Roo.grid.RowSelectionModel
57518  * The default SelectionModel used by {@link Roo.grid.Grid}.
57519  * It supports multiple selections and keyboard selection/navigation. 
57520  * @constructor
57521  * @param {Object} config
57522  */
57523 Roo.grid.RowSelectionModel = function(config){
57524     Roo.apply(this, config);
57525     this.selections = new Roo.util.MixedCollection(false, function(o){
57526         return o.id;
57527     });
57528
57529     this.last = false;
57530     this.lastActive = false;
57531
57532     this.addEvents({
57533         /**
57534              * @event selectionchange
57535              * Fires when the selection changes
57536              * @param {SelectionModel} this
57537              */
57538             "selectionchange" : true,
57539         /**
57540              * @event afterselectionchange
57541              * Fires after the selection changes (eg. by key press or clicking)
57542              * @param {SelectionModel} this
57543              */
57544             "afterselectionchange" : true,
57545         /**
57546              * @event beforerowselect
57547              * Fires when a row is selected being selected, return false to cancel.
57548              * @param {SelectionModel} this
57549              * @param {Number} rowIndex The selected index
57550              * @param {Boolean} keepExisting False if other selections will be cleared
57551              */
57552             "beforerowselect" : true,
57553         /**
57554              * @event rowselect
57555              * Fires when a row is selected.
57556              * @param {SelectionModel} this
57557              * @param {Number} rowIndex The selected index
57558              * @param {Roo.data.Record} r The record
57559              */
57560             "rowselect" : true,
57561         /**
57562              * @event rowdeselect
57563              * Fires when a row is deselected.
57564              * @param {SelectionModel} this
57565              * @param {Number} rowIndex The selected index
57566              */
57567         "rowdeselect" : true
57568     });
57569     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57570     this.locked = false;
57571 };
57572
57573 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57574     /**
57575      * @cfg {Boolean} singleSelect
57576      * True to allow selection of only one row at a time (defaults to false)
57577      */
57578     singleSelect : false,
57579
57580     // private
57581     initEvents : function(){
57582
57583         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57584             this.grid.on("mousedown", this.handleMouseDown, this);
57585         }else{ // allow click to work like normal
57586             this.grid.on("rowclick", this.handleDragableRowClick, this);
57587         }
57588
57589         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57590             "up" : function(e){
57591                 if(!e.shiftKey){
57592                     this.selectPrevious(e.shiftKey);
57593                 }else if(this.last !== false && this.lastActive !== false){
57594                     var last = this.last;
57595                     this.selectRange(this.last,  this.lastActive-1);
57596                     this.grid.getView().focusRow(this.lastActive);
57597                     if(last !== false){
57598                         this.last = last;
57599                     }
57600                 }else{
57601                     this.selectFirstRow();
57602                 }
57603                 this.fireEvent("afterselectionchange", this);
57604             },
57605             "down" : function(e){
57606                 if(!e.shiftKey){
57607                     this.selectNext(e.shiftKey);
57608                 }else if(this.last !== false && this.lastActive !== false){
57609                     var last = this.last;
57610                     this.selectRange(this.last,  this.lastActive+1);
57611                     this.grid.getView().focusRow(this.lastActive);
57612                     if(last !== false){
57613                         this.last = last;
57614                     }
57615                 }else{
57616                     this.selectFirstRow();
57617                 }
57618                 this.fireEvent("afterselectionchange", this);
57619             },
57620             scope: this
57621         });
57622
57623         var view = this.grid.view;
57624         view.on("refresh", this.onRefresh, this);
57625         view.on("rowupdated", this.onRowUpdated, this);
57626         view.on("rowremoved", this.onRemove, this);
57627     },
57628
57629     // private
57630     onRefresh : function(){
57631         var ds = this.grid.dataSource, i, v = this.grid.view;
57632         var s = this.selections;
57633         s.each(function(r){
57634             if((i = ds.indexOfId(r.id)) != -1){
57635                 v.onRowSelect(i);
57636                 s.add(ds.getAt(i)); // updating the selection relate data
57637             }else{
57638                 s.remove(r);
57639             }
57640         });
57641     },
57642
57643     // private
57644     onRemove : function(v, index, r){
57645         this.selections.remove(r);
57646     },
57647
57648     // private
57649     onRowUpdated : function(v, index, r){
57650         if(this.isSelected(r)){
57651             v.onRowSelect(index);
57652         }
57653     },
57654
57655     /**
57656      * Select records.
57657      * @param {Array} records The records to select
57658      * @param {Boolean} keepExisting (optional) True to keep existing selections
57659      */
57660     selectRecords : function(records, keepExisting){
57661         if(!keepExisting){
57662             this.clearSelections();
57663         }
57664         var ds = this.grid.dataSource;
57665         for(var i = 0, len = records.length; i < len; i++){
57666             this.selectRow(ds.indexOf(records[i]), true);
57667         }
57668     },
57669
57670     /**
57671      * Gets the number of selected rows.
57672      * @return {Number}
57673      */
57674     getCount : function(){
57675         return this.selections.length;
57676     },
57677
57678     /**
57679      * Selects the first row in the grid.
57680      */
57681     selectFirstRow : function(){
57682         this.selectRow(0);
57683     },
57684
57685     /**
57686      * Select the last row.
57687      * @param {Boolean} keepExisting (optional) True to keep existing selections
57688      */
57689     selectLastRow : function(keepExisting){
57690         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57691     },
57692
57693     /**
57694      * Selects the row immediately following the last selected row.
57695      * @param {Boolean} keepExisting (optional) True to keep existing selections
57696      */
57697     selectNext : function(keepExisting){
57698         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57699             this.selectRow(this.last+1, keepExisting);
57700             this.grid.getView().focusRow(this.last);
57701         }
57702     },
57703
57704     /**
57705      * Selects the row that precedes the last selected row.
57706      * @param {Boolean} keepExisting (optional) True to keep existing selections
57707      */
57708     selectPrevious : function(keepExisting){
57709         if(this.last){
57710             this.selectRow(this.last-1, keepExisting);
57711             this.grid.getView().focusRow(this.last);
57712         }
57713     },
57714
57715     /**
57716      * Returns the selected records
57717      * @return {Array} Array of selected records
57718      */
57719     getSelections : function(){
57720         return [].concat(this.selections.items);
57721     },
57722
57723     /**
57724      * Returns the first selected record.
57725      * @return {Record}
57726      */
57727     getSelected : function(){
57728         return this.selections.itemAt(0);
57729     },
57730
57731
57732     /**
57733      * Clears all selections.
57734      */
57735     clearSelections : function(fast){
57736         if(this.locked) {
57737             return;
57738         }
57739         if(fast !== true){
57740             var ds = this.grid.dataSource;
57741             var s = this.selections;
57742             s.each(function(r){
57743                 this.deselectRow(ds.indexOfId(r.id));
57744             }, this);
57745             s.clear();
57746         }else{
57747             this.selections.clear();
57748         }
57749         this.last = false;
57750     },
57751
57752
57753     /**
57754      * Selects all rows.
57755      */
57756     selectAll : function(){
57757         if(this.locked) {
57758             return;
57759         }
57760         this.selections.clear();
57761         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57762             this.selectRow(i, true);
57763         }
57764     },
57765
57766     /**
57767      * Returns True if there is a selection.
57768      * @return {Boolean}
57769      */
57770     hasSelection : function(){
57771         return this.selections.length > 0;
57772     },
57773
57774     /**
57775      * Returns True if the specified row is selected.
57776      * @param {Number/Record} record The record or index of the record to check
57777      * @return {Boolean}
57778      */
57779     isSelected : function(index){
57780         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57781         return (r && this.selections.key(r.id) ? true : false);
57782     },
57783
57784     /**
57785      * Returns True if the specified record id is selected.
57786      * @param {String} id The id of record to check
57787      * @return {Boolean}
57788      */
57789     isIdSelected : function(id){
57790         return (this.selections.key(id) ? true : false);
57791     },
57792
57793     // private
57794     handleMouseDown : function(e, t){
57795         var view = this.grid.getView(), rowIndex;
57796         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57797             return;
57798         };
57799         if(e.shiftKey && this.last !== false){
57800             var last = this.last;
57801             this.selectRange(last, rowIndex, e.ctrlKey);
57802             this.last = last; // reset the last
57803             view.focusRow(rowIndex);
57804         }else{
57805             var isSelected = this.isSelected(rowIndex);
57806             if(e.button !== 0 && isSelected){
57807                 view.focusRow(rowIndex);
57808             }else if(e.ctrlKey && isSelected){
57809                 this.deselectRow(rowIndex);
57810             }else if(!isSelected){
57811                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57812                 view.focusRow(rowIndex);
57813             }
57814         }
57815         this.fireEvent("afterselectionchange", this);
57816     },
57817     // private
57818     handleDragableRowClick :  function(grid, rowIndex, e) 
57819     {
57820         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57821             this.selectRow(rowIndex, false);
57822             grid.view.focusRow(rowIndex);
57823              this.fireEvent("afterselectionchange", this);
57824         }
57825     },
57826     
57827     /**
57828      * Selects multiple rows.
57829      * @param {Array} rows Array of the indexes of the row to select
57830      * @param {Boolean} keepExisting (optional) True to keep existing selections
57831      */
57832     selectRows : function(rows, keepExisting){
57833         if(!keepExisting){
57834             this.clearSelections();
57835         }
57836         for(var i = 0, len = rows.length; i < len; i++){
57837             this.selectRow(rows[i], true);
57838         }
57839     },
57840
57841     /**
57842      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57843      * @param {Number} startRow The index of the first row in the range
57844      * @param {Number} endRow The index of the last row in the range
57845      * @param {Boolean} keepExisting (optional) True to retain existing selections
57846      */
57847     selectRange : function(startRow, endRow, keepExisting){
57848         if(this.locked) {
57849             return;
57850         }
57851         if(!keepExisting){
57852             this.clearSelections();
57853         }
57854         if(startRow <= endRow){
57855             for(var i = startRow; i <= endRow; i++){
57856                 this.selectRow(i, true);
57857             }
57858         }else{
57859             for(var i = startRow; i >= endRow; i--){
57860                 this.selectRow(i, true);
57861             }
57862         }
57863     },
57864
57865     /**
57866      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57867      * @param {Number} startRow The index of the first row in the range
57868      * @param {Number} endRow The index of the last row in the range
57869      */
57870     deselectRange : function(startRow, endRow, preventViewNotify){
57871         if(this.locked) {
57872             return;
57873         }
57874         for(var i = startRow; i <= endRow; i++){
57875             this.deselectRow(i, preventViewNotify);
57876         }
57877     },
57878
57879     /**
57880      * Selects a row.
57881      * @param {Number} row The index of the row to select
57882      * @param {Boolean} keepExisting (optional) True to keep existing selections
57883      */
57884     selectRow : function(index, keepExisting, preventViewNotify){
57885         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57886             return;
57887         }
57888         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57889             if(!keepExisting || this.singleSelect){
57890                 this.clearSelections();
57891             }
57892             var r = this.grid.dataSource.getAt(index);
57893             this.selections.add(r);
57894             this.last = this.lastActive = index;
57895             if(!preventViewNotify){
57896                 this.grid.getView().onRowSelect(index);
57897             }
57898             this.fireEvent("rowselect", this, index, r);
57899             this.fireEvent("selectionchange", this);
57900         }
57901     },
57902
57903     /**
57904      * Deselects a row.
57905      * @param {Number} row The index of the row to deselect
57906      */
57907     deselectRow : function(index, preventViewNotify){
57908         if(this.locked) {
57909             return;
57910         }
57911         if(this.last == index){
57912             this.last = false;
57913         }
57914         if(this.lastActive == index){
57915             this.lastActive = false;
57916         }
57917         var r = this.grid.dataSource.getAt(index);
57918         this.selections.remove(r);
57919         if(!preventViewNotify){
57920             this.grid.getView().onRowDeselect(index);
57921         }
57922         this.fireEvent("rowdeselect", this, index);
57923         this.fireEvent("selectionchange", this);
57924     },
57925
57926     // private
57927     restoreLast : function(){
57928         if(this._last){
57929             this.last = this._last;
57930         }
57931     },
57932
57933     // private
57934     acceptsNav : function(row, col, cm){
57935         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57936     },
57937
57938     // private
57939     onEditorKey : function(field, e){
57940         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57941         if(k == e.TAB){
57942             e.stopEvent();
57943             ed.completeEdit();
57944             if(e.shiftKey){
57945                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57946             }else{
57947                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57948             }
57949         }else if(k == e.ENTER && !e.ctrlKey){
57950             e.stopEvent();
57951             ed.completeEdit();
57952             if(e.shiftKey){
57953                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57954             }else{
57955                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57956             }
57957         }else if(k == e.ESC){
57958             ed.cancelEdit();
57959         }
57960         if(newCell){
57961             g.startEditing(newCell[0], newCell[1]);
57962         }
57963     }
57964 });/*
57965  * Based on:
57966  * Ext JS Library 1.1.1
57967  * Copyright(c) 2006-2007, Ext JS, LLC.
57968  *
57969  * Originally Released Under LGPL - original licence link has changed is not relivant.
57970  *
57971  * Fork - LGPL
57972  * <script type="text/javascript">
57973  */
57974 /**
57975  * @class Roo.grid.CellSelectionModel
57976  * @extends Roo.grid.AbstractSelectionModel
57977  * This class provides the basic implementation for cell selection in a grid.
57978  * @constructor
57979  * @param {Object} config The object containing the configuration of this model.
57980  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57981  */
57982 Roo.grid.CellSelectionModel = function(config){
57983     Roo.apply(this, config);
57984
57985     this.selection = null;
57986
57987     this.addEvents({
57988         /**
57989              * @event beforerowselect
57990              * Fires before a cell is selected.
57991              * @param {SelectionModel} this
57992              * @param {Number} rowIndex The selected row index
57993              * @param {Number} colIndex The selected cell index
57994              */
57995             "beforecellselect" : true,
57996         /**
57997              * @event cellselect
57998              * Fires when a cell is selected.
57999              * @param {SelectionModel} this
58000              * @param {Number} rowIndex The selected row index
58001              * @param {Number} colIndex The selected cell index
58002              */
58003             "cellselect" : true,
58004         /**
58005              * @event selectionchange
58006              * Fires when the active selection changes.
58007              * @param {SelectionModel} this
58008              * @param {Object} selection null for no selection or an object (o) with two properties
58009                 <ul>
58010                 <li>o.record: the record object for the row the selection is in</li>
58011                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58012                 </ul>
58013              */
58014             "selectionchange" : true,
58015         /**
58016              * @event tabend
58017              * Fires when the tab (or enter) was pressed on the last editable cell
58018              * You can use this to trigger add new row.
58019              * @param {SelectionModel} this
58020              */
58021             "tabend" : true,
58022          /**
58023              * @event beforeeditnext
58024              * Fires before the next editable sell is made active
58025              * You can use this to skip to another cell or fire the tabend
58026              *    if you set cell to false
58027              * @param {Object} eventdata object : { cell : [ row, col ] } 
58028              */
58029             "beforeeditnext" : true
58030     });
58031     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58032 };
58033
58034 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58035     
58036     enter_is_tab: false,
58037
58038     /** @ignore */
58039     initEvents : function(){
58040         this.grid.on("mousedown", this.handleMouseDown, this);
58041         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58042         var view = this.grid.view;
58043         view.on("refresh", this.onViewChange, this);
58044         view.on("rowupdated", this.onRowUpdated, this);
58045         view.on("beforerowremoved", this.clearSelections, this);
58046         view.on("beforerowsinserted", this.clearSelections, this);
58047         if(this.grid.isEditor){
58048             this.grid.on("beforeedit", this.beforeEdit,  this);
58049         }
58050     },
58051
58052         //private
58053     beforeEdit : function(e){
58054         this.select(e.row, e.column, false, true, e.record);
58055     },
58056
58057         //private
58058     onRowUpdated : function(v, index, r){
58059         if(this.selection && this.selection.record == r){
58060             v.onCellSelect(index, this.selection.cell[1]);
58061         }
58062     },
58063
58064         //private
58065     onViewChange : function(){
58066         this.clearSelections(true);
58067     },
58068
58069         /**
58070          * Returns the currently selected cell,.
58071          * @return {Array} The selected cell (row, column) or null if none selected.
58072          */
58073     getSelectedCell : function(){
58074         return this.selection ? this.selection.cell : null;
58075     },
58076
58077     /**
58078      * Clears all selections.
58079      * @param {Boolean} true to prevent the gridview from being notified about the change.
58080      */
58081     clearSelections : function(preventNotify){
58082         var s = this.selection;
58083         if(s){
58084             if(preventNotify !== true){
58085                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58086             }
58087             this.selection = null;
58088             this.fireEvent("selectionchange", this, null);
58089         }
58090     },
58091
58092     /**
58093      * Returns true if there is a selection.
58094      * @return {Boolean}
58095      */
58096     hasSelection : function(){
58097         return this.selection ? true : false;
58098     },
58099
58100     /** @ignore */
58101     handleMouseDown : function(e, t){
58102         var v = this.grid.getView();
58103         if(this.isLocked()){
58104             return;
58105         };
58106         var row = v.findRowIndex(t);
58107         var cell = v.findCellIndex(t);
58108         if(row !== false && cell !== false){
58109             this.select(row, cell);
58110         }
58111     },
58112
58113     /**
58114      * Selects a cell.
58115      * @param {Number} rowIndex
58116      * @param {Number} collIndex
58117      */
58118     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58119         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58120             this.clearSelections();
58121             r = r || this.grid.dataSource.getAt(rowIndex);
58122             this.selection = {
58123                 record : r,
58124                 cell : [rowIndex, colIndex]
58125             };
58126             if(!preventViewNotify){
58127                 var v = this.grid.getView();
58128                 v.onCellSelect(rowIndex, colIndex);
58129                 if(preventFocus !== true){
58130                     v.focusCell(rowIndex, colIndex);
58131                 }
58132             }
58133             this.fireEvent("cellselect", this, rowIndex, colIndex);
58134             this.fireEvent("selectionchange", this, this.selection);
58135         }
58136     },
58137
58138         //private
58139     isSelectable : function(rowIndex, colIndex, cm){
58140         return !cm.isHidden(colIndex);
58141     },
58142
58143     /** @ignore */
58144     handleKeyDown : function(e){
58145         //Roo.log('Cell Sel Model handleKeyDown');
58146         if(!e.isNavKeyPress()){
58147             return;
58148         }
58149         var g = this.grid, s = this.selection;
58150         if(!s){
58151             e.stopEvent();
58152             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58153             if(cell){
58154                 this.select(cell[0], cell[1]);
58155             }
58156             return;
58157         }
58158         var sm = this;
58159         var walk = function(row, col, step){
58160             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58161         };
58162         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58163         var newCell;
58164
58165       
58166
58167         switch(k){
58168             case e.TAB:
58169                 // handled by onEditorKey
58170                 if (g.isEditor && g.editing) {
58171                     return;
58172                 }
58173                 if(e.shiftKey) {
58174                     newCell = walk(r, c-1, -1);
58175                 } else {
58176                     newCell = walk(r, c+1, 1);
58177                 }
58178                 break;
58179             
58180             case e.DOWN:
58181                newCell = walk(r+1, c, 1);
58182                 break;
58183             
58184             case e.UP:
58185                 newCell = walk(r-1, c, -1);
58186                 break;
58187             
58188             case e.RIGHT:
58189                 newCell = walk(r, c+1, 1);
58190                 break;
58191             
58192             case e.LEFT:
58193                 newCell = walk(r, c-1, -1);
58194                 break;
58195             
58196             case e.ENTER:
58197                 
58198                 if(g.isEditor && !g.editing){
58199                    g.startEditing(r, c);
58200                    e.stopEvent();
58201                    return;
58202                 }
58203                 
58204                 
58205              break;
58206         };
58207         if(newCell){
58208             this.select(newCell[0], newCell[1]);
58209             e.stopEvent();
58210             
58211         }
58212     },
58213
58214     acceptsNav : function(row, col, cm){
58215         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58216     },
58217     /**
58218      * Selects a cell.
58219      * @param {Number} field (not used) - as it's normally used as a listener
58220      * @param {Number} e - event - fake it by using
58221      *
58222      * var e = Roo.EventObjectImpl.prototype;
58223      * e.keyCode = e.TAB
58224      *
58225      * 
58226      */
58227     onEditorKey : function(field, e){
58228         
58229         var k = e.getKey(),
58230             newCell,
58231             g = this.grid,
58232             ed = g.activeEditor,
58233             forward = false;
58234         ///Roo.log('onEditorKey' + k);
58235         
58236         
58237         if (this.enter_is_tab && k == e.ENTER) {
58238             k = e.TAB;
58239         }
58240         
58241         if(k == e.TAB){
58242             if(e.shiftKey){
58243                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58244             }else{
58245                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58246                 forward = true;
58247             }
58248             
58249             e.stopEvent();
58250             
58251         } else if(k == e.ENTER &&  !e.ctrlKey){
58252             ed.completeEdit();
58253             e.stopEvent();
58254             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58255         
58256                 } else if(k == e.ESC){
58257             ed.cancelEdit();
58258         }
58259                 
58260         if (newCell) {
58261             var ecall = { cell : newCell, forward : forward };
58262             this.fireEvent('beforeeditnext', ecall );
58263             newCell = ecall.cell;
58264                         forward = ecall.forward;
58265         }
58266                 
58267         if(newCell){
58268             //Roo.log('next cell after edit');
58269             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58270         } else if (forward) {
58271             // tabbed past last
58272             this.fireEvent.defer(100, this, ['tabend',this]);
58273         }
58274     }
58275 });/*
58276  * Based on:
58277  * Ext JS Library 1.1.1
58278  * Copyright(c) 2006-2007, Ext JS, LLC.
58279  *
58280  * Originally Released Under LGPL - original licence link has changed is not relivant.
58281  *
58282  * Fork - LGPL
58283  * <script type="text/javascript">
58284  */
58285  
58286 /**
58287  * @class Roo.grid.EditorGrid
58288  * @extends Roo.grid.Grid
58289  * Class for creating and editable grid.
58290  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58291  * The container MUST have some type of size defined for the grid to fill. The container will be 
58292  * automatically set to position relative if it isn't already.
58293  * @param {Object} dataSource The data model to bind to
58294  * @param {Object} colModel The column model with info about this grid's columns
58295  */
58296 Roo.grid.EditorGrid = function(container, config){
58297     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58298     this.getGridEl().addClass("xedit-grid");
58299
58300     if(!this.selModel){
58301         this.selModel = new Roo.grid.CellSelectionModel();
58302     }
58303
58304     this.activeEditor = null;
58305
58306         this.addEvents({
58307             /**
58308              * @event beforeedit
58309              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58310              * <ul style="padding:5px;padding-left:16px;">
58311              * <li>grid - This grid</li>
58312              * <li>record - The record being edited</li>
58313              * <li>field - The field name being edited</li>
58314              * <li>value - The value for the field being edited.</li>
58315              * <li>row - The grid row index</li>
58316              * <li>column - The grid column index</li>
58317              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58318              * </ul>
58319              * @param {Object} e An edit event (see above for description)
58320              */
58321             "beforeedit" : true,
58322             /**
58323              * @event afteredit
58324              * Fires after a cell is edited. <br />
58325              * <ul style="padding:5px;padding-left:16px;">
58326              * <li>grid - This grid</li>
58327              * <li>record - The record being edited</li>
58328              * <li>field - The field name being edited</li>
58329              * <li>value - The value being set</li>
58330              * <li>originalValue - The original value for the field, before the edit.</li>
58331              * <li>row - The grid row index</li>
58332              * <li>column - The grid column index</li>
58333              * </ul>
58334              * @param {Object} e An edit event (see above for description)
58335              */
58336             "afteredit" : true,
58337             /**
58338              * @event validateedit
58339              * Fires after a cell is edited, but before the value is set in the record. 
58340          * You can use this to modify the value being set in the field, Return false
58341              * to cancel the change. The edit event object has the following properties <br />
58342              * <ul style="padding:5px;padding-left:16px;">
58343          * <li>editor - This editor</li>
58344              * <li>grid - This grid</li>
58345              * <li>record - The record being edited</li>
58346              * <li>field - The field name being edited</li>
58347              * <li>value - The value being set</li>
58348              * <li>originalValue - The original value for the field, before the edit.</li>
58349              * <li>row - The grid row index</li>
58350              * <li>column - The grid column index</li>
58351              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58352              * </ul>
58353              * @param {Object} e An edit event (see above for description)
58354              */
58355             "validateedit" : true
58356         });
58357     this.on("bodyscroll", this.stopEditing,  this);
58358     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58359 };
58360
58361 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58362     /**
58363      * @cfg {Number} clicksToEdit
58364      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58365      */
58366     clicksToEdit: 2,
58367
58368     // private
58369     isEditor : true,
58370     // private
58371     trackMouseOver: false, // causes very odd FF errors
58372
58373     onCellDblClick : function(g, row, col){
58374         this.startEditing(row, col);
58375     },
58376
58377     onEditComplete : function(ed, value, startValue){
58378         this.editing = false;
58379         this.activeEditor = null;
58380         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58381         var r = ed.record;
58382         var field = this.colModel.getDataIndex(ed.col);
58383         var e = {
58384             grid: this,
58385             record: r,
58386             field: field,
58387             originalValue: startValue,
58388             value: value,
58389             row: ed.row,
58390             column: ed.col,
58391             cancel:false,
58392             editor: ed
58393         };
58394         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58395         cell.show();
58396           
58397         if(String(value) !== String(startValue)){
58398             
58399             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58400                 r.set(field, e.value);
58401                 // if we are dealing with a combo box..
58402                 // then we also set the 'name' colum to be the displayField
58403                 if (ed.field.displayField && ed.field.name) {
58404                     r.set(ed.field.name, ed.field.el.dom.value);
58405                 }
58406                 
58407                 delete e.cancel; //?? why!!!
58408                 this.fireEvent("afteredit", e);
58409             }
58410         } else {
58411             this.fireEvent("afteredit", e); // always fire it!
58412         }
58413         this.view.focusCell(ed.row, ed.col);
58414     },
58415
58416     /**
58417      * Starts editing the specified for the specified row/column
58418      * @param {Number} rowIndex
58419      * @param {Number} colIndex
58420      */
58421     startEditing : function(row, col){
58422         this.stopEditing();
58423         if(this.colModel.isCellEditable(col, row)){
58424             this.view.ensureVisible(row, col, true);
58425           
58426             var r = this.dataSource.getAt(row);
58427             var field = this.colModel.getDataIndex(col);
58428             var cell = Roo.get(this.view.getCell(row,col));
58429             var e = {
58430                 grid: this,
58431                 record: r,
58432                 field: field,
58433                 value: r.data[field],
58434                 row: row,
58435                 column: col,
58436                 cancel:false 
58437             };
58438             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58439                 this.editing = true;
58440                 var ed = this.colModel.getCellEditor(col, row);
58441                 
58442                 if (!ed) {
58443                     return;
58444                 }
58445                 if(!ed.rendered){
58446                     ed.render(ed.parentEl || document.body);
58447                 }
58448                 ed.field.reset();
58449                
58450                 cell.hide();
58451                 
58452                 (function(){ // complex but required for focus issues in safari, ie and opera
58453                     ed.row = row;
58454                     ed.col = col;
58455                     ed.record = r;
58456                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58457                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58458                     this.activeEditor = ed;
58459                     var v = r.data[field];
58460                     ed.startEdit(this.view.getCell(row, col), v);
58461                     // combo's with 'displayField and name set
58462                     if (ed.field.displayField && ed.field.name) {
58463                         ed.field.el.dom.value = r.data[ed.field.name];
58464                     }
58465                     
58466                     
58467                 }).defer(50, this);
58468             }
58469         }
58470     },
58471         
58472     /**
58473      * Stops any active editing
58474      */
58475     stopEditing : function(){
58476         if(this.activeEditor){
58477             this.activeEditor.completeEdit();
58478         }
58479         this.activeEditor = null;
58480     },
58481         
58482          /**
58483      * Called to get grid's drag proxy text, by default returns this.ddText.
58484      * @return {String}
58485      */
58486     getDragDropText : function(){
58487         var count = this.selModel.getSelectedCell() ? 1 : 0;
58488         return String.format(this.ddText, count, count == 1 ? '' : 's');
58489     }
58490         
58491 });/*
58492  * Based on:
58493  * Ext JS Library 1.1.1
58494  * Copyright(c) 2006-2007, Ext JS, LLC.
58495  *
58496  * Originally Released Under LGPL - original licence link has changed is not relivant.
58497  *
58498  * Fork - LGPL
58499  * <script type="text/javascript">
58500  */
58501
58502 // private - not really -- you end up using it !
58503 // This is a support class used internally by the Grid components
58504
58505 /**
58506  * @class Roo.grid.GridEditor
58507  * @extends Roo.Editor
58508  * Class for creating and editable grid elements.
58509  * @param {Object} config any settings (must include field)
58510  */
58511 Roo.grid.GridEditor = function(field, config){
58512     if (!config && field.field) {
58513         config = field;
58514         field = Roo.factory(config.field, Roo.form);
58515     }
58516     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58517     field.monitorTab = false;
58518 };
58519
58520 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58521     
58522     /**
58523      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58524      */
58525     
58526     alignment: "tl-tl",
58527     autoSize: "width",
58528     hideEl : false,
58529     cls: "x-small-editor x-grid-editor",
58530     shim:false,
58531     shadow:"frame"
58532 });/*
58533  * Based on:
58534  * Ext JS Library 1.1.1
58535  * Copyright(c) 2006-2007, Ext JS, LLC.
58536  *
58537  * Originally Released Under LGPL - original licence link has changed is not relivant.
58538  *
58539  * Fork - LGPL
58540  * <script type="text/javascript">
58541  */
58542   
58543
58544   
58545 Roo.grid.PropertyRecord = Roo.data.Record.create([
58546     {name:'name',type:'string'},  'value'
58547 ]);
58548
58549
58550 Roo.grid.PropertyStore = function(grid, source){
58551     this.grid = grid;
58552     this.store = new Roo.data.Store({
58553         recordType : Roo.grid.PropertyRecord
58554     });
58555     this.store.on('update', this.onUpdate,  this);
58556     if(source){
58557         this.setSource(source);
58558     }
58559     Roo.grid.PropertyStore.superclass.constructor.call(this);
58560 };
58561
58562
58563
58564 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58565     setSource : function(o){
58566         this.source = o;
58567         this.store.removeAll();
58568         var data = [];
58569         for(var k in o){
58570             if(this.isEditableValue(o[k])){
58571                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58572             }
58573         }
58574         this.store.loadRecords({records: data}, {}, true);
58575     },
58576
58577     onUpdate : function(ds, record, type){
58578         if(type == Roo.data.Record.EDIT){
58579             var v = record.data['value'];
58580             var oldValue = record.modified['value'];
58581             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58582                 this.source[record.id] = v;
58583                 record.commit();
58584                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58585             }else{
58586                 record.reject();
58587             }
58588         }
58589     },
58590
58591     getProperty : function(row){
58592        return this.store.getAt(row);
58593     },
58594
58595     isEditableValue: function(val){
58596         if(val && val instanceof Date){
58597             return true;
58598         }else if(typeof val == 'object' || typeof val == 'function'){
58599             return false;
58600         }
58601         return true;
58602     },
58603
58604     setValue : function(prop, value){
58605         this.source[prop] = value;
58606         this.store.getById(prop).set('value', value);
58607     },
58608
58609     getSource : function(){
58610         return this.source;
58611     }
58612 });
58613
58614 Roo.grid.PropertyColumnModel = function(grid, store){
58615     this.grid = grid;
58616     var g = Roo.grid;
58617     g.PropertyColumnModel.superclass.constructor.call(this, [
58618         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58619         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58620     ]);
58621     this.store = store;
58622     this.bselect = Roo.DomHelper.append(document.body, {
58623         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58624             {tag: 'option', value: 'true', html: 'true'},
58625             {tag: 'option', value: 'false', html: 'false'}
58626         ]
58627     });
58628     Roo.id(this.bselect);
58629     var f = Roo.form;
58630     this.editors = {
58631         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58632         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58633         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58634         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58635         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58636     };
58637     this.renderCellDelegate = this.renderCell.createDelegate(this);
58638     this.renderPropDelegate = this.renderProp.createDelegate(this);
58639 };
58640
58641 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58642     
58643     
58644     nameText : 'Name',
58645     valueText : 'Value',
58646     
58647     dateFormat : 'm/j/Y',
58648     
58649     
58650     renderDate : function(dateVal){
58651         return dateVal.dateFormat(this.dateFormat);
58652     },
58653
58654     renderBool : function(bVal){
58655         return bVal ? 'true' : 'false';
58656     },
58657
58658     isCellEditable : function(colIndex, rowIndex){
58659         return colIndex == 1;
58660     },
58661
58662     getRenderer : function(col){
58663         return col == 1 ?
58664             this.renderCellDelegate : this.renderPropDelegate;
58665     },
58666
58667     renderProp : function(v){
58668         return this.getPropertyName(v);
58669     },
58670
58671     renderCell : function(val){
58672         var rv = val;
58673         if(val instanceof Date){
58674             rv = this.renderDate(val);
58675         }else if(typeof val == 'boolean'){
58676             rv = this.renderBool(val);
58677         }
58678         return Roo.util.Format.htmlEncode(rv);
58679     },
58680
58681     getPropertyName : function(name){
58682         var pn = this.grid.propertyNames;
58683         return pn && pn[name] ? pn[name] : name;
58684     },
58685
58686     getCellEditor : function(colIndex, rowIndex){
58687         var p = this.store.getProperty(rowIndex);
58688         var n = p.data['name'], val = p.data['value'];
58689         
58690         if(typeof(this.grid.customEditors[n]) == 'string'){
58691             return this.editors[this.grid.customEditors[n]];
58692         }
58693         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58694             return this.grid.customEditors[n];
58695         }
58696         if(val instanceof Date){
58697             return this.editors['date'];
58698         }else if(typeof val == 'number'){
58699             return this.editors['number'];
58700         }else if(typeof val == 'boolean'){
58701             return this.editors['boolean'];
58702         }else{
58703             return this.editors['string'];
58704         }
58705     }
58706 });
58707
58708 /**
58709  * @class Roo.grid.PropertyGrid
58710  * @extends Roo.grid.EditorGrid
58711  * This class represents the  interface of a component based property grid control.
58712  * <br><br>Usage:<pre><code>
58713  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58714       
58715  });
58716  // set any options
58717  grid.render();
58718  * </code></pre>
58719   
58720  * @constructor
58721  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58722  * The container MUST have some type of size defined for the grid to fill. The container will be
58723  * automatically set to position relative if it isn't already.
58724  * @param {Object} config A config object that sets properties on this grid.
58725  */
58726 Roo.grid.PropertyGrid = function(container, config){
58727     config = config || {};
58728     var store = new Roo.grid.PropertyStore(this);
58729     this.store = store;
58730     var cm = new Roo.grid.PropertyColumnModel(this, store);
58731     store.store.sort('name', 'ASC');
58732     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58733         ds: store.store,
58734         cm: cm,
58735         enableColLock:false,
58736         enableColumnMove:false,
58737         stripeRows:false,
58738         trackMouseOver: false,
58739         clicksToEdit:1
58740     }, config));
58741     this.getGridEl().addClass('x-props-grid');
58742     this.lastEditRow = null;
58743     this.on('columnresize', this.onColumnResize, this);
58744     this.addEvents({
58745          /**
58746              * @event beforepropertychange
58747              * Fires before a property changes (return false to stop?)
58748              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58749              * @param {String} id Record Id
58750              * @param {String} newval New Value
58751          * @param {String} oldval Old Value
58752              */
58753         "beforepropertychange": true,
58754         /**
58755              * @event propertychange
58756              * Fires after a property changes
58757              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58758              * @param {String} id Record Id
58759              * @param {String} newval New Value
58760          * @param {String} oldval Old Value
58761              */
58762         "propertychange": true
58763     });
58764     this.customEditors = this.customEditors || {};
58765 };
58766 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58767     
58768      /**
58769      * @cfg {Object} customEditors map of colnames=> custom editors.
58770      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58771      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58772      * false disables editing of the field.
58773          */
58774     
58775       /**
58776      * @cfg {Object} propertyNames map of property Names to their displayed value
58777          */
58778     
58779     render : function(){
58780         Roo.grid.PropertyGrid.superclass.render.call(this);
58781         this.autoSize.defer(100, this);
58782     },
58783
58784     autoSize : function(){
58785         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58786         if(this.view){
58787             this.view.fitColumns();
58788         }
58789     },
58790
58791     onColumnResize : function(){
58792         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58793         this.autoSize();
58794     },
58795     /**
58796      * Sets the data for the Grid
58797      * accepts a Key => Value object of all the elements avaiable.
58798      * @param {Object} data  to appear in grid.
58799      */
58800     setSource : function(source){
58801         this.store.setSource(source);
58802         //this.autoSize();
58803     },
58804     /**
58805      * Gets all the data from the grid.
58806      * @return {Object} data  data stored in grid
58807      */
58808     getSource : function(){
58809         return this.store.getSource();
58810     }
58811 });/*
58812   
58813  * Licence LGPL
58814  
58815  */
58816  
58817 /**
58818  * @class Roo.grid.Calendar
58819  * @extends Roo.util.Grid
58820  * This class extends the Grid to provide a calendar widget
58821  * <br><br>Usage:<pre><code>
58822  var grid = new Roo.grid.Calendar("my-container-id", {
58823      ds: myDataStore,
58824      cm: myColModel,
58825      selModel: mySelectionModel,
58826      autoSizeColumns: true,
58827      monitorWindowResize: false,
58828      trackMouseOver: true
58829      eventstore : real data store..
58830  });
58831  // set any options
58832  grid.render();
58833   
58834   * @constructor
58835  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58836  * The container MUST have some type of size defined for the grid to fill. The container will be
58837  * automatically set to position relative if it isn't already.
58838  * @param {Object} config A config object that sets properties on this grid.
58839  */
58840 Roo.grid.Calendar = function(container, config){
58841         // initialize the container
58842         this.container = Roo.get(container);
58843         this.container.update("");
58844         this.container.setStyle("overflow", "hidden");
58845     this.container.addClass('x-grid-container');
58846
58847     this.id = this.container.id;
58848
58849     Roo.apply(this, config);
58850     // check and correct shorthanded configs
58851     
58852     var rows = [];
58853     var d =1;
58854     for (var r = 0;r < 6;r++) {
58855         
58856         rows[r]=[];
58857         for (var c =0;c < 7;c++) {
58858             rows[r][c]= '';
58859         }
58860     }
58861     if (this.eventStore) {
58862         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58863         this.eventStore.on('load',this.onLoad, this);
58864         this.eventStore.on('beforeload',this.clearEvents, this);
58865          
58866     }
58867     
58868     this.dataSource = new Roo.data.Store({
58869             proxy: new Roo.data.MemoryProxy(rows),
58870             reader: new Roo.data.ArrayReader({}, [
58871                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58872     });
58873
58874     this.dataSource.load();
58875     this.ds = this.dataSource;
58876     this.ds.xmodule = this.xmodule || false;
58877     
58878     
58879     var cellRender = function(v,x,r)
58880     {
58881         return String.format(
58882             '<div class="fc-day  fc-widget-content"><div>' +
58883                 '<div class="fc-event-container"></div>' +
58884                 '<div class="fc-day-number">{0}</div>'+
58885                 
58886                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58887             '</div></div>', v);
58888     
58889     }
58890     
58891     
58892     this.colModel = new Roo.grid.ColumnModel( [
58893         {
58894             xtype: 'ColumnModel',
58895             xns: Roo.grid,
58896             dataIndex : 'weekday0',
58897             header : 'Sunday',
58898             renderer : cellRender
58899         },
58900         {
58901             xtype: 'ColumnModel',
58902             xns: Roo.grid,
58903             dataIndex : 'weekday1',
58904             header : 'Monday',
58905             renderer : cellRender
58906         },
58907         {
58908             xtype: 'ColumnModel',
58909             xns: Roo.grid,
58910             dataIndex : 'weekday2',
58911             header : 'Tuesday',
58912             renderer : cellRender
58913         },
58914         {
58915             xtype: 'ColumnModel',
58916             xns: Roo.grid,
58917             dataIndex : 'weekday3',
58918             header : 'Wednesday',
58919             renderer : cellRender
58920         },
58921         {
58922             xtype: 'ColumnModel',
58923             xns: Roo.grid,
58924             dataIndex : 'weekday4',
58925             header : 'Thursday',
58926             renderer : cellRender
58927         },
58928         {
58929             xtype: 'ColumnModel',
58930             xns: Roo.grid,
58931             dataIndex : 'weekday5',
58932             header : 'Friday',
58933             renderer : cellRender
58934         },
58935         {
58936             xtype: 'ColumnModel',
58937             xns: Roo.grid,
58938             dataIndex : 'weekday6',
58939             header : 'Saturday',
58940             renderer : cellRender
58941         }
58942     ]);
58943     this.cm = this.colModel;
58944     this.cm.xmodule = this.xmodule || false;
58945  
58946         
58947           
58948     //this.selModel = new Roo.grid.CellSelectionModel();
58949     //this.sm = this.selModel;
58950     //this.selModel.init(this);
58951     
58952     
58953     if(this.width){
58954         this.container.setWidth(this.width);
58955     }
58956
58957     if(this.height){
58958         this.container.setHeight(this.height);
58959     }
58960     /** @private */
58961         this.addEvents({
58962         // raw events
58963         /**
58964          * @event click
58965          * The raw click event for the entire grid.
58966          * @param {Roo.EventObject} e
58967          */
58968         "click" : true,
58969         /**
58970          * @event dblclick
58971          * The raw dblclick event for the entire grid.
58972          * @param {Roo.EventObject} e
58973          */
58974         "dblclick" : true,
58975         /**
58976          * @event contextmenu
58977          * The raw contextmenu event for the entire grid.
58978          * @param {Roo.EventObject} e
58979          */
58980         "contextmenu" : true,
58981         /**
58982          * @event mousedown
58983          * The raw mousedown event for the entire grid.
58984          * @param {Roo.EventObject} e
58985          */
58986         "mousedown" : true,
58987         /**
58988          * @event mouseup
58989          * The raw mouseup event for the entire grid.
58990          * @param {Roo.EventObject} e
58991          */
58992         "mouseup" : true,
58993         /**
58994          * @event mouseover
58995          * The raw mouseover event for the entire grid.
58996          * @param {Roo.EventObject} e
58997          */
58998         "mouseover" : true,
58999         /**
59000          * @event mouseout
59001          * The raw mouseout event for the entire grid.
59002          * @param {Roo.EventObject} e
59003          */
59004         "mouseout" : true,
59005         /**
59006          * @event keypress
59007          * The raw keypress event for the entire grid.
59008          * @param {Roo.EventObject} e
59009          */
59010         "keypress" : true,
59011         /**
59012          * @event keydown
59013          * The raw keydown event for the entire grid.
59014          * @param {Roo.EventObject} e
59015          */
59016         "keydown" : true,
59017
59018         // custom events
59019
59020         /**
59021          * @event cellclick
59022          * Fires when a cell is clicked
59023          * @param {Grid} this
59024          * @param {Number} rowIndex
59025          * @param {Number} columnIndex
59026          * @param {Roo.EventObject} e
59027          */
59028         "cellclick" : true,
59029         /**
59030          * @event celldblclick
59031          * Fires when a cell is double clicked
59032          * @param {Grid} this
59033          * @param {Number} rowIndex
59034          * @param {Number} columnIndex
59035          * @param {Roo.EventObject} e
59036          */
59037         "celldblclick" : true,
59038         /**
59039          * @event rowclick
59040          * Fires when a row is clicked
59041          * @param {Grid} this
59042          * @param {Number} rowIndex
59043          * @param {Roo.EventObject} e
59044          */
59045         "rowclick" : true,
59046         /**
59047          * @event rowdblclick
59048          * Fires when a row is double clicked
59049          * @param {Grid} this
59050          * @param {Number} rowIndex
59051          * @param {Roo.EventObject} e
59052          */
59053         "rowdblclick" : true,
59054         /**
59055          * @event headerclick
59056          * Fires when a header is clicked
59057          * @param {Grid} this
59058          * @param {Number} columnIndex
59059          * @param {Roo.EventObject} e
59060          */
59061         "headerclick" : true,
59062         /**
59063          * @event headerdblclick
59064          * Fires when a header cell is double clicked
59065          * @param {Grid} this
59066          * @param {Number} columnIndex
59067          * @param {Roo.EventObject} e
59068          */
59069         "headerdblclick" : true,
59070         /**
59071          * @event rowcontextmenu
59072          * Fires when a row is right clicked
59073          * @param {Grid} this
59074          * @param {Number} rowIndex
59075          * @param {Roo.EventObject} e
59076          */
59077         "rowcontextmenu" : true,
59078         /**
59079          * @event cellcontextmenu
59080          * Fires when a cell is right clicked
59081          * @param {Grid} this
59082          * @param {Number} rowIndex
59083          * @param {Number} cellIndex
59084          * @param {Roo.EventObject} e
59085          */
59086          "cellcontextmenu" : true,
59087         /**
59088          * @event headercontextmenu
59089          * Fires when a header is right clicked
59090          * @param {Grid} this
59091          * @param {Number} columnIndex
59092          * @param {Roo.EventObject} e
59093          */
59094         "headercontextmenu" : true,
59095         /**
59096          * @event bodyscroll
59097          * Fires when the body element is scrolled
59098          * @param {Number} scrollLeft
59099          * @param {Number} scrollTop
59100          */
59101         "bodyscroll" : true,
59102         /**
59103          * @event columnresize
59104          * Fires when the user resizes a column
59105          * @param {Number} columnIndex
59106          * @param {Number} newSize
59107          */
59108         "columnresize" : true,
59109         /**
59110          * @event columnmove
59111          * Fires when the user moves a column
59112          * @param {Number} oldIndex
59113          * @param {Number} newIndex
59114          */
59115         "columnmove" : true,
59116         /**
59117          * @event startdrag
59118          * Fires when row(s) start being dragged
59119          * @param {Grid} this
59120          * @param {Roo.GridDD} dd The drag drop object
59121          * @param {event} e The raw browser event
59122          */
59123         "startdrag" : true,
59124         /**
59125          * @event enddrag
59126          * Fires when a drag operation is complete
59127          * @param {Grid} this
59128          * @param {Roo.GridDD} dd The drag drop object
59129          * @param {event} e The raw browser event
59130          */
59131         "enddrag" : true,
59132         /**
59133          * @event dragdrop
59134          * Fires when dragged row(s) are dropped on a valid DD target
59135          * @param {Grid} this
59136          * @param {Roo.GridDD} dd The drag drop object
59137          * @param {String} targetId The target drag drop object
59138          * @param {event} e The raw browser event
59139          */
59140         "dragdrop" : true,
59141         /**
59142          * @event dragover
59143          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59144          * @param {Grid} this
59145          * @param {Roo.GridDD} dd The drag drop object
59146          * @param {String} targetId The target drag drop object
59147          * @param {event} e The raw browser event
59148          */
59149         "dragover" : true,
59150         /**
59151          * @event dragenter
59152          *  Fires when the dragged row(s) first cross another DD target while being dragged
59153          * @param {Grid} this
59154          * @param {Roo.GridDD} dd The drag drop object
59155          * @param {String} targetId The target drag drop object
59156          * @param {event} e The raw browser event
59157          */
59158         "dragenter" : true,
59159         /**
59160          * @event dragout
59161          * Fires when the dragged row(s) leave another DD target while being dragged
59162          * @param {Grid} this
59163          * @param {Roo.GridDD} dd The drag drop object
59164          * @param {String} targetId The target drag drop object
59165          * @param {event} e The raw browser event
59166          */
59167         "dragout" : true,
59168         /**
59169          * @event rowclass
59170          * Fires when a row is rendered, so you can change add a style to it.
59171          * @param {GridView} gridview   The grid view
59172          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59173          */
59174         'rowclass' : true,
59175
59176         /**
59177          * @event render
59178          * Fires when the grid is rendered
59179          * @param {Grid} grid
59180          */
59181         'render' : true,
59182             /**
59183              * @event select
59184              * Fires when a date is selected
59185              * @param {DatePicker} this
59186              * @param {Date} date The selected date
59187              */
59188         'select': true,
59189         /**
59190              * @event monthchange
59191              * Fires when the displayed month changes 
59192              * @param {DatePicker} this
59193              * @param {Date} date The selected month
59194              */
59195         'monthchange': true,
59196         /**
59197              * @event evententer
59198              * Fires when mouse over an event
59199              * @param {Calendar} this
59200              * @param {event} Event
59201              */
59202         'evententer': true,
59203         /**
59204              * @event eventleave
59205              * Fires when the mouse leaves an
59206              * @param {Calendar} this
59207              * @param {event}
59208              */
59209         'eventleave': true,
59210         /**
59211              * @event eventclick
59212              * Fires when the mouse click an
59213              * @param {Calendar} this
59214              * @param {event}
59215              */
59216         'eventclick': true,
59217         /**
59218              * @event eventrender
59219              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59220              * @param {Calendar} this
59221              * @param {data} data to be modified
59222              */
59223         'eventrender': true
59224         
59225     });
59226
59227     Roo.grid.Grid.superclass.constructor.call(this);
59228     this.on('render', function() {
59229         this.view.el.addClass('x-grid-cal'); 
59230         
59231         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59232
59233     },this);
59234     
59235     if (!Roo.grid.Calendar.style) {
59236         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59237             
59238             
59239             '.x-grid-cal .x-grid-col' :  {
59240                 height: 'auto !important',
59241                 'vertical-align': 'top'
59242             },
59243             '.x-grid-cal  .fc-event-hori' : {
59244                 height: '14px'
59245             }
59246              
59247             
59248         }, Roo.id());
59249     }
59250
59251     
59252     
59253 };
59254 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59255     /**
59256      * @cfg {Store} eventStore The store that loads events.
59257      */
59258     eventStore : 25,
59259
59260      
59261     activeDate : false,
59262     startDay : 0,
59263     autoWidth : true,
59264     monitorWindowResize : false,
59265
59266     
59267     resizeColumns : function() {
59268         var col = (this.view.el.getWidth() / 7) - 3;
59269         // loop through cols, and setWidth
59270         for(var i =0 ; i < 7 ; i++){
59271             this.cm.setColumnWidth(i, col);
59272         }
59273     },
59274      setDate :function(date) {
59275         
59276         Roo.log('setDate?');
59277         
59278         this.resizeColumns();
59279         var vd = this.activeDate;
59280         this.activeDate = date;
59281 //        if(vd && this.el){
59282 //            var t = date.getTime();
59283 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59284 //                Roo.log('using add remove');
59285 //                
59286 //                this.fireEvent('monthchange', this, date);
59287 //                
59288 //                this.cells.removeClass("fc-state-highlight");
59289 //                this.cells.each(function(c){
59290 //                   if(c.dateValue == t){
59291 //                       c.addClass("fc-state-highlight");
59292 //                       setTimeout(function(){
59293 //                            try{c.dom.firstChild.focus();}catch(e){}
59294 //                       }, 50);
59295 //                       return false;
59296 //                   }
59297 //                   return true;
59298 //                });
59299 //                return;
59300 //            }
59301 //        }
59302         
59303         var days = date.getDaysInMonth();
59304         
59305         var firstOfMonth = date.getFirstDateOfMonth();
59306         var startingPos = firstOfMonth.getDay()-this.startDay;
59307         
59308         if(startingPos < this.startDay){
59309             startingPos += 7;
59310         }
59311         
59312         var pm = date.add(Date.MONTH, -1);
59313         var prevStart = pm.getDaysInMonth()-startingPos;
59314 //        
59315         
59316         
59317         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59318         
59319         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59320         //this.cells.addClassOnOver('fc-state-hover');
59321         
59322         var cells = this.cells.elements;
59323         var textEls = this.textNodes;
59324         
59325         //Roo.each(cells, function(cell){
59326         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59327         //});
59328         
59329         days += startingPos;
59330
59331         // convert everything to numbers so it's fast
59332         var day = 86400000;
59333         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59334         //Roo.log(d);
59335         //Roo.log(pm);
59336         //Roo.log(prevStart);
59337         
59338         var today = new Date().clearTime().getTime();
59339         var sel = date.clearTime().getTime();
59340         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59341         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59342         var ddMatch = this.disabledDatesRE;
59343         var ddText = this.disabledDatesText;
59344         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59345         var ddaysText = this.disabledDaysText;
59346         var format = this.format;
59347         
59348         var setCellClass = function(cal, cell){
59349             
59350             //Roo.log('set Cell Class');
59351             cell.title = "";
59352             var t = d.getTime();
59353             
59354             //Roo.log(d);
59355             
59356             
59357             cell.dateValue = t;
59358             if(t == today){
59359                 cell.className += " fc-today";
59360                 cell.className += " fc-state-highlight";
59361                 cell.title = cal.todayText;
59362             }
59363             if(t == sel){
59364                 // disable highlight in other month..
59365                 cell.className += " fc-state-highlight";
59366                 
59367             }
59368             // disabling
59369             if(t < min) {
59370                 //cell.className = " fc-state-disabled";
59371                 cell.title = cal.minText;
59372                 return;
59373             }
59374             if(t > max) {
59375                 //cell.className = " fc-state-disabled";
59376                 cell.title = cal.maxText;
59377                 return;
59378             }
59379             if(ddays){
59380                 if(ddays.indexOf(d.getDay()) != -1){
59381                     // cell.title = ddaysText;
59382                    // cell.className = " fc-state-disabled";
59383                 }
59384             }
59385             if(ddMatch && format){
59386                 var fvalue = d.dateFormat(format);
59387                 if(ddMatch.test(fvalue)){
59388                     cell.title = ddText.replace("%0", fvalue);
59389                    cell.className = " fc-state-disabled";
59390                 }
59391             }
59392             
59393             if (!cell.initialClassName) {
59394                 cell.initialClassName = cell.dom.className;
59395             }
59396             
59397             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59398         };
59399
59400         var i = 0;
59401         
59402         for(; i < startingPos; i++) {
59403             cells[i].dayName =  (++prevStart);
59404             Roo.log(textEls[i]);
59405             d.setDate(d.getDate()+1);
59406             
59407             //cells[i].className = "fc-past fc-other-month";
59408             setCellClass(this, cells[i]);
59409         }
59410         
59411         var intDay = 0;
59412         
59413         for(; i < days; i++){
59414             intDay = i - startingPos + 1;
59415             cells[i].dayName =  (intDay);
59416             d.setDate(d.getDate()+1);
59417             
59418             cells[i].className = ''; // "x-date-active";
59419             setCellClass(this, cells[i]);
59420         }
59421         var extraDays = 0;
59422         
59423         for(; i < 42; i++) {
59424             //textEls[i].innerHTML = (++extraDays);
59425             
59426             d.setDate(d.getDate()+1);
59427             cells[i].dayName = (++extraDays);
59428             cells[i].className = "fc-future fc-other-month";
59429             setCellClass(this, cells[i]);
59430         }
59431         
59432         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59433         
59434         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59435         
59436         // this will cause all the cells to mis
59437         var rows= [];
59438         var i =0;
59439         for (var r = 0;r < 6;r++) {
59440             for (var c =0;c < 7;c++) {
59441                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59442             }    
59443         }
59444         
59445         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59446         for(i=0;i<cells.length;i++) {
59447             
59448             this.cells.elements[i].dayName = cells[i].dayName ;
59449             this.cells.elements[i].className = cells[i].className;
59450             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59451             this.cells.elements[i].title = cells[i].title ;
59452             this.cells.elements[i].dateValue = cells[i].dateValue ;
59453         }
59454         
59455         
59456         
59457         
59458         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59459         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59460         
59461         ////if(totalRows != 6){
59462             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59463            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59464        // }
59465         
59466         this.fireEvent('monthchange', this, date);
59467         
59468         
59469     },
59470  /**
59471      * Returns the grid's SelectionModel.
59472      * @return {SelectionModel}
59473      */
59474     getSelectionModel : function(){
59475         if(!this.selModel){
59476             this.selModel = new Roo.grid.CellSelectionModel();
59477         }
59478         return this.selModel;
59479     },
59480
59481     load: function() {
59482         this.eventStore.load()
59483         
59484         
59485         
59486     },
59487     
59488     findCell : function(dt) {
59489         dt = dt.clearTime().getTime();
59490         var ret = false;
59491         this.cells.each(function(c){
59492             //Roo.log("check " +c.dateValue + '?=' + dt);
59493             if(c.dateValue == dt){
59494                 ret = c;
59495                 return false;
59496             }
59497             return true;
59498         });
59499         
59500         return ret;
59501     },
59502     
59503     findCells : function(rec) {
59504         var s = rec.data.start_dt.clone().clearTime().getTime();
59505        // Roo.log(s);
59506         var e= rec.data.end_dt.clone().clearTime().getTime();
59507        // Roo.log(e);
59508         var ret = [];
59509         this.cells.each(function(c){
59510              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59511             
59512             if(c.dateValue > e){
59513                 return ;
59514             }
59515             if(c.dateValue < s){
59516                 return ;
59517             }
59518             ret.push(c);
59519         });
59520         
59521         return ret;    
59522     },
59523     
59524     findBestRow: function(cells)
59525     {
59526         var ret = 0;
59527         
59528         for (var i =0 ; i < cells.length;i++) {
59529             ret  = Math.max(cells[i].rows || 0,ret);
59530         }
59531         return ret;
59532         
59533     },
59534     
59535     
59536     addItem : function(rec)
59537     {
59538         // look for vertical location slot in
59539         var cells = this.findCells(rec);
59540         
59541         rec.row = this.findBestRow(cells);
59542         
59543         // work out the location.
59544         
59545         var crow = false;
59546         var rows = [];
59547         for(var i =0; i < cells.length; i++) {
59548             if (!crow) {
59549                 crow = {
59550                     start : cells[i],
59551                     end :  cells[i]
59552                 };
59553                 continue;
59554             }
59555             if (crow.start.getY() == cells[i].getY()) {
59556                 // on same row.
59557                 crow.end = cells[i];
59558                 continue;
59559             }
59560             // different row.
59561             rows.push(crow);
59562             crow = {
59563                 start: cells[i],
59564                 end : cells[i]
59565             };
59566             
59567         }
59568         
59569         rows.push(crow);
59570         rec.els = [];
59571         rec.rows = rows;
59572         rec.cells = cells;
59573         for (var i = 0; i < cells.length;i++) {
59574             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59575             
59576         }
59577         
59578         
59579     },
59580     
59581     clearEvents: function() {
59582         
59583         if (!this.eventStore.getCount()) {
59584             return;
59585         }
59586         // reset number of rows in cells.
59587         Roo.each(this.cells.elements, function(c){
59588             c.rows = 0;
59589         });
59590         
59591         this.eventStore.each(function(e) {
59592             this.clearEvent(e);
59593         },this);
59594         
59595     },
59596     
59597     clearEvent : function(ev)
59598     {
59599         if (ev.els) {
59600             Roo.each(ev.els, function(el) {
59601                 el.un('mouseenter' ,this.onEventEnter, this);
59602                 el.un('mouseleave' ,this.onEventLeave, this);
59603                 el.remove();
59604             },this);
59605             ev.els = [];
59606         }
59607     },
59608     
59609     
59610     renderEvent : function(ev,ctr) {
59611         if (!ctr) {
59612              ctr = this.view.el.select('.fc-event-container',true).first();
59613         }
59614         
59615          
59616         this.clearEvent(ev);
59617             //code
59618        
59619         
59620         
59621         ev.els = [];
59622         var cells = ev.cells;
59623         var rows = ev.rows;
59624         this.fireEvent('eventrender', this, ev);
59625         
59626         for(var i =0; i < rows.length; i++) {
59627             
59628             cls = '';
59629             if (i == 0) {
59630                 cls += ' fc-event-start';
59631             }
59632             if ((i+1) == rows.length) {
59633                 cls += ' fc-event-end';
59634             }
59635             
59636             //Roo.log(ev.data);
59637             // how many rows should it span..
59638             var cg = this.eventTmpl.append(ctr,Roo.apply({
59639                 fccls : cls
59640                 
59641             }, ev.data) , true);
59642             
59643             
59644             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59645             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59646             cg.on('click', this.onEventClick, this, ev);
59647             
59648             ev.els.push(cg);
59649             
59650             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59651             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59652             //Roo.log(cg);
59653              
59654             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59655             cg.setWidth(ebox.right - sbox.x -2);
59656         }
59657     },
59658     
59659     renderEvents: function()
59660     {   
59661         // first make sure there is enough space..
59662         
59663         if (!this.eventTmpl) {
59664             this.eventTmpl = new Roo.Template(
59665                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59666                     '<div class="fc-event-inner">' +
59667                         '<span class="fc-event-time">{time}</span>' +
59668                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59669                     '</div>' +
59670                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59671                 '</div>'
59672             );
59673                 
59674         }
59675                
59676         
59677         
59678         this.cells.each(function(c) {
59679             //Roo.log(c.select('.fc-day-content div',true).first());
59680             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59681         });
59682         
59683         var ctr = this.view.el.select('.fc-event-container',true).first();
59684         
59685         var cls;
59686         this.eventStore.each(function(ev){
59687             
59688             this.renderEvent(ev);
59689              
59690              
59691         }, this);
59692         this.view.layout();
59693         
59694     },
59695     
59696     onEventEnter: function (e, el,event,d) {
59697         this.fireEvent('evententer', this, el, event);
59698     },
59699     
59700     onEventLeave: function (e, el,event,d) {
59701         this.fireEvent('eventleave', this, el, event);
59702     },
59703     
59704     onEventClick: function (e, el,event,d) {
59705         this.fireEvent('eventclick', this, el, event);
59706     },
59707     
59708     onMonthChange: function () {
59709         this.store.load();
59710     },
59711     
59712     onLoad: function () {
59713         
59714         //Roo.log('calendar onload');
59715 //         
59716         if(this.eventStore.getCount() > 0){
59717             
59718            
59719             
59720             this.eventStore.each(function(d){
59721                 
59722                 
59723                 // FIXME..
59724                 var add =   d.data;
59725                 if (typeof(add.end_dt) == 'undefined')  {
59726                     Roo.log("Missing End time in calendar data: ");
59727                     Roo.log(d);
59728                     return;
59729                 }
59730                 if (typeof(add.start_dt) == 'undefined')  {
59731                     Roo.log("Missing Start time in calendar data: ");
59732                     Roo.log(d);
59733                     return;
59734                 }
59735                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59736                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59737                 add.id = add.id || d.id;
59738                 add.title = add.title || '??';
59739                 
59740                 this.addItem(d);
59741                 
59742              
59743             },this);
59744         }
59745         
59746         this.renderEvents();
59747     }
59748     
59749
59750 });
59751 /*
59752  grid : {
59753                 xtype: 'Grid',
59754                 xns: Roo.grid,
59755                 listeners : {
59756                     render : function ()
59757                     {
59758                         _this.grid = this;
59759                         
59760                         if (!this.view.el.hasClass('course-timesheet')) {
59761                             this.view.el.addClass('course-timesheet');
59762                         }
59763                         if (this.tsStyle) {
59764                             this.ds.load({});
59765                             return; 
59766                         }
59767                         Roo.log('width');
59768                         Roo.log(_this.grid.view.el.getWidth());
59769                         
59770                         
59771                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59772                             '.course-timesheet .x-grid-row' : {
59773                                 height: '80px'
59774                             },
59775                             '.x-grid-row td' : {
59776                                 'vertical-align' : 0
59777                             },
59778                             '.course-edit-link' : {
59779                                 'color' : 'blue',
59780                                 'text-overflow' : 'ellipsis',
59781                                 'overflow' : 'hidden',
59782                                 'white-space' : 'nowrap',
59783                                 'cursor' : 'pointer'
59784                             },
59785                             '.sub-link' : {
59786                                 'color' : 'green'
59787                             },
59788                             '.de-act-sup-link' : {
59789                                 'color' : 'purple',
59790                                 'text-decoration' : 'line-through'
59791                             },
59792                             '.de-act-link' : {
59793                                 'color' : 'red',
59794                                 'text-decoration' : 'line-through'
59795                             },
59796                             '.course-timesheet .course-highlight' : {
59797                                 'border-top-style': 'dashed !important',
59798                                 'border-bottom-bottom': 'dashed !important'
59799                             },
59800                             '.course-timesheet .course-item' : {
59801                                 'font-family'   : 'tahoma, arial, helvetica',
59802                                 'font-size'     : '11px',
59803                                 'overflow'      : 'hidden',
59804                                 'padding-left'  : '10px',
59805                                 'padding-right' : '10px',
59806                                 'padding-top' : '10px' 
59807                             }
59808                             
59809                         }, Roo.id());
59810                                 this.ds.load({});
59811                     }
59812                 },
59813                 autoWidth : true,
59814                 monitorWindowResize : false,
59815                 cellrenderer : function(v,x,r)
59816                 {
59817                     return v;
59818                 },
59819                 sm : {
59820                     xtype: 'CellSelectionModel',
59821                     xns: Roo.grid
59822                 },
59823                 dataSource : {
59824                     xtype: 'Store',
59825                     xns: Roo.data,
59826                     listeners : {
59827                         beforeload : function (_self, options)
59828                         {
59829                             options.params = options.params || {};
59830                             options.params._month = _this.monthField.getValue();
59831                             options.params.limit = 9999;
59832                             options.params['sort'] = 'when_dt';    
59833                             options.params['dir'] = 'ASC';    
59834                             this.proxy.loadResponse = this.loadResponse;
59835                             Roo.log("load?");
59836                             //this.addColumns();
59837                         },
59838                         load : function (_self, records, options)
59839                         {
59840                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59841                                 // if you click on the translation.. you can edit it...
59842                                 var el = Roo.get(this);
59843                                 var id = el.dom.getAttribute('data-id');
59844                                 var d = el.dom.getAttribute('data-date');
59845                                 var t = el.dom.getAttribute('data-time');
59846                                 //var id = this.child('span').dom.textContent;
59847                                 
59848                                 //Roo.log(this);
59849                                 Pman.Dialog.CourseCalendar.show({
59850                                     id : id,
59851                                     when_d : d,
59852                                     when_t : t,
59853                                     productitem_active : id ? 1 : 0
59854                                 }, function() {
59855                                     _this.grid.ds.load({});
59856                                 });
59857                            
59858                            });
59859                            
59860                            _this.panel.fireEvent('resize', [ '', '' ]);
59861                         }
59862                     },
59863                     loadResponse : function(o, success, response){
59864                             // this is overridden on before load..
59865                             
59866                             Roo.log("our code?");       
59867                             //Roo.log(success);
59868                             //Roo.log(response)
59869                             delete this.activeRequest;
59870                             if(!success){
59871                                 this.fireEvent("loadexception", this, o, response);
59872                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59873                                 return;
59874                             }
59875                             var result;
59876                             try {
59877                                 result = o.reader.read(response);
59878                             }catch(e){
59879                                 Roo.log("load exception?");
59880                                 this.fireEvent("loadexception", this, o, response, e);
59881                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59882                                 return;
59883                             }
59884                             Roo.log("ready...");        
59885                             // loop through result.records;
59886                             // and set this.tdate[date] = [] << array of records..
59887                             _this.tdata  = {};
59888                             Roo.each(result.records, function(r){
59889                                 //Roo.log(r.data);
59890                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59891                                     _this.tdata[r.data.when_dt.format('j')] = [];
59892                                 }
59893                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59894                             });
59895                             
59896                             //Roo.log(_this.tdata);
59897                             
59898                             result.records = [];
59899                             result.totalRecords = 6;
59900                     
59901                             // let's generate some duumy records for the rows.
59902                             //var st = _this.dateField.getValue();
59903                             
59904                             // work out monday..
59905                             //st = st.add(Date.DAY, -1 * st.format('w'));
59906                             
59907                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59908                             
59909                             var firstOfMonth = date.getFirstDayOfMonth();
59910                             var days = date.getDaysInMonth();
59911                             var d = 1;
59912                             var firstAdded = false;
59913                             for (var i = 0; i < result.totalRecords ; i++) {
59914                                 //var d= st.add(Date.DAY, i);
59915                                 var row = {};
59916                                 var added = 0;
59917                                 for(var w = 0 ; w < 7 ; w++){
59918                                     if(!firstAdded && firstOfMonth != w){
59919                                         continue;
59920                                     }
59921                                     if(d > days){
59922                                         continue;
59923                                     }
59924                                     firstAdded = true;
59925                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59926                                     row['weekday'+w] = String.format(
59927                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59928                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59929                                                     d,
59930                                                     date.format('Y-m-')+dd
59931                                                 );
59932                                     added++;
59933                                     if(typeof(_this.tdata[d]) != 'undefined'){
59934                                         Roo.each(_this.tdata[d], function(r){
59935                                             var is_sub = '';
59936                                             var deactive = '';
59937                                             var id = r.id;
59938                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59939                                             if(r.parent_id*1>0){
59940                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59941                                                 id = r.parent_id;
59942                                             }
59943                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59944                                                 deactive = 'de-act-link';
59945                                             }
59946                                             
59947                                             row['weekday'+w] += String.format(
59948                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59949                                                     id, //0
59950                                                     r.product_id_name, //1
59951                                                     r.when_dt.format('h:ia'), //2
59952                                                     is_sub, //3
59953                                                     deactive, //4
59954                                                     desc // 5
59955                                             );
59956                                         });
59957                                     }
59958                                     d++;
59959                                 }
59960                                 
59961                                 // only do this if something added..
59962                                 if(added > 0){ 
59963                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59964                                 }
59965                                 
59966                                 
59967                                 // push it twice. (second one with an hour..
59968                                 
59969                             }
59970                             //Roo.log(result);
59971                             this.fireEvent("load", this, o, o.request.arg);
59972                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59973                         },
59974                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59975                     proxy : {
59976                         xtype: 'HttpProxy',
59977                         xns: Roo.data,
59978                         method : 'GET',
59979                         url : baseURL + '/Roo/Shop_course.php'
59980                     },
59981                     reader : {
59982                         xtype: 'JsonReader',
59983                         xns: Roo.data,
59984                         id : 'id',
59985                         fields : [
59986                             {
59987                                 'name': 'id',
59988                                 'type': 'int'
59989                             },
59990                             {
59991                                 'name': 'when_dt',
59992                                 'type': 'string'
59993                             },
59994                             {
59995                                 'name': 'end_dt',
59996                                 'type': 'string'
59997                             },
59998                             {
59999                                 'name': 'parent_id',
60000                                 'type': 'int'
60001                             },
60002                             {
60003                                 'name': 'product_id',
60004                                 'type': 'int'
60005                             },
60006                             {
60007                                 'name': 'productitem_id',
60008                                 'type': 'int'
60009                             },
60010                             {
60011                                 'name': 'guid',
60012                                 'type': 'int'
60013                             }
60014                         ]
60015                     }
60016                 },
60017                 toolbar : {
60018                     xtype: 'Toolbar',
60019                     xns: Roo,
60020                     items : [
60021                         {
60022                             xtype: 'Button',
60023                             xns: Roo.Toolbar,
60024                             listeners : {
60025                                 click : function (_self, e)
60026                                 {
60027                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60028                                     sd.setMonth(sd.getMonth()-1);
60029                                     _this.monthField.setValue(sd.format('Y-m-d'));
60030                                     _this.grid.ds.load({});
60031                                 }
60032                             },
60033                             text : "Back"
60034                         },
60035                         {
60036                             xtype: 'Separator',
60037                             xns: Roo.Toolbar
60038                         },
60039                         {
60040                             xtype: 'MonthField',
60041                             xns: Roo.form,
60042                             listeners : {
60043                                 render : function (_self)
60044                                 {
60045                                     _this.monthField = _self;
60046                                    // _this.monthField.set  today
60047                                 },
60048                                 select : function (combo, date)
60049                                 {
60050                                     _this.grid.ds.load({});
60051                                 }
60052                             },
60053                             value : (function() { return new Date(); })()
60054                         },
60055                         {
60056                             xtype: 'Separator',
60057                             xns: Roo.Toolbar
60058                         },
60059                         {
60060                             xtype: 'TextItem',
60061                             xns: Roo.Toolbar,
60062                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60063                         },
60064                         {
60065                             xtype: 'Fill',
60066                             xns: Roo.Toolbar
60067                         },
60068                         {
60069                             xtype: 'Button',
60070                             xns: Roo.Toolbar,
60071                             listeners : {
60072                                 click : function (_self, e)
60073                                 {
60074                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60075                                     sd.setMonth(sd.getMonth()+1);
60076                                     _this.monthField.setValue(sd.format('Y-m-d'));
60077                                     _this.grid.ds.load({});
60078                                 }
60079                             },
60080                             text : "Next"
60081                         }
60082                     ]
60083                 },
60084                  
60085             }
60086         };
60087         
60088         *//*
60089  * Based on:
60090  * Ext JS Library 1.1.1
60091  * Copyright(c) 2006-2007, Ext JS, LLC.
60092  *
60093  * Originally Released Under LGPL - original licence link has changed is not relivant.
60094  *
60095  * Fork - LGPL
60096  * <script type="text/javascript">
60097  */
60098  
60099 /**
60100  * @class Roo.LoadMask
60101  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60102  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60103  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60104  * element's UpdateManager load indicator and will be destroyed after the initial load.
60105  * @constructor
60106  * Create a new LoadMask
60107  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60108  * @param {Object} config The config object
60109  */
60110 Roo.LoadMask = function(el, config){
60111     this.el = Roo.get(el);
60112     Roo.apply(this, config);
60113     if(this.store){
60114         this.store.on('beforeload', this.onBeforeLoad, this);
60115         this.store.on('load', this.onLoad, this);
60116         this.store.on('loadexception', this.onLoadException, this);
60117         this.removeMask = false;
60118     }else{
60119         var um = this.el.getUpdateManager();
60120         um.showLoadIndicator = false; // disable the default indicator
60121         um.on('beforeupdate', this.onBeforeLoad, this);
60122         um.on('update', this.onLoad, this);
60123         um.on('failure', this.onLoad, this);
60124         this.removeMask = true;
60125     }
60126 };
60127
60128 Roo.LoadMask.prototype = {
60129     /**
60130      * @cfg {Boolean} removeMask
60131      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60132      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60133      */
60134     /**
60135      * @cfg {String} msg
60136      * The text to display in a centered loading message box (defaults to 'Loading...')
60137      */
60138     msg : 'Loading...',
60139     /**
60140      * @cfg {String} msgCls
60141      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60142      */
60143     msgCls : 'x-mask-loading',
60144
60145     /**
60146      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60147      * @type Boolean
60148      */
60149     disabled: false,
60150
60151     /**
60152      * Disables the mask to prevent it from being displayed
60153      */
60154     disable : function(){
60155        this.disabled = true;
60156     },
60157
60158     /**
60159      * Enables the mask so that it can be displayed
60160      */
60161     enable : function(){
60162         this.disabled = false;
60163     },
60164     
60165     onLoadException : function()
60166     {
60167         Roo.log(arguments);
60168         
60169         if (typeof(arguments[3]) != 'undefined') {
60170             Roo.MessageBox.alert("Error loading",arguments[3]);
60171         } 
60172         /*
60173         try {
60174             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60175                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60176             }   
60177         } catch(e) {
60178             
60179         }
60180         */
60181     
60182         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60183     },
60184     // private
60185     onLoad : function()
60186     {
60187         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60188     },
60189
60190     // private
60191     onBeforeLoad : function(){
60192         if(!this.disabled){
60193             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60194         }
60195     },
60196
60197     // private
60198     destroy : function(){
60199         if(this.store){
60200             this.store.un('beforeload', this.onBeforeLoad, this);
60201             this.store.un('load', this.onLoad, this);
60202             this.store.un('loadexception', this.onLoadException, this);
60203         }else{
60204             var um = this.el.getUpdateManager();
60205             um.un('beforeupdate', this.onBeforeLoad, this);
60206             um.un('update', this.onLoad, this);
60207             um.un('failure', this.onLoad, this);
60208         }
60209     }
60210 };/*
60211  * Based on:
60212  * Ext JS Library 1.1.1
60213  * Copyright(c) 2006-2007, Ext JS, LLC.
60214  *
60215  * Originally Released Under LGPL - original licence link has changed is not relivant.
60216  *
60217  * Fork - LGPL
60218  * <script type="text/javascript">
60219  */
60220
60221
60222 /**
60223  * @class Roo.XTemplate
60224  * @extends Roo.Template
60225  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60226 <pre><code>
60227 var t = new Roo.XTemplate(
60228         '&lt;select name="{name}"&gt;',
60229                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60230         '&lt;/select&gt;'
60231 );
60232  
60233 // then append, applying the master template values
60234  </code></pre>
60235  *
60236  * Supported features:
60237  *
60238  *  Tags:
60239
60240 <pre><code>
60241       {a_variable} - output encoded.
60242       {a_variable.format:("Y-m-d")} - call a method on the variable
60243       {a_variable:raw} - unencoded output
60244       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60245       {a_variable:this.method_on_template(...)} - call a method on the template object.
60246  
60247 </code></pre>
60248  *  The tpl tag:
60249 <pre><code>
60250         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60251         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60252         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60253         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60254   
60255         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60256         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60257 </code></pre>
60258  *      
60259  */
60260 Roo.XTemplate = function()
60261 {
60262     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60263     if (this.html) {
60264         this.compile();
60265     }
60266 };
60267
60268
60269 Roo.extend(Roo.XTemplate, Roo.Template, {
60270
60271     /**
60272      * The various sub templates
60273      */
60274     tpls : false,
60275     /**
60276      *
60277      * basic tag replacing syntax
60278      * WORD:WORD()
60279      *
60280      * // you can fake an object call by doing this
60281      *  x.t:(test,tesT) 
60282      * 
60283      */
60284     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60285
60286     /**
60287      * compile the template
60288      *
60289      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60290      *
60291      */
60292     compile: function()
60293     {
60294         var s = this.html;
60295      
60296         s = ['<tpl>', s, '</tpl>'].join('');
60297     
60298         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60299             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60300             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60301             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60302             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60303             m,
60304             id     = 0,
60305             tpls   = [];
60306     
60307         while(true == !!(m = s.match(re))){
60308             var forMatch   = m[0].match(nameRe),
60309                 ifMatch   = m[0].match(ifRe),
60310                 execMatch   = m[0].match(execRe),
60311                 namedMatch   = m[0].match(namedRe),
60312                 
60313                 exp  = null, 
60314                 fn   = null,
60315                 exec = null,
60316                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60317                 
60318             if (ifMatch) {
60319                 // if - puts fn into test..
60320                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60321                 if(exp){
60322                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60323                 }
60324             }
60325             
60326             if (execMatch) {
60327                 // exec - calls a function... returns empty if true is  returned.
60328                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60329                 if(exp){
60330                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60331                 }
60332             }
60333             
60334             
60335             if (name) {
60336                 // for = 
60337                 switch(name){
60338                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60339                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60340                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60341                 }
60342             }
60343             var uid = namedMatch ? namedMatch[1] : id;
60344             
60345             
60346             tpls.push({
60347                 id:     namedMatch ? namedMatch[1] : id,
60348                 target: name,
60349                 exec:   exec,
60350                 test:   fn,
60351                 body:   m[1] || ''
60352             });
60353             if (namedMatch) {
60354                 s = s.replace(m[0], '');
60355             } else { 
60356                 s = s.replace(m[0], '{xtpl'+ id + '}');
60357             }
60358             ++id;
60359         }
60360         this.tpls = [];
60361         for(var i = tpls.length-1; i >= 0; --i){
60362             this.compileTpl(tpls[i]);
60363             this.tpls[tpls[i].id] = tpls[i];
60364         }
60365         this.master = tpls[tpls.length-1];
60366         return this;
60367     },
60368     /**
60369      * same as applyTemplate, except it's done to one of the subTemplates
60370      * when using named templates, you can do:
60371      *
60372      * var str = pl.applySubTemplate('your-name', values);
60373      *
60374      * 
60375      * @param {Number} id of the template
60376      * @param {Object} values to apply to template
60377      * @param {Object} parent (normaly the instance of this object)
60378      */
60379     applySubTemplate : function(id, values, parent)
60380     {
60381         
60382         
60383         var t = this.tpls[id];
60384         
60385         
60386         try { 
60387             if(t.test && !t.test.call(this, values, parent)){
60388                 return '';
60389             }
60390         } catch(e) {
60391             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60392             Roo.log(e.toString());
60393             Roo.log(t.test);
60394             return ''
60395         }
60396         try { 
60397             
60398             if(t.exec && t.exec.call(this, values, parent)){
60399                 return '';
60400             }
60401         } catch(e) {
60402             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60403             Roo.log(e.toString());
60404             Roo.log(t.exec);
60405             return ''
60406         }
60407         try {
60408             var vs = t.target ? t.target.call(this, values, parent) : values;
60409             parent = t.target ? values : parent;
60410             if(t.target && vs instanceof Array){
60411                 var buf = [];
60412                 for(var i = 0, len = vs.length; i < len; i++){
60413                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60414                 }
60415                 return buf.join('');
60416             }
60417             return t.compiled.call(this, vs, parent);
60418         } catch (e) {
60419             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60420             Roo.log(e.toString());
60421             Roo.log(t.compiled);
60422             return '';
60423         }
60424     },
60425
60426     compileTpl : function(tpl)
60427     {
60428         var fm = Roo.util.Format;
60429         var useF = this.disableFormats !== true;
60430         var sep = Roo.isGecko ? "+" : ",";
60431         var undef = function(str) {
60432             Roo.log("Property not found :"  + str);
60433             return '';
60434         };
60435         
60436         var fn = function(m, name, format, args)
60437         {
60438             //Roo.log(arguments);
60439             args = args ? args.replace(/\\'/g,"'") : args;
60440             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60441             if (typeof(format) == 'undefined') {
60442                 format= 'htmlEncode';
60443             }
60444             if (format == 'raw' ) {
60445                 format = false;
60446             }
60447             
60448             if(name.substr(0, 4) == 'xtpl'){
60449                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60450             }
60451             
60452             // build an array of options to determine if value is undefined..
60453             
60454             // basically get 'xxxx.yyyy' then do
60455             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60456             //    (function () { Roo.log("Property not found"); return ''; })() :
60457             //    ......
60458             
60459             var udef_ar = [];
60460             var lookfor = '';
60461             Roo.each(name.split('.'), function(st) {
60462                 lookfor += (lookfor.length ? '.': '') + st;
60463                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60464             });
60465             
60466             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60467             
60468             
60469             if(format && useF){
60470                 
60471                 args = args ? ',' + args : "";
60472                  
60473                 if(format.substr(0, 5) != "this."){
60474                     format = "fm." + format + '(';
60475                 }else{
60476                     format = 'this.call("'+ format.substr(5) + '", ';
60477                     args = ", values";
60478                 }
60479                 
60480                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60481             }
60482              
60483             if (args.length) {
60484                 // called with xxyx.yuu:(test,test)
60485                 // change to ()
60486                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60487             }
60488             // raw.. - :raw modifier..
60489             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60490             
60491         };
60492         var body;
60493         // branched to use + in gecko and [].join() in others
60494         if(Roo.isGecko){
60495             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60496                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60497                     "';};};";
60498         }else{
60499             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60500             body.push(tpl.body.replace(/(\r\n|\n)/g,
60501                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60502             body.push("'].join('');};};");
60503             body = body.join('');
60504         }
60505         
60506         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60507        
60508         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60509         eval(body);
60510         
60511         return this;
60512     },
60513
60514     applyTemplate : function(values){
60515         return this.master.compiled.call(this, values, {});
60516         //var s = this.subs;
60517     },
60518
60519     apply : function(){
60520         return this.applyTemplate.apply(this, arguments);
60521     }
60522
60523  });
60524
60525 Roo.XTemplate.from = function(el){
60526     el = Roo.getDom(el);
60527     return new Roo.XTemplate(el.value || el.innerHTML);
60528 };