e33dafc44f306ba7f41ddd079fe03ec33ef1e091
[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.isIOS ? Roo.get(document.body) : Roo.get(document.documentElement);
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 (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7186                     return parent;
7187                 }
7188                 
7189                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7190                     return Roo.isIOS ? Roo.get(document.body) : Roo.get(document.documentElement);
7191                 }
7192             }
7193             
7194             return Roo.isIOS ? Roo.get(document.body) : Roo.get(document.documentElement);
7195         },
7196
7197         /**
7198          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7199          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7200          * @param {String} selector The simple selector to test
7201          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7202                 search as a number or element (defaults to 10 || document.body)
7203          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7204          */
7205         up : function(simpleSelector, maxDepth){
7206             return this.findParentNode(simpleSelector, maxDepth, true);
7207         },
7208
7209
7210
7211         /**
7212          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7213          * @param {String} selector The simple selector to test
7214          * @return {Boolean} True if this element matches the selector, else false
7215          */
7216         is : function(simpleSelector){
7217             return Roo.DomQuery.is(this.dom, simpleSelector);
7218         },
7219
7220         /**
7221          * Perform animation on this element.
7222          * @param {Object} args The YUI animation control args
7223          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7224          * @param {Function} onComplete (optional) Function to call when animation completes
7225          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7226          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7227          * @return {Roo.Element} this
7228          */
7229         animate : function(args, duration, onComplete, easing, animType){
7230             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7231             return this;
7232         },
7233
7234         /*
7235          * @private Internal animation call
7236          */
7237         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7238             animType = animType || 'run';
7239             opt = opt || {};
7240             var anim = Roo.lib.Anim[animType](
7241                 this.dom, args,
7242                 (opt.duration || defaultDur) || .35,
7243                 (opt.easing || defaultEase) || 'easeOut',
7244                 function(){
7245                     Roo.callback(cb, this);
7246                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7247                 },
7248                 this
7249             );
7250             opt.anim = anim;
7251             return anim;
7252         },
7253
7254         // private legacy anim prep
7255         preanim : function(a, i){
7256             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7257         },
7258
7259         /**
7260          * Removes worthless text nodes
7261          * @param {Boolean} forceReclean (optional) By default the element
7262          * keeps track if it has been cleaned already so
7263          * you can call this over and over. However, if you update the element and
7264          * need to force a reclean, you can pass true.
7265          */
7266         clean : function(forceReclean){
7267             if(this.isCleaned && forceReclean !== true){
7268                 return this;
7269             }
7270             var ns = /\S/;
7271             var d = this.dom, n = d.firstChild, ni = -1;
7272             while(n){
7273                 var nx = n.nextSibling;
7274                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7275                     d.removeChild(n);
7276                 }else{
7277                     n.nodeIndex = ++ni;
7278                 }
7279                 n = nx;
7280             }
7281             this.isCleaned = true;
7282             return this;
7283         },
7284
7285         // private
7286         calcOffsetsTo : function(el){
7287             el = Roo.get(el);
7288             var d = el.dom;
7289             var restorePos = false;
7290             if(el.getStyle('position') == 'static'){
7291                 el.position('relative');
7292                 restorePos = true;
7293             }
7294             var x = 0, y =0;
7295             var op = this.dom;
7296             while(op && op != d && op.tagName != 'HTML'){
7297                 x+= op.offsetLeft;
7298                 y+= op.offsetTop;
7299                 op = op.offsetParent;
7300             }
7301             if(restorePos){
7302                 el.position('static');
7303             }
7304             return [x, y];
7305         },
7306
7307         /**
7308          * Scrolls this element into view within the passed container.
7309          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7310          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7311          * @return {Roo.Element} this
7312          */
7313         scrollIntoView : function(container, hscroll){
7314             var c = Roo.getDom(container) || document.body;
7315             var el = this.dom;
7316
7317             var o = this.calcOffsetsTo(c),
7318                 l = o[0],
7319                 t = o[1],
7320                 b = t+el.offsetHeight,
7321                 r = l+el.offsetWidth;
7322
7323             var ch = c.clientHeight;
7324             var ct = parseInt(c.scrollTop, 10);
7325             var cl = parseInt(c.scrollLeft, 10);
7326             var cb = ct + ch;
7327             var cr = cl + c.clientWidth;
7328
7329             if(t < ct){
7330                 c.scrollTop = t;
7331             }else if(b > cb){
7332                 c.scrollTop = b-ch;
7333             }
7334
7335             if(hscroll !== false){
7336                 if(l < cl){
7337                     c.scrollLeft = l;
7338                 }else if(r > cr){
7339                     c.scrollLeft = r-c.clientWidth;
7340                 }
7341             }
7342             return this;
7343         },
7344
7345         // private
7346         scrollChildIntoView : function(child, hscroll){
7347             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7348         },
7349
7350         /**
7351          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7352          * the new height may not be available immediately.
7353          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7354          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7355          * @param {Function} onComplete (optional) Function to call when animation completes
7356          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7357          * @return {Roo.Element} this
7358          */
7359         autoHeight : function(animate, duration, onComplete, easing){
7360             var oldHeight = this.getHeight();
7361             this.clip();
7362             this.setHeight(1); // force clipping
7363             setTimeout(function(){
7364                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7365                 if(!animate){
7366                     this.setHeight(height);
7367                     this.unclip();
7368                     if(typeof onComplete == "function"){
7369                         onComplete();
7370                     }
7371                 }else{
7372                     this.setHeight(oldHeight); // restore original height
7373                     this.setHeight(height, animate, duration, function(){
7374                         this.unclip();
7375                         if(typeof onComplete == "function") { onComplete(); }
7376                     }.createDelegate(this), easing);
7377                 }
7378             }.createDelegate(this), 0);
7379             return this;
7380         },
7381
7382         /**
7383          * Returns true if this element is an ancestor of the passed element
7384          * @param {HTMLElement/String} el The element to check
7385          * @return {Boolean} True if this element is an ancestor of el, else false
7386          */
7387         contains : function(el){
7388             if(!el){return false;}
7389             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7390         },
7391
7392         /**
7393          * Checks whether the element is currently visible using both visibility and display properties.
7394          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7395          * @return {Boolean} True if the element is currently visible, else false
7396          */
7397         isVisible : function(deep) {
7398             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7399             if(deep !== true || !vis){
7400                 return vis;
7401             }
7402             var p = this.dom.parentNode;
7403             while(p && p.tagName.toLowerCase() != "body"){
7404                 if(!Roo.fly(p, '_isVisible').isVisible()){
7405                     return false;
7406                 }
7407                 p = p.parentNode;
7408             }
7409             return true;
7410         },
7411
7412         /**
7413          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7414          * @param {String} selector The CSS selector
7415          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7416          * @return {CompositeElement/CompositeElementLite} The composite element
7417          */
7418         select : function(selector, unique){
7419             return El.select(selector, unique, this.dom);
7420         },
7421
7422         /**
7423          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7424          * @param {String} selector The CSS selector
7425          * @return {Array} An array of the matched nodes
7426          */
7427         query : function(selector, unique){
7428             return Roo.DomQuery.select(selector, this.dom);
7429         },
7430
7431         /**
7432          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7433          * @param {String} selector The CSS selector
7434          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7435          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7436          */
7437         child : function(selector, returnDom){
7438             var n = Roo.DomQuery.selectNode(selector, this.dom);
7439             return returnDom ? n : Roo.get(n);
7440         },
7441
7442         /**
7443          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7444          * @param {String} selector The CSS selector
7445          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7446          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7447          */
7448         down : function(selector, returnDom){
7449             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7450             return returnDom ? n : Roo.get(n);
7451         },
7452
7453         /**
7454          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7455          * @param {String} group The group the DD object is member of
7456          * @param {Object} config The DD config object
7457          * @param {Object} overrides An object containing methods to override/implement on the DD object
7458          * @return {Roo.dd.DD} The DD object
7459          */
7460         initDD : function(group, config, overrides){
7461             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7462             return Roo.apply(dd, overrides);
7463         },
7464
7465         /**
7466          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7467          * @param {String} group The group the DDProxy object is member of
7468          * @param {Object} config The DDProxy config object
7469          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7470          * @return {Roo.dd.DDProxy} The DDProxy object
7471          */
7472         initDDProxy : function(group, config, overrides){
7473             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7474             return Roo.apply(dd, overrides);
7475         },
7476
7477         /**
7478          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7479          * @param {String} group The group the DDTarget object is member of
7480          * @param {Object} config The DDTarget config object
7481          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7482          * @return {Roo.dd.DDTarget} The DDTarget object
7483          */
7484         initDDTarget : function(group, config, overrides){
7485             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7486             return Roo.apply(dd, overrides);
7487         },
7488
7489         /**
7490          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7491          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7492          * @param {Boolean} visible Whether the element is visible
7493          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7494          * @return {Roo.Element} this
7495          */
7496          setVisible : function(visible, animate){
7497             if(!animate || !A){
7498                 if(this.visibilityMode == El.DISPLAY){
7499                     this.setDisplayed(visible);
7500                 }else{
7501                     this.fixDisplay();
7502                     this.dom.style.visibility = visible ? "visible" : "hidden";
7503                 }
7504             }else{
7505                 // closure for composites
7506                 var dom = this.dom;
7507                 var visMode = this.visibilityMode;
7508                 if(visible){
7509                     this.setOpacity(.01);
7510                     this.setVisible(true);
7511                 }
7512                 this.anim({opacity: { to: (visible?1:0) }},
7513                       this.preanim(arguments, 1),
7514                       null, .35, 'easeIn', function(){
7515                          if(!visible){
7516                              if(visMode == El.DISPLAY){
7517                                  dom.style.display = "none";
7518                              }else{
7519                                  dom.style.visibility = "hidden";
7520                              }
7521                              Roo.get(dom).setOpacity(1);
7522                          }
7523                      });
7524             }
7525             return this;
7526         },
7527
7528         /**
7529          * Returns true if display is not "none"
7530          * @return {Boolean}
7531          */
7532         isDisplayed : function() {
7533             return this.getStyle("display") != "none";
7534         },
7535
7536         /**
7537          * Toggles the element's visibility or display, depending on visibility mode.
7538          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7539          * @return {Roo.Element} this
7540          */
7541         toggle : function(animate){
7542             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7543             return this;
7544         },
7545
7546         /**
7547          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7548          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7549          * @return {Roo.Element} this
7550          */
7551         setDisplayed : function(value) {
7552             if(typeof value == "boolean"){
7553                value = value ? this.originalDisplay : "none";
7554             }
7555             this.setStyle("display", value);
7556             return this;
7557         },
7558
7559         /**
7560          * Tries to focus the element. Any exceptions are caught and ignored.
7561          * @return {Roo.Element} this
7562          */
7563         focus : function() {
7564             try{
7565                 this.dom.focus();
7566             }catch(e){}
7567             return this;
7568         },
7569
7570         /**
7571          * Tries to blur the element. Any exceptions are caught and ignored.
7572          * @return {Roo.Element} this
7573          */
7574         blur : function() {
7575             try{
7576                 this.dom.blur();
7577             }catch(e){}
7578             return this;
7579         },
7580
7581         /**
7582          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7583          * @param {String/Array} className The CSS class to add, or an array of classes
7584          * @return {Roo.Element} this
7585          */
7586         addClass : function(className){
7587             if(className instanceof Array){
7588                 for(var i = 0, len = className.length; i < len; i++) {
7589                     this.addClass(className[i]);
7590                 }
7591             }else{
7592                 if(className && !this.hasClass(className)){
7593                     this.dom.className = this.dom.className + " " + className;
7594                 }
7595             }
7596             return this;
7597         },
7598
7599         /**
7600          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7601          * @param {String/Array} className The CSS class to add, or an array of classes
7602          * @return {Roo.Element} this
7603          */
7604         radioClass : function(className){
7605             var siblings = this.dom.parentNode.childNodes;
7606             for(var i = 0; i < siblings.length; i++) {
7607                 var s = siblings[i];
7608                 if(s.nodeType == 1){
7609                     Roo.get(s).removeClass(className);
7610                 }
7611             }
7612             this.addClass(className);
7613             return this;
7614         },
7615
7616         /**
7617          * Removes one or more CSS classes from the element.
7618          * @param {String/Array} className The CSS class to remove, or an array of classes
7619          * @return {Roo.Element} this
7620          */
7621         removeClass : function(className){
7622             if(!className || !this.dom.className){
7623                 return this;
7624             }
7625             if(className instanceof Array){
7626                 for(var i = 0, len = className.length; i < len; i++) {
7627                     this.removeClass(className[i]);
7628                 }
7629             }else{
7630                 if(this.hasClass(className)){
7631                     var re = this.classReCache[className];
7632                     if (!re) {
7633                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7634                        this.classReCache[className] = re;
7635                     }
7636                     this.dom.className =
7637                         this.dom.className.replace(re, " ");
7638                 }
7639             }
7640             return this;
7641         },
7642
7643         // private
7644         classReCache: {},
7645
7646         /**
7647          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7648          * @param {String} className The CSS class to toggle
7649          * @return {Roo.Element} this
7650          */
7651         toggleClass : function(className){
7652             if(this.hasClass(className)){
7653                 this.removeClass(className);
7654             }else{
7655                 this.addClass(className);
7656             }
7657             return this;
7658         },
7659
7660         /**
7661          * Checks if the specified CSS class exists on this element's DOM node.
7662          * @param {String} className The CSS class to check for
7663          * @return {Boolean} True if the class exists, else false
7664          */
7665         hasClass : function(className){
7666             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7667         },
7668
7669         /**
7670          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7671          * @param {String} oldClassName The CSS class to replace
7672          * @param {String} newClassName The replacement CSS class
7673          * @return {Roo.Element} this
7674          */
7675         replaceClass : function(oldClassName, newClassName){
7676             this.removeClass(oldClassName);
7677             this.addClass(newClassName);
7678             return this;
7679         },
7680
7681         /**
7682          * Returns an object with properties matching the styles requested.
7683          * For example, el.getStyles('color', 'font-size', 'width') might return
7684          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7685          * @param {String} style1 A style name
7686          * @param {String} style2 A style name
7687          * @param {String} etc.
7688          * @return {Object} The style object
7689          */
7690         getStyles : function(){
7691             var a = arguments, len = a.length, r = {};
7692             for(var i = 0; i < len; i++){
7693                 r[a[i]] = this.getStyle(a[i]);
7694             }
7695             return r;
7696         },
7697
7698         /**
7699          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7700          * @param {String} property The style property whose value is returned.
7701          * @return {String} The current value of the style property for this element.
7702          */
7703         getStyle : function(){
7704             return view && view.getComputedStyle ?
7705                 function(prop){
7706                     var el = this.dom, v, cs, camel;
7707                     if(prop == 'float'){
7708                         prop = "cssFloat";
7709                     }
7710                     if(el.style && (v = el.style[prop])){
7711                         return v;
7712                     }
7713                     if(cs = view.getComputedStyle(el, "")){
7714                         if(!(camel = propCache[prop])){
7715                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7716                         }
7717                         return cs[camel];
7718                     }
7719                     return null;
7720                 } :
7721                 function(prop){
7722                     var el = this.dom, v, cs, camel;
7723                     if(prop == 'opacity'){
7724                         if(typeof el.style.filter == 'string'){
7725                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7726                             if(m){
7727                                 var fv = parseFloat(m[1]);
7728                                 if(!isNaN(fv)){
7729                                     return fv ? fv / 100 : 0;
7730                                 }
7731                             }
7732                         }
7733                         return 1;
7734                     }else if(prop == 'float'){
7735                         prop = "styleFloat";
7736                     }
7737                     if(!(camel = propCache[prop])){
7738                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7739                     }
7740                     if(v = el.style[camel]){
7741                         return v;
7742                     }
7743                     if(cs = el.currentStyle){
7744                         return cs[camel];
7745                     }
7746                     return null;
7747                 };
7748         }(),
7749
7750         /**
7751          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7752          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7753          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7754          * @return {Roo.Element} this
7755          */
7756         setStyle : function(prop, value){
7757             if(typeof prop == "string"){
7758                 
7759                 if (prop == 'float') {
7760                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7761                     return this;
7762                 }
7763                 
7764                 var camel;
7765                 if(!(camel = propCache[prop])){
7766                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7767                 }
7768                 
7769                 if(camel == 'opacity') {
7770                     this.setOpacity(value);
7771                 }else{
7772                     this.dom.style[camel] = value;
7773                 }
7774             }else{
7775                 for(var style in prop){
7776                     if(typeof prop[style] != "function"){
7777                        this.setStyle(style, prop[style]);
7778                     }
7779                 }
7780             }
7781             return this;
7782         },
7783
7784         /**
7785          * More flexible version of {@link #setStyle} for setting style properties.
7786          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7787          * a function which returns such a specification.
7788          * @return {Roo.Element} this
7789          */
7790         applyStyles : function(style){
7791             Roo.DomHelper.applyStyles(this.dom, style);
7792             return this;
7793         },
7794
7795         /**
7796           * 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).
7797           * @return {Number} The X position of the element
7798           */
7799         getX : function(){
7800             return D.getX(this.dom);
7801         },
7802
7803         /**
7804           * 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).
7805           * @return {Number} The Y position of the element
7806           */
7807         getY : function(){
7808             return D.getY(this.dom);
7809         },
7810
7811         /**
7812           * 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).
7813           * @return {Array} The XY position of the element
7814           */
7815         getXY : function(){
7816             return D.getXY(this.dom);
7817         },
7818
7819         /**
7820          * 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).
7821          * @param {Number} The X position of the element
7822          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7823          * @return {Roo.Element} this
7824          */
7825         setX : function(x, animate){
7826             if(!animate || !A){
7827                 D.setX(this.dom, x);
7828             }else{
7829                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7830             }
7831             return this;
7832         },
7833
7834         /**
7835          * 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).
7836          * @param {Number} The Y position of the element
7837          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7838          * @return {Roo.Element} this
7839          */
7840         setY : function(y, animate){
7841             if(!animate || !A){
7842                 D.setY(this.dom, y);
7843             }else{
7844                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7845             }
7846             return this;
7847         },
7848
7849         /**
7850          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7851          * @param {String} left The left CSS property value
7852          * @return {Roo.Element} this
7853          */
7854         setLeft : function(left){
7855             this.setStyle("left", this.addUnits(left));
7856             return this;
7857         },
7858
7859         /**
7860          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7861          * @param {String} top The top CSS property value
7862          * @return {Roo.Element} this
7863          */
7864         setTop : function(top){
7865             this.setStyle("top", this.addUnits(top));
7866             return this;
7867         },
7868
7869         /**
7870          * Sets the element's CSS right style.
7871          * @param {String} right The right CSS property value
7872          * @return {Roo.Element} this
7873          */
7874         setRight : function(right){
7875             this.setStyle("right", this.addUnits(right));
7876             return this;
7877         },
7878
7879         /**
7880          * Sets the element's CSS bottom style.
7881          * @param {String} bottom The bottom CSS property value
7882          * @return {Roo.Element} this
7883          */
7884         setBottom : function(bottom){
7885             this.setStyle("bottom", this.addUnits(bottom));
7886             return this;
7887         },
7888
7889         /**
7890          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7891          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7892          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7893          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7894          * @return {Roo.Element} this
7895          */
7896         setXY : function(pos, animate){
7897             if(!animate || !A){
7898                 D.setXY(this.dom, pos);
7899             }else{
7900                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7901             }
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7907          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7908          * @param {Number} x X value for new position (coordinates are page-based)
7909          * @param {Number} y Y value for new position (coordinates are page-based)
7910          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7911          * @return {Roo.Element} this
7912          */
7913         setLocation : function(x, y, animate){
7914             this.setXY([x, y], this.preanim(arguments, 2));
7915             return this;
7916         },
7917
7918         /**
7919          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7920          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7921          * @param {Number} x X value for new position (coordinates are page-based)
7922          * @param {Number} y Y value for new position (coordinates are page-based)
7923          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7924          * @return {Roo.Element} this
7925          */
7926         moveTo : function(x, y, animate){
7927             this.setXY([x, y], this.preanim(arguments, 2));
7928             return this;
7929         },
7930
7931         /**
7932          * Returns the region of the given element.
7933          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7934          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7935          */
7936         getRegion : function(){
7937             return D.getRegion(this.dom);
7938         },
7939
7940         /**
7941          * Returns the offset height of the element
7942          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7943          * @return {Number} The element's height
7944          */
7945         getHeight : function(contentHeight){
7946             var h = this.dom.offsetHeight || 0;
7947             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7948         },
7949
7950         /**
7951          * Returns the offset width of the element
7952          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7953          * @return {Number} The element's width
7954          */
7955         getWidth : function(contentWidth){
7956             var w = this.dom.offsetWidth || 0;
7957             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7958         },
7959
7960         /**
7961          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7962          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7963          * if a height has not been set using CSS.
7964          * @return {Number}
7965          */
7966         getComputedHeight : function(){
7967             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7968             if(!h){
7969                 h = parseInt(this.getStyle('height'), 10) || 0;
7970                 if(!this.isBorderBox()){
7971                     h += this.getFrameWidth('tb');
7972                 }
7973             }
7974             return h;
7975         },
7976
7977         /**
7978          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7979          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7980          * if a width has not been set using CSS.
7981          * @return {Number}
7982          */
7983         getComputedWidth : function(){
7984             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7985             if(!w){
7986                 w = parseInt(this.getStyle('width'), 10) || 0;
7987                 if(!this.isBorderBox()){
7988                     w += this.getFrameWidth('lr');
7989                 }
7990             }
7991             return w;
7992         },
7993
7994         /**
7995          * Returns the size of the element.
7996          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7997          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7998          */
7999         getSize : function(contentSize){
8000             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8001         },
8002
8003         /**
8004          * Returns the width and height of the viewport.
8005          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8006          */
8007         getViewSize : function(){
8008             var d = this.dom, doc = document, aw = 0, ah = 0;
8009             if(d == doc || d == doc.body){
8010                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8011             }else{
8012                 return {
8013                     width : d.clientWidth,
8014                     height: d.clientHeight
8015                 };
8016             }
8017         },
8018
8019         /**
8020          * Returns the value of the "value" attribute
8021          * @param {Boolean} asNumber true to parse the value as a number
8022          * @return {String/Number}
8023          */
8024         getValue : function(asNumber){
8025             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8026         },
8027
8028         // private
8029         adjustWidth : function(width){
8030             if(typeof width == "number"){
8031                 if(this.autoBoxAdjust && !this.isBorderBox()){
8032                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8033                 }
8034                 if(width < 0){
8035                     width = 0;
8036                 }
8037             }
8038             return width;
8039         },
8040
8041         // private
8042         adjustHeight : function(height){
8043             if(typeof height == "number"){
8044                if(this.autoBoxAdjust && !this.isBorderBox()){
8045                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8046                }
8047                if(height < 0){
8048                    height = 0;
8049                }
8050             }
8051             return height;
8052         },
8053
8054         /**
8055          * Set the width of the element
8056          * @param {Number} width The new width
8057          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8058          * @return {Roo.Element} this
8059          */
8060         setWidth : function(width, animate){
8061             width = this.adjustWidth(width);
8062             if(!animate || !A){
8063                 this.dom.style.width = this.addUnits(width);
8064             }else{
8065                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8066             }
8067             return this;
8068         },
8069
8070         /**
8071          * Set the height of the element
8072          * @param {Number} height The new height
8073          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8074          * @return {Roo.Element} this
8075          */
8076          setHeight : function(height, animate){
8077             height = this.adjustHeight(height);
8078             if(!animate || !A){
8079                 this.dom.style.height = this.addUnits(height);
8080             }else{
8081                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8082             }
8083             return this;
8084         },
8085
8086         /**
8087          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8088          * @param {Number} width The new width
8089          * @param {Number} height The new height
8090          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8091          * @return {Roo.Element} this
8092          */
8093          setSize : function(width, height, animate){
8094             if(typeof width == "object"){ // in case of object from getSize()
8095                 height = width.height; width = width.width;
8096             }
8097             width = this.adjustWidth(width); height = this.adjustHeight(height);
8098             if(!animate || !A){
8099                 this.dom.style.width = this.addUnits(width);
8100                 this.dom.style.height = this.addUnits(height);
8101             }else{
8102                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8103             }
8104             return this;
8105         },
8106
8107         /**
8108          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8109          * @param {Number} x X value for new position (coordinates are page-based)
8110          * @param {Number} y Y value for new position (coordinates are page-based)
8111          * @param {Number} width The new width
8112          * @param {Number} height The new height
8113          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8114          * @return {Roo.Element} this
8115          */
8116         setBounds : function(x, y, width, height, animate){
8117             if(!animate || !A){
8118                 this.setSize(width, height);
8119                 this.setLocation(x, y);
8120             }else{
8121                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8122                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8123                               this.preanim(arguments, 4), 'motion');
8124             }
8125             return this;
8126         },
8127
8128         /**
8129          * 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.
8130          * @param {Roo.lib.Region} region The region to fill
8131          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8132          * @return {Roo.Element} this
8133          */
8134         setRegion : function(region, animate){
8135             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8136             return this;
8137         },
8138
8139         /**
8140          * Appends an event handler
8141          *
8142          * @param {String}   eventName     The type of event to append
8143          * @param {Function} fn        The method the event invokes
8144          * @param {Object} scope       (optional) The scope (this object) of the fn
8145          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8146          */
8147         addListener : function(eventName, fn, scope, options){
8148             if (this.dom) {
8149                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8150             }
8151         },
8152
8153         /**
8154          * Removes an event handler from this element
8155          * @param {String} eventName the type of event to remove
8156          * @param {Function} fn the method the event invokes
8157          * @return {Roo.Element} this
8158          */
8159         removeListener : function(eventName, fn){
8160             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8161             return this;
8162         },
8163
8164         /**
8165          * Removes all previous added listeners from this element
8166          * @return {Roo.Element} this
8167          */
8168         removeAllListeners : function(){
8169             E.purgeElement(this.dom);
8170             return this;
8171         },
8172
8173         relayEvent : function(eventName, observable){
8174             this.on(eventName, function(e){
8175                 observable.fireEvent(eventName, e);
8176             });
8177         },
8178
8179         /**
8180          * Set the opacity of the element
8181          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8182          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8183          * @return {Roo.Element} this
8184          */
8185          setOpacity : function(opacity, animate){
8186             if(!animate || !A){
8187                 var s = this.dom.style;
8188                 if(Roo.isIE){
8189                     s.zoom = 1;
8190                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8191                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8192                 }else{
8193                     s.opacity = opacity;
8194                 }
8195             }else{
8196                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8197             }
8198             return this;
8199         },
8200
8201         /**
8202          * Gets the left X coordinate
8203          * @param {Boolean} local True to get the local css position instead of page coordinate
8204          * @return {Number}
8205          */
8206         getLeft : function(local){
8207             if(!local){
8208                 return this.getX();
8209             }else{
8210                 return parseInt(this.getStyle("left"), 10) || 0;
8211             }
8212         },
8213
8214         /**
8215          * Gets the right X coordinate of the element (element X position + element width)
8216          * @param {Boolean} local True to get the local css position instead of page coordinate
8217          * @return {Number}
8218          */
8219         getRight : function(local){
8220             if(!local){
8221                 return this.getX() + this.getWidth();
8222             }else{
8223                 return (this.getLeft(true) + this.getWidth()) || 0;
8224             }
8225         },
8226
8227         /**
8228          * Gets the top Y coordinate
8229          * @param {Boolean} local True to get the local css position instead of page coordinate
8230          * @return {Number}
8231          */
8232         getTop : function(local) {
8233             if(!local){
8234                 return this.getY();
8235             }else{
8236                 return parseInt(this.getStyle("top"), 10) || 0;
8237             }
8238         },
8239
8240         /**
8241          * Gets the bottom Y coordinate of the element (element Y position + element height)
8242          * @param {Boolean} local True to get the local css position instead of page coordinate
8243          * @return {Number}
8244          */
8245         getBottom : function(local){
8246             if(!local){
8247                 return this.getY() + this.getHeight();
8248             }else{
8249                 return (this.getTop(true) + this.getHeight()) || 0;
8250             }
8251         },
8252
8253         /**
8254         * Initializes positioning on this element. If a desired position is not passed, it will make the
8255         * the element positioned relative IF it is not already positioned.
8256         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8257         * @param {Number} zIndex (optional) The zIndex to apply
8258         * @param {Number} x (optional) Set the page X position
8259         * @param {Number} y (optional) Set the page Y position
8260         */
8261         position : function(pos, zIndex, x, y){
8262             if(!pos){
8263                if(this.getStyle('position') == 'static'){
8264                    this.setStyle('position', 'relative');
8265                }
8266             }else{
8267                 this.setStyle("position", pos);
8268             }
8269             if(zIndex){
8270                 this.setStyle("z-index", zIndex);
8271             }
8272             if(x !== undefined && y !== undefined){
8273                 this.setXY([x, y]);
8274             }else if(x !== undefined){
8275                 this.setX(x);
8276             }else if(y !== undefined){
8277                 this.setY(y);
8278             }
8279         },
8280
8281         /**
8282         * Clear positioning back to the default when the document was loaded
8283         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8284         * @return {Roo.Element} this
8285          */
8286         clearPositioning : function(value){
8287             value = value ||'';
8288             this.setStyle({
8289                 "left": value,
8290                 "right": value,
8291                 "top": value,
8292                 "bottom": value,
8293                 "z-index": "",
8294                 "position" : "static"
8295             });
8296             return this;
8297         },
8298
8299         /**
8300         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8301         * snapshot before performing an update and then restoring the element.
8302         * @return {Object}
8303         */
8304         getPositioning : function(){
8305             var l = this.getStyle("left");
8306             var t = this.getStyle("top");
8307             return {
8308                 "position" : this.getStyle("position"),
8309                 "left" : l,
8310                 "right" : l ? "" : this.getStyle("right"),
8311                 "top" : t,
8312                 "bottom" : t ? "" : this.getStyle("bottom"),
8313                 "z-index" : this.getStyle("z-index")
8314             };
8315         },
8316
8317         /**
8318          * Gets the width of the border(s) for the specified side(s)
8319          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8320          * passing lr would get the border (l)eft width + the border (r)ight width.
8321          * @return {Number} The width of the sides passed added together
8322          */
8323         getBorderWidth : function(side){
8324             return this.addStyles(side, El.borders);
8325         },
8326
8327         /**
8328          * Gets the width of the padding(s) for the specified side(s)
8329          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8330          * passing lr would get the padding (l)eft + the padding (r)ight.
8331          * @return {Number} The padding of the sides passed added together
8332          */
8333         getPadding : function(side){
8334             return this.addStyles(side, El.paddings);
8335         },
8336
8337         /**
8338         * Set positioning with an object returned by getPositioning().
8339         * @param {Object} posCfg
8340         * @return {Roo.Element} this
8341          */
8342         setPositioning : function(pc){
8343             this.applyStyles(pc);
8344             if(pc.right == "auto"){
8345                 this.dom.style.right = "";
8346             }
8347             if(pc.bottom == "auto"){
8348                 this.dom.style.bottom = "";
8349             }
8350             return this;
8351         },
8352
8353         // private
8354         fixDisplay : function(){
8355             if(this.getStyle("display") == "none"){
8356                 this.setStyle("visibility", "hidden");
8357                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8358                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8359                     this.setStyle("display", "block");
8360                 }
8361             }
8362         },
8363
8364         /**
8365          * Quick set left and top adding default units
8366          * @param {String} left The left CSS property value
8367          * @param {String} top The top CSS property value
8368          * @return {Roo.Element} this
8369          */
8370          setLeftTop : function(left, top){
8371             this.dom.style.left = this.addUnits(left);
8372             this.dom.style.top = this.addUnits(top);
8373             return this;
8374         },
8375
8376         /**
8377          * Move this element relative to its current position.
8378          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8379          * @param {Number} distance How far to move the element in pixels
8380          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8381          * @return {Roo.Element} this
8382          */
8383          move : function(direction, distance, animate){
8384             var xy = this.getXY();
8385             direction = direction.toLowerCase();
8386             switch(direction){
8387                 case "l":
8388                 case "left":
8389                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8390                     break;
8391                case "r":
8392                case "right":
8393                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8394                     break;
8395                case "t":
8396                case "top":
8397                case "up":
8398                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8399                     break;
8400                case "b":
8401                case "bottom":
8402                case "down":
8403                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8404                     break;
8405             }
8406             return this;
8407         },
8408
8409         /**
8410          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8411          * @return {Roo.Element} this
8412          */
8413         clip : function(){
8414             if(!this.isClipped){
8415                this.isClipped = true;
8416                this.originalClip = {
8417                    "o": this.getStyle("overflow"),
8418                    "x": this.getStyle("overflow-x"),
8419                    "y": this.getStyle("overflow-y")
8420                };
8421                this.setStyle("overflow", "hidden");
8422                this.setStyle("overflow-x", "hidden");
8423                this.setStyle("overflow-y", "hidden");
8424             }
8425             return this;
8426         },
8427
8428         /**
8429          *  Return clipping (overflow) to original clipping before clip() was called
8430          * @return {Roo.Element} this
8431          */
8432         unclip : function(){
8433             if(this.isClipped){
8434                 this.isClipped = false;
8435                 var o = this.originalClip;
8436                 if(o.o){this.setStyle("overflow", o.o);}
8437                 if(o.x){this.setStyle("overflow-x", o.x);}
8438                 if(o.y){this.setStyle("overflow-y", o.y);}
8439             }
8440             return this;
8441         },
8442
8443
8444         /**
8445          * Gets the x,y coordinates specified by the anchor position on the element.
8446          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8447          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8448          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8449          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8450          * @return {Array} [x, y] An array containing the element's x and y coordinates
8451          */
8452         getAnchorXY : function(anchor, local, s){
8453             //Passing a different size is useful for pre-calculating anchors,
8454             //especially for anchored animations that change the el size.
8455
8456             var w, h, vp = false;
8457             if(!s){
8458                 var d = this.dom;
8459                 if(d == document.body || d == document){
8460                     vp = true;
8461                     w = D.getViewWidth(); h = D.getViewHeight();
8462                 }else{
8463                     w = this.getWidth(); h = this.getHeight();
8464                 }
8465             }else{
8466                 w = s.width;  h = s.height;
8467             }
8468             var x = 0, y = 0, r = Math.round;
8469             switch((anchor || "tl").toLowerCase()){
8470                 case "c":
8471                     x = r(w*.5);
8472                     y = r(h*.5);
8473                 break;
8474                 case "t":
8475                     x = r(w*.5);
8476                     y = 0;
8477                 break;
8478                 case "l":
8479                     x = 0;
8480                     y = r(h*.5);
8481                 break;
8482                 case "r":
8483                     x = w;
8484                     y = r(h*.5);
8485                 break;
8486                 case "b":
8487                     x = r(w*.5);
8488                     y = h;
8489                 break;
8490                 case "tl":
8491                     x = 0;
8492                     y = 0;
8493                 break;
8494                 case "bl":
8495                     x = 0;
8496                     y = h;
8497                 break;
8498                 case "br":
8499                     x = w;
8500                     y = h;
8501                 break;
8502                 case "tr":
8503                     x = w;
8504                     y = 0;
8505                 break;
8506             }
8507             if(local === true){
8508                 return [x, y];
8509             }
8510             if(vp){
8511                 var sc = this.getScroll();
8512                 return [x + sc.left, y + sc.top];
8513             }
8514             //Add the element's offset xy
8515             var o = this.getXY();
8516             return [x+o[0], y+o[1]];
8517         },
8518
8519         /**
8520          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8521          * supported position values.
8522          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8523          * @param {String} position The position to align to.
8524          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8525          * @return {Array} [x, y]
8526          */
8527         getAlignToXY : function(el, p, o){
8528             el = Roo.get(el);
8529             var d = this.dom;
8530             if(!el.dom){
8531                 throw "Element.alignTo with an element that doesn't exist";
8532             }
8533             var c = false; //constrain to viewport
8534             var p1 = "", p2 = "";
8535             o = o || [0,0];
8536
8537             if(!p){
8538                 p = "tl-bl";
8539             }else if(p == "?"){
8540                 p = "tl-bl?";
8541             }else if(p.indexOf("-") == -1){
8542                 p = "tl-" + p;
8543             }
8544             p = p.toLowerCase();
8545             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8546             if(!m){
8547                throw "Element.alignTo with an invalid alignment " + p;
8548             }
8549             p1 = m[1]; p2 = m[2]; c = !!m[3];
8550
8551             //Subtract the aligned el's internal xy from the target's offset xy
8552             //plus custom offset to get the aligned el's new offset xy
8553             var a1 = this.getAnchorXY(p1, true);
8554             var a2 = el.getAnchorXY(p2, false);
8555             var x = a2[0] - a1[0] + o[0];
8556             var y = a2[1] - a1[1] + o[1];
8557             if(c){
8558                 //constrain the aligned el to viewport if necessary
8559                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8560                 // 5px of margin for ie
8561                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8562
8563                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8564                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8565                 //otherwise swap the aligned el to the opposite border of the target.
8566                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8567                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8568                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8569                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8570
8571                var doc = document;
8572                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8573                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8574
8575                if((x+w) > dw + scrollX){
8576                     x = swapX ? r.left-w : dw+scrollX-w;
8577                 }
8578                if(x < scrollX){
8579                    x = swapX ? r.right : scrollX;
8580                }
8581                if((y+h) > dh + scrollY){
8582                     y = swapY ? r.top-h : dh+scrollY-h;
8583                 }
8584                if (y < scrollY){
8585                    y = swapY ? r.bottom : scrollY;
8586                }
8587             }
8588             return [x,y];
8589         },
8590
8591         // private
8592         getConstrainToXY : function(){
8593             var os = {top:0, left:0, bottom:0, right: 0};
8594
8595             return function(el, local, offsets, proposedXY){
8596                 el = Roo.get(el);
8597                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8598
8599                 var vw, vh, vx = 0, vy = 0;
8600                 if(el.dom == document.body || el.dom == document){
8601                     vw = Roo.lib.Dom.getViewWidth();
8602                     vh = Roo.lib.Dom.getViewHeight();
8603                 }else{
8604                     vw = el.dom.clientWidth;
8605                     vh = el.dom.clientHeight;
8606                     if(!local){
8607                         var vxy = el.getXY();
8608                         vx = vxy[0];
8609                         vy = vxy[1];
8610                     }
8611                 }
8612
8613                 var s = el.getScroll();
8614
8615                 vx += offsets.left + s.left;
8616                 vy += offsets.top + s.top;
8617
8618                 vw -= offsets.right;
8619                 vh -= offsets.bottom;
8620
8621                 var vr = vx+vw;
8622                 var vb = vy+vh;
8623
8624                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8625                 var x = xy[0], y = xy[1];
8626                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8627
8628                 // only move it if it needs it
8629                 var moved = false;
8630
8631                 // first validate right/bottom
8632                 if((x + w) > vr){
8633                     x = vr - w;
8634                     moved = true;
8635                 }
8636                 if((y + h) > vb){
8637                     y = vb - h;
8638                     moved = true;
8639                 }
8640                 // then make sure top/left isn't negative
8641                 if(x < vx){
8642                     x = vx;
8643                     moved = true;
8644                 }
8645                 if(y < vy){
8646                     y = vy;
8647                     moved = true;
8648                 }
8649                 return moved ? [x, y] : false;
8650             };
8651         }(),
8652
8653         // private
8654         adjustForConstraints : function(xy, parent, offsets){
8655             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8656         },
8657
8658         /**
8659          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8660          * document it aligns it to the viewport.
8661          * The position parameter is optional, and can be specified in any one of the following formats:
8662          * <ul>
8663          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8664          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8665          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8666          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8667          *   <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
8668          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8669          * </ul>
8670          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8671          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8672          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8673          * that specified in order to enforce the viewport constraints.
8674          * Following are all of the supported anchor positions:
8675     <pre>
8676     Value  Description
8677     -----  -----------------------------
8678     tl     The top left corner (default)
8679     t      The center of the top edge
8680     tr     The top right corner
8681     l      The center of the left edge
8682     c      In the center of the element
8683     r      The center of the right edge
8684     bl     The bottom left corner
8685     b      The center of the bottom edge
8686     br     The bottom right corner
8687     </pre>
8688     Example Usage:
8689     <pre><code>
8690     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8691     el.alignTo("other-el");
8692
8693     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8694     el.alignTo("other-el", "tr?");
8695
8696     // align the bottom right corner of el with the center left edge of other-el
8697     el.alignTo("other-el", "br-l?");
8698
8699     // align the center of el with the bottom left corner of other-el and
8700     // adjust the x position by -6 pixels (and the y position by 0)
8701     el.alignTo("other-el", "c-bl", [-6, 0]);
8702     </code></pre>
8703          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8704          * @param {String} position The position to align to.
8705          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8706          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8707          * @return {Roo.Element} this
8708          */
8709         alignTo : function(element, position, offsets, animate){
8710             var xy = this.getAlignToXY(element, position, offsets);
8711             this.setXY(xy, this.preanim(arguments, 3));
8712             return this;
8713         },
8714
8715         /**
8716          * Anchors an element to another element and realigns it when the window is resized.
8717          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8718          * @param {String} position The position to align to.
8719          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8720          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8721          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8722          * is a number, it is used as the buffer delay (defaults to 50ms).
8723          * @param {Function} callback The function to call after the animation finishes
8724          * @return {Roo.Element} this
8725          */
8726         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8727             var action = function(){
8728                 this.alignTo(el, alignment, offsets, animate);
8729                 Roo.callback(callback, this);
8730             };
8731             Roo.EventManager.onWindowResize(action, this);
8732             var tm = typeof monitorScroll;
8733             if(tm != 'undefined'){
8734                 Roo.EventManager.on(window, 'scroll', action, this,
8735                     {buffer: tm == 'number' ? monitorScroll : 50});
8736             }
8737             action.call(this); // align immediately
8738             return this;
8739         },
8740         /**
8741          * Clears any opacity settings from this element. Required in some cases for IE.
8742          * @return {Roo.Element} this
8743          */
8744         clearOpacity : function(){
8745             if (window.ActiveXObject) {
8746                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8747                     this.dom.style.filter = "";
8748                 }
8749             } else {
8750                 this.dom.style.opacity = "";
8751                 this.dom.style["-moz-opacity"] = "";
8752                 this.dom.style["-khtml-opacity"] = "";
8753             }
8754             return this;
8755         },
8756
8757         /**
8758          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8759          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8760          * @return {Roo.Element} this
8761          */
8762         hide : function(animate){
8763             this.setVisible(false, this.preanim(arguments, 0));
8764             return this;
8765         },
8766
8767         /**
8768         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8769         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8770          * @return {Roo.Element} this
8771          */
8772         show : function(animate){
8773             this.setVisible(true, this.preanim(arguments, 0));
8774             return this;
8775         },
8776
8777         /**
8778          * @private Test if size has a unit, otherwise appends the default
8779          */
8780         addUnits : function(size){
8781             return Roo.Element.addUnits(size, this.defaultUnit);
8782         },
8783
8784         /**
8785          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8786          * @return {Roo.Element} this
8787          */
8788         beginMeasure : function(){
8789             var el = this.dom;
8790             if(el.offsetWidth || el.offsetHeight){
8791                 return this; // offsets work already
8792             }
8793             var changed = [];
8794             var p = this.dom, b = document.body; // start with this element
8795             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8796                 var pe = Roo.get(p);
8797                 if(pe.getStyle('display') == 'none'){
8798                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8799                     p.style.visibility = "hidden";
8800                     p.style.display = "block";
8801                 }
8802                 p = p.parentNode;
8803             }
8804             this._measureChanged = changed;
8805             return this;
8806
8807         },
8808
8809         /**
8810          * Restores displays to before beginMeasure was called
8811          * @return {Roo.Element} this
8812          */
8813         endMeasure : function(){
8814             var changed = this._measureChanged;
8815             if(changed){
8816                 for(var i = 0, len = changed.length; i < len; i++) {
8817                     var r = changed[i];
8818                     r.el.style.visibility = r.visibility;
8819                     r.el.style.display = "none";
8820                 }
8821                 this._measureChanged = null;
8822             }
8823             return this;
8824         },
8825
8826         /**
8827         * Update the innerHTML of this element, optionally searching for and processing scripts
8828         * @param {String} html The new HTML
8829         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8830         * @param {Function} callback For async script loading you can be noticed when the update completes
8831         * @return {Roo.Element} this
8832          */
8833         update : function(html, loadScripts, callback){
8834             if(typeof html == "undefined"){
8835                 html = "";
8836             }
8837             if(loadScripts !== true){
8838                 this.dom.innerHTML = html;
8839                 if(typeof callback == "function"){
8840                     callback();
8841                 }
8842                 return this;
8843             }
8844             var id = Roo.id();
8845             var dom = this.dom;
8846
8847             html += '<span id="' + id + '"></span>';
8848
8849             E.onAvailable(id, function(){
8850                 var hd = document.getElementsByTagName("head")[0];
8851                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8852                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8853                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8854
8855                 var match;
8856                 while(match = re.exec(html)){
8857                     var attrs = match[1];
8858                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8859                     if(srcMatch && srcMatch[2]){
8860                        var s = document.createElement("script");
8861                        s.src = srcMatch[2];
8862                        var typeMatch = attrs.match(typeRe);
8863                        if(typeMatch && typeMatch[2]){
8864                            s.type = typeMatch[2];
8865                        }
8866                        hd.appendChild(s);
8867                     }else if(match[2] && match[2].length > 0){
8868                         if(window.execScript) {
8869                            window.execScript(match[2]);
8870                         } else {
8871                             /**
8872                              * eval:var:id
8873                              * eval:var:dom
8874                              * eval:var:html
8875                              * 
8876                              */
8877                            window.eval(match[2]);
8878                         }
8879                     }
8880                 }
8881                 var el = document.getElementById(id);
8882                 if(el){el.parentNode.removeChild(el);}
8883                 if(typeof callback == "function"){
8884                     callback();
8885                 }
8886             });
8887             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8888             return this;
8889         },
8890
8891         /**
8892          * Direct access to the UpdateManager update() method (takes the same parameters).
8893          * @param {String/Function} url The url for this request or a function to call to get the url
8894          * @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}
8895          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8896          * @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.
8897          * @return {Roo.Element} this
8898          */
8899         load : function(){
8900             var um = this.getUpdateManager();
8901             um.update.apply(um, arguments);
8902             return this;
8903         },
8904
8905         /**
8906         * Gets this element's UpdateManager
8907         * @return {Roo.UpdateManager} The UpdateManager
8908         */
8909         getUpdateManager : function(){
8910             if(!this.updateManager){
8911                 this.updateManager = new Roo.UpdateManager(this);
8912             }
8913             return this.updateManager;
8914         },
8915
8916         /**
8917          * Disables text selection for this element (normalized across browsers)
8918          * @return {Roo.Element} this
8919          */
8920         unselectable : function(){
8921             this.dom.unselectable = "on";
8922             this.swallowEvent("selectstart", true);
8923             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8924             this.addClass("x-unselectable");
8925             return this;
8926         },
8927
8928         /**
8929         * Calculates the x, y to center this element on the screen
8930         * @return {Array} The x, y values [x, y]
8931         */
8932         getCenterXY : function(){
8933             return this.getAlignToXY(document, 'c-c');
8934         },
8935
8936         /**
8937         * Centers the Element in either the viewport, or another Element.
8938         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8939         */
8940         center : function(centerIn){
8941             this.alignTo(centerIn || document, 'c-c');
8942             return this;
8943         },
8944
8945         /**
8946          * Tests various css rules/browsers to determine if this element uses a border box
8947          * @return {Boolean}
8948          */
8949         isBorderBox : function(){
8950             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8951         },
8952
8953         /**
8954          * Return a box {x, y, width, height} that can be used to set another elements
8955          * size/location to match this element.
8956          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8957          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8958          * @return {Object} box An object in the format {x, y, width, height}
8959          */
8960         getBox : function(contentBox, local){
8961             var xy;
8962             if(!local){
8963                 xy = this.getXY();
8964             }else{
8965                 var left = parseInt(this.getStyle("left"), 10) || 0;
8966                 var top = parseInt(this.getStyle("top"), 10) || 0;
8967                 xy = [left, top];
8968             }
8969             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8970             if(!contentBox){
8971                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8972             }else{
8973                 var l = this.getBorderWidth("l")+this.getPadding("l");
8974                 var r = this.getBorderWidth("r")+this.getPadding("r");
8975                 var t = this.getBorderWidth("t")+this.getPadding("t");
8976                 var b = this.getBorderWidth("b")+this.getPadding("b");
8977                 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)};
8978             }
8979             bx.right = bx.x + bx.width;
8980             bx.bottom = bx.y + bx.height;
8981             return bx;
8982         },
8983
8984         /**
8985          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8986          for more information about the sides.
8987          * @param {String} sides
8988          * @return {Number}
8989          */
8990         getFrameWidth : function(sides, onlyContentBox){
8991             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8992         },
8993
8994         /**
8995          * 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.
8996          * @param {Object} box The box to fill {x, y, width, height}
8997          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8998          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8999          * @return {Roo.Element} this
9000          */
9001         setBox : function(box, adjust, animate){
9002             var w = box.width, h = box.height;
9003             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9004                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9005                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9006             }
9007             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9008             return this;
9009         },
9010
9011         /**
9012          * Forces the browser to repaint this element
9013          * @return {Roo.Element} this
9014          */
9015          repaint : function(){
9016             var dom = this.dom;
9017             this.addClass("x-repaint");
9018             setTimeout(function(){
9019                 Roo.get(dom).removeClass("x-repaint");
9020             }, 1);
9021             return this;
9022         },
9023
9024         /**
9025          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9026          * then it returns the calculated width of the sides (see getPadding)
9027          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9028          * @return {Object/Number}
9029          */
9030         getMargins : function(side){
9031             if(!side){
9032                 return {
9033                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9034                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9035                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9036                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9037                 };
9038             }else{
9039                 return this.addStyles(side, El.margins);
9040              }
9041         },
9042
9043         // private
9044         addStyles : function(sides, styles){
9045             var val = 0, v, w;
9046             for(var i = 0, len = sides.length; i < len; i++){
9047                 v = this.getStyle(styles[sides.charAt(i)]);
9048                 if(v){
9049                      w = parseInt(v, 10);
9050                      if(w){ val += w; }
9051                 }
9052             }
9053             return val;
9054         },
9055
9056         /**
9057          * Creates a proxy element of this element
9058          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9059          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9060          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9061          * @return {Roo.Element} The new proxy element
9062          */
9063         createProxy : function(config, renderTo, matchBox){
9064             if(renderTo){
9065                 renderTo = Roo.getDom(renderTo);
9066             }else{
9067                 renderTo = document.body;
9068             }
9069             config = typeof config == "object" ?
9070                 config : {tag : "div", cls: config};
9071             var proxy = Roo.DomHelper.append(renderTo, config, true);
9072             if(matchBox){
9073                proxy.setBox(this.getBox());
9074             }
9075             return proxy;
9076         },
9077
9078         /**
9079          * Puts a mask over this element to disable user interaction. Requires core.css.
9080          * This method can only be applied to elements which accept child nodes.
9081          * @param {String} msg (optional) A message to display in the mask
9082          * @param {String} msgCls (optional) A css class to apply to the msg element
9083          * @return {Element} The mask  element
9084          */
9085         mask : function(msg, msgCls)
9086         {
9087             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9088                 this.setStyle("position", "relative");
9089             }
9090             if(!this._mask){
9091                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9092             }
9093             this.addClass("x-masked");
9094             this._mask.setDisplayed(true);
9095             
9096             // we wander
9097             var z = 0;
9098             var dom = this.dom;
9099             while (dom && dom.style) {
9100                 if (!isNaN(parseInt(dom.style.zIndex))) {
9101                     z = Math.max(z, parseInt(dom.style.zIndex));
9102                 }
9103                 dom = dom.parentNode;
9104             }
9105             // if we are masking the body - then it hides everything..
9106             if (this.dom == document.body) {
9107                 z = 1000000;
9108                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9109                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9110             }
9111            
9112             if(typeof msg == 'string'){
9113                 if(!this._maskMsg){
9114                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9115                 }
9116                 var mm = this._maskMsg;
9117                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9118                 if (mm.dom.firstChild) { // weird IE issue?
9119                     mm.dom.firstChild.innerHTML = msg;
9120                 }
9121                 mm.setDisplayed(true);
9122                 mm.center(this);
9123                 mm.setStyle('z-index', z + 102);
9124             }
9125             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9126                 this._mask.setHeight(this.getHeight());
9127             }
9128             this._mask.setStyle('z-index', z + 100);
9129             
9130             return this._mask;
9131         },
9132
9133         /**
9134          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9135          * it is cached for reuse.
9136          */
9137         unmask : function(removeEl){
9138             if(this._mask){
9139                 if(removeEl === true){
9140                     this._mask.remove();
9141                     delete this._mask;
9142                     if(this._maskMsg){
9143                         this._maskMsg.remove();
9144                         delete this._maskMsg;
9145                     }
9146                 }else{
9147                     this._mask.setDisplayed(false);
9148                     if(this._maskMsg){
9149                         this._maskMsg.setDisplayed(false);
9150                     }
9151                 }
9152             }
9153             this.removeClass("x-masked");
9154         },
9155
9156         /**
9157          * Returns true if this element is masked
9158          * @return {Boolean}
9159          */
9160         isMasked : function(){
9161             return this._mask && this._mask.isVisible();
9162         },
9163
9164         /**
9165          * Creates an iframe shim for this element to keep selects and other windowed objects from
9166          * showing through.
9167          * @return {Roo.Element} The new shim element
9168          */
9169         createShim : function(){
9170             var el = document.createElement('iframe');
9171             el.frameBorder = 'no';
9172             el.className = 'roo-shim';
9173             if(Roo.isIE && Roo.isSecure){
9174                 el.src = Roo.SSL_SECURE_URL;
9175             }
9176             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9177             shim.autoBoxAdjust = false;
9178             return shim;
9179         },
9180
9181         /**
9182          * Removes this element from the DOM and deletes it from the cache
9183          */
9184         remove : function(){
9185             if(this.dom.parentNode){
9186                 this.dom.parentNode.removeChild(this.dom);
9187             }
9188             delete El.cache[this.dom.id];
9189         },
9190
9191         /**
9192          * Sets up event handlers to add and remove a css class when the mouse is over this element
9193          * @param {String} className
9194          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9195          * mouseout events for children elements
9196          * @return {Roo.Element} this
9197          */
9198         addClassOnOver : function(className, preventFlicker){
9199             this.on("mouseover", function(){
9200                 Roo.fly(this, '_internal').addClass(className);
9201             }, this.dom);
9202             var removeFn = function(e){
9203                 if(preventFlicker !== true || !e.within(this, true)){
9204                     Roo.fly(this, '_internal').removeClass(className);
9205                 }
9206             };
9207             this.on("mouseout", removeFn, this.dom);
9208             return this;
9209         },
9210
9211         /**
9212          * Sets up event handlers to add and remove a css class when this element has the focus
9213          * @param {String} className
9214          * @return {Roo.Element} this
9215          */
9216         addClassOnFocus : function(className){
9217             this.on("focus", function(){
9218                 Roo.fly(this, '_internal').addClass(className);
9219             }, this.dom);
9220             this.on("blur", function(){
9221                 Roo.fly(this, '_internal').removeClass(className);
9222             }, this.dom);
9223             return this;
9224         },
9225         /**
9226          * 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)
9227          * @param {String} className
9228          * @return {Roo.Element} this
9229          */
9230         addClassOnClick : function(className){
9231             var dom = this.dom;
9232             this.on("mousedown", function(){
9233                 Roo.fly(dom, '_internal').addClass(className);
9234                 var d = Roo.get(document);
9235                 var fn = function(){
9236                     Roo.fly(dom, '_internal').removeClass(className);
9237                     d.removeListener("mouseup", fn);
9238                 };
9239                 d.on("mouseup", fn);
9240             });
9241             return this;
9242         },
9243
9244         /**
9245          * Stops the specified event from bubbling and optionally prevents the default action
9246          * @param {String} eventName
9247          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9248          * @return {Roo.Element} this
9249          */
9250         swallowEvent : function(eventName, preventDefault){
9251             var fn = function(e){
9252                 e.stopPropagation();
9253                 if(preventDefault){
9254                     e.preventDefault();
9255                 }
9256             };
9257             if(eventName instanceof Array){
9258                 for(var i = 0, len = eventName.length; i < len; i++){
9259                      this.on(eventName[i], fn);
9260                 }
9261                 return this;
9262             }
9263             this.on(eventName, fn);
9264             return this;
9265         },
9266
9267         /**
9268          * @private
9269          */
9270       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9271
9272         /**
9273          * Sizes this element to its parent element's dimensions performing
9274          * neccessary box adjustments.
9275          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9276          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9277          * @return {Roo.Element} this
9278          */
9279         fitToParent : function(monitorResize, targetParent) {
9280           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9281           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9282           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9283             return;
9284           }
9285           var p = Roo.get(targetParent || this.dom.parentNode);
9286           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9287           if (monitorResize === true) {
9288             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9289             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9290           }
9291           return this;
9292         },
9293
9294         /**
9295          * Gets the next sibling, skipping text nodes
9296          * @return {HTMLElement} The next sibling or null
9297          */
9298         getNextSibling : function(){
9299             var n = this.dom.nextSibling;
9300             while(n && n.nodeType != 1){
9301                 n = n.nextSibling;
9302             }
9303             return n;
9304         },
9305
9306         /**
9307          * Gets the previous sibling, skipping text nodes
9308          * @return {HTMLElement} The previous sibling or null
9309          */
9310         getPrevSibling : function(){
9311             var n = this.dom.previousSibling;
9312             while(n && n.nodeType != 1){
9313                 n = n.previousSibling;
9314             }
9315             return n;
9316         },
9317
9318
9319         /**
9320          * Appends the passed element(s) to this element
9321          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9322          * @return {Roo.Element} this
9323          */
9324         appendChild: function(el){
9325             el = Roo.get(el);
9326             el.appendTo(this);
9327             return this;
9328         },
9329
9330         /**
9331          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9332          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9333          * automatically generated with the specified attributes.
9334          * @param {HTMLElement} insertBefore (optional) a child element of this element
9335          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9336          * @return {Roo.Element} The new child element
9337          */
9338         createChild: function(config, insertBefore, returnDom){
9339             config = config || {tag:'div'};
9340             if(insertBefore){
9341                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9342             }
9343             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9344         },
9345
9346         /**
9347          * Appends this element to the passed element
9348          * @param {String/HTMLElement/Element} el The new parent element
9349          * @return {Roo.Element} this
9350          */
9351         appendTo: function(el){
9352             el = Roo.getDom(el);
9353             el.appendChild(this.dom);
9354             return this;
9355         },
9356
9357         /**
9358          * Inserts this element before the passed element in the DOM
9359          * @param {String/HTMLElement/Element} el The element to insert before
9360          * @return {Roo.Element} this
9361          */
9362         insertBefore: function(el){
9363             el = Roo.getDom(el);
9364             el.parentNode.insertBefore(this.dom, el);
9365             return this;
9366         },
9367
9368         /**
9369          * Inserts this element after the passed element in the DOM
9370          * @param {String/HTMLElement/Element} el The element to insert after
9371          * @return {Roo.Element} this
9372          */
9373         insertAfter: function(el){
9374             el = Roo.getDom(el);
9375             el.parentNode.insertBefore(this.dom, el.nextSibling);
9376             return this;
9377         },
9378
9379         /**
9380          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9381          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9382          * @return {Roo.Element} The new child
9383          */
9384         insertFirst: function(el, returnDom){
9385             el = el || {};
9386             if(typeof el == 'object' && !el.nodeType){ // dh config
9387                 return this.createChild(el, this.dom.firstChild, returnDom);
9388             }else{
9389                 el = Roo.getDom(el);
9390                 this.dom.insertBefore(el, this.dom.firstChild);
9391                 return !returnDom ? Roo.get(el) : el;
9392             }
9393         },
9394
9395         /**
9396          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9397          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9398          * @param {String} where (optional) 'before' or 'after' defaults to before
9399          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9400          * @return {Roo.Element} the inserted Element
9401          */
9402         insertSibling: function(el, where, returnDom){
9403             where = where ? where.toLowerCase() : 'before';
9404             el = el || {};
9405             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9406
9407             if(typeof el == 'object' && !el.nodeType){ // dh config
9408                 if(where == 'after' && !this.dom.nextSibling){
9409                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9410                 }else{
9411                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9412                 }
9413
9414             }else{
9415                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9416                             where == 'before' ? this.dom : this.dom.nextSibling);
9417                 if(!returnDom){
9418                     rt = Roo.get(rt);
9419                 }
9420             }
9421             return rt;
9422         },
9423
9424         /**
9425          * Creates and wraps this element with another element
9426          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9427          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9428          * @return {HTMLElement/Element} The newly created wrapper element
9429          */
9430         wrap: function(config, returnDom){
9431             if(!config){
9432                 config = {tag: "div"};
9433             }
9434             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9435             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9436             return newEl;
9437         },
9438
9439         /**
9440          * Replaces the passed element with this element
9441          * @param {String/HTMLElement/Element} el The element to replace
9442          * @return {Roo.Element} this
9443          */
9444         replace: function(el){
9445             el = Roo.get(el);
9446             this.insertBefore(el);
9447             el.remove();
9448             return this;
9449         },
9450
9451         /**
9452          * Inserts an html fragment into this element
9453          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9454          * @param {String} html The HTML fragment
9455          * @param {Boolean} returnEl True to return an Roo.Element
9456          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9457          */
9458         insertHtml : function(where, html, returnEl){
9459             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9460             return returnEl ? Roo.get(el) : el;
9461         },
9462
9463         /**
9464          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9465          * @param {Object} o The object with the attributes
9466          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9467          * @return {Roo.Element} this
9468          */
9469         set : function(o, useSet){
9470             var el = this.dom;
9471             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9472             for(var attr in o){
9473                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9474                 if(attr=="cls"){
9475                     el.className = o["cls"];
9476                 }else{
9477                     if(useSet) {
9478                         el.setAttribute(attr, o[attr]);
9479                     } else {
9480                         el[attr] = o[attr];
9481                     }
9482                 }
9483             }
9484             if(o.style){
9485                 Roo.DomHelper.applyStyles(el, o.style);
9486             }
9487             return this;
9488         },
9489
9490         /**
9491          * Convenience method for constructing a KeyMap
9492          * @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:
9493          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9494          * @param {Function} fn The function to call
9495          * @param {Object} scope (optional) The scope of the function
9496          * @return {Roo.KeyMap} The KeyMap created
9497          */
9498         addKeyListener : function(key, fn, scope){
9499             var config;
9500             if(typeof key != "object" || key instanceof Array){
9501                 config = {
9502                     key: key,
9503                     fn: fn,
9504                     scope: scope
9505                 };
9506             }else{
9507                 config = {
9508                     key : key.key,
9509                     shift : key.shift,
9510                     ctrl : key.ctrl,
9511                     alt : key.alt,
9512                     fn: fn,
9513                     scope: scope
9514                 };
9515             }
9516             return new Roo.KeyMap(this, config);
9517         },
9518
9519         /**
9520          * Creates a KeyMap for this element
9521          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9522          * @return {Roo.KeyMap} The KeyMap created
9523          */
9524         addKeyMap : function(config){
9525             return new Roo.KeyMap(this, config);
9526         },
9527
9528         /**
9529          * Returns true if this element is scrollable.
9530          * @return {Boolean}
9531          */
9532          isScrollable : function(){
9533             var dom = this.dom;
9534             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9535         },
9536
9537         /**
9538          * 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().
9539          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9540          * @param {Number} value The new scroll value
9541          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9542          * @return {Element} this
9543          */
9544
9545         scrollTo : function(side, value, animate){
9546             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9547             if(!animate || !A){
9548                 this.dom[prop] = value;
9549             }else{
9550                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9551                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9552             }
9553             return this;
9554         },
9555
9556         /**
9557          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9558          * within this element's scrollable range.
9559          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9560          * @param {Number} distance How far to scroll the element in pixels
9561          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9562          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9563          * was scrolled as far as it could go.
9564          */
9565          scroll : function(direction, distance, animate){
9566              if(!this.isScrollable()){
9567                  return;
9568              }
9569              var el = this.dom;
9570              var l = el.scrollLeft, t = el.scrollTop;
9571              var w = el.scrollWidth, h = el.scrollHeight;
9572              var cw = el.clientWidth, ch = el.clientHeight;
9573              direction = direction.toLowerCase();
9574              var scrolled = false;
9575              var a = this.preanim(arguments, 2);
9576              switch(direction){
9577                  case "l":
9578                  case "left":
9579                      if(w - l > cw){
9580                          var v = Math.min(l + distance, w-cw);
9581                          this.scrollTo("left", v, a);
9582                          scrolled = true;
9583                      }
9584                      break;
9585                 case "r":
9586                 case "right":
9587                      if(l > 0){
9588                          var v = Math.max(l - distance, 0);
9589                          this.scrollTo("left", v, a);
9590                          scrolled = true;
9591                      }
9592                      break;
9593                 case "t":
9594                 case "top":
9595                 case "up":
9596                      if(t > 0){
9597                          var v = Math.max(t - distance, 0);
9598                          this.scrollTo("top", v, a);
9599                          scrolled = true;
9600                      }
9601                      break;
9602                 case "b":
9603                 case "bottom":
9604                 case "down":
9605                      if(h - t > ch){
9606                          var v = Math.min(t + distance, h-ch);
9607                          this.scrollTo("top", v, a);
9608                          scrolled = true;
9609                      }
9610                      break;
9611              }
9612              return scrolled;
9613         },
9614
9615         /**
9616          * Translates the passed page coordinates into left/top css values for this element
9617          * @param {Number/Array} x The page x or an array containing [x, y]
9618          * @param {Number} y The page y
9619          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9620          */
9621         translatePoints : function(x, y){
9622             if(typeof x == 'object' || x instanceof Array){
9623                 y = x[1]; x = x[0];
9624             }
9625             var p = this.getStyle('position');
9626             var o = this.getXY();
9627
9628             var l = parseInt(this.getStyle('left'), 10);
9629             var t = parseInt(this.getStyle('top'), 10);
9630
9631             if(isNaN(l)){
9632                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9633             }
9634             if(isNaN(t)){
9635                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9636             }
9637
9638             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9639         },
9640
9641         /**
9642          * Returns the current scroll position of the element.
9643          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9644          */
9645         getScroll : function(){
9646             var d = this.dom, doc = document;
9647             if(d == doc || d == doc.body){
9648                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9649                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9650                 return {left: l, top: t};
9651             }else{
9652                 return {left: d.scrollLeft, top: d.scrollTop};
9653             }
9654         },
9655
9656         /**
9657          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9658          * are convert to standard 6 digit hex color.
9659          * @param {String} attr The css attribute
9660          * @param {String} defaultValue The default value to use when a valid color isn't found
9661          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9662          * YUI color anims.
9663          */
9664         getColor : function(attr, defaultValue, prefix){
9665             var v = this.getStyle(attr);
9666             if(!v || v == "transparent" || v == "inherit") {
9667                 return defaultValue;
9668             }
9669             var color = typeof prefix == "undefined" ? "#" : prefix;
9670             if(v.substr(0, 4) == "rgb("){
9671                 var rvs = v.slice(4, v.length -1).split(",");
9672                 for(var i = 0; i < 3; i++){
9673                     var h = parseInt(rvs[i]).toString(16);
9674                     if(h < 16){
9675                         h = "0" + h;
9676                     }
9677                     color += h;
9678                 }
9679             } else {
9680                 if(v.substr(0, 1) == "#"){
9681                     if(v.length == 4) {
9682                         for(var i = 1; i < 4; i++){
9683                             var c = v.charAt(i);
9684                             color +=  c + c;
9685                         }
9686                     }else if(v.length == 7){
9687                         color += v.substr(1);
9688                     }
9689                 }
9690             }
9691             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9692         },
9693
9694         /**
9695          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9696          * gradient background, rounded corners and a 4-way shadow.
9697          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9698          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9699          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9700          * @return {Roo.Element} this
9701          */
9702         boxWrap : function(cls){
9703             cls = cls || 'x-box';
9704             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9705             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9706             return el;
9707         },
9708
9709         /**
9710          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9711          * @param {String} namespace The namespace in which to look for the attribute
9712          * @param {String} name The attribute name
9713          * @return {String} The attribute value
9714          */
9715         getAttributeNS : Roo.isIE ? function(ns, name){
9716             var d = this.dom;
9717             var type = typeof d[ns+":"+name];
9718             if(type != 'undefined' && type != 'unknown'){
9719                 return d[ns+":"+name];
9720             }
9721             return d[name];
9722         } : function(ns, name){
9723             var d = this.dom;
9724             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9725         },
9726         
9727         
9728         /**
9729          * Sets or Returns the value the dom attribute value
9730          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9731          * @param {String} value (optional) The value to set the attribute to
9732          * @return {String} The attribute value
9733          */
9734         attr : function(name){
9735             if (arguments.length > 1) {
9736                 this.dom.setAttribute(name, arguments[1]);
9737                 return arguments[1];
9738             }
9739             if (typeof(name) == 'object') {
9740                 for(var i in name) {
9741                     this.attr(i, name[i]);
9742                 }
9743                 return name;
9744             }
9745             
9746             
9747             if (!this.dom.hasAttribute(name)) {
9748                 return undefined;
9749             }
9750             return this.dom.getAttribute(name);
9751         }
9752         
9753         
9754         
9755     };
9756
9757     var ep = El.prototype;
9758
9759     /**
9760      * Appends an event handler (Shorthand for addListener)
9761      * @param {String}   eventName     The type of event to append
9762      * @param {Function} fn        The method the event invokes
9763      * @param {Object} scope       (optional) The scope (this object) of the fn
9764      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9765      * @method
9766      */
9767     ep.on = ep.addListener;
9768         // backwards compat
9769     ep.mon = ep.addListener;
9770
9771     /**
9772      * Removes an event handler from this element (shorthand for removeListener)
9773      * @param {String} eventName the type of event to remove
9774      * @param {Function} fn the method the event invokes
9775      * @return {Roo.Element} this
9776      * @method
9777      */
9778     ep.un = ep.removeListener;
9779
9780     /**
9781      * true to automatically adjust width and height settings for box-model issues (default to true)
9782      */
9783     ep.autoBoxAdjust = true;
9784
9785     // private
9786     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9787
9788     // private
9789     El.addUnits = function(v, defaultUnit){
9790         if(v === "" || v == "auto"){
9791             return v;
9792         }
9793         if(v === undefined){
9794             return '';
9795         }
9796         if(typeof v == "number" || !El.unitPattern.test(v)){
9797             return v + (defaultUnit || 'px');
9798         }
9799         return v;
9800     };
9801
9802     // special markup used throughout Roo when box wrapping elements
9803     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>';
9804     /**
9805      * Visibility mode constant - Use visibility to hide element
9806      * @static
9807      * @type Number
9808      */
9809     El.VISIBILITY = 1;
9810     /**
9811      * Visibility mode constant - Use display to hide element
9812      * @static
9813      * @type Number
9814      */
9815     El.DISPLAY = 2;
9816
9817     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9818     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9819     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9820
9821
9822
9823     /**
9824      * @private
9825      */
9826     El.cache = {};
9827
9828     var docEl;
9829
9830     /**
9831      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9832      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9833      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9834      * @return {Element} The Element object
9835      * @static
9836      */
9837     El.get = function(el){
9838         var ex, elm, id;
9839         if(!el){ return null; }
9840         if(typeof el == "string"){ // element id
9841             if(!(elm = document.getElementById(el))){
9842                 return null;
9843             }
9844             if(ex = El.cache[el]){
9845                 ex.dom = elm;
9846             }else{
9847                 ex = El.cache[el] = new El(elm);
9848             }
9849             return ex;
9850         }else if(el.tagName){ // dom element
9851             if(!(id = el.id)){
9852                 id = Roo.id(el);
9853             }
9854             if(ex = El.cache[id]){
9855                 ex.dom = el;
9856             }else{
9857                 ex = El.cache[id] = new El(el);
9858             }
9859             return ex;
9860         }else if(el instanceof El){
9861             if(el != docEl){
9862                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9863                                                               // catch case where it hasn't been appended
9864                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9865             }
9866             return el;
9867         }else if(el.isComposite){
9868             return el;
9869         }else if(el instanceof Array){
9870             return El.select(el);
9871         }else if(el == document){
9872             // create a bogus element object representing the document object
9873             if(!docEl){
9874                 var f = function(){};
9875                 f.prototype = El.prototype;
9876                 docEl = new f();
9877                 docEl.dom = document;
9878             }
9879             return docEl;
9880         }
9881         return null;
9882     };
9883
9884     // private
9885     El.uncache = function(el){
9886         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9887             if(a[i]){
9888                 delete El.cache[a[i].id || a[i]];
9889             }
9890         }
9891     };
9892
9893     // private
9894     // Garbage collection - uncache elements/purge listeners on orphaned elements
9895     // so we don't hold a reference and cause the browser to retain them
9896     El.garbageCollect = function(){
9897         if(!Roo.enableGarbageCollector){
9898             clearInterval(El.collectorThread);
9899             return;
9900         }
9901         for(var eid in El.cache){
9902             var el = El.cache[eid], d = el.dom;
9903             // -------------------------------------------------------
9904             // Determining what is garbage:
9905             // -------------------------------------------------------
9906             // !d
9907             // dom node is null, definitely garbage
9908             // -------------------------------------------------------
9909             // !d.parentNode
9910             // no parentNode == direct orphan, definitely garbage
9911             // -------------------------------------------------------
9912             // !d.offsetParent && !document.getElementById(eid)
9913             // display none elements have no offsetParent so we will
9914             // also try to look it up by it's id. However, check
9915             // offsetParent first so we don't do unneeded lookups.
9916             // This enables collection of elements that are not orphans
9917             // directly, but somewhere up the line they have an orphan
9918             // parent.
9919             // -------------------------------------------------------
9920             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9921                 delete El.cache[eid];
9922                 if(d && Roo.enableListenerCollection){
9923                     E.purgeElement(d);
9924                 }
9925             }
9926         }
9927     }
9928     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9929
9930
9931     // dom is optional
9932     El.Flyweight = function(dom){
9933         this.dom = dom;
9934     };
9935     El.Flyweight.prototype = El.prototype;
9936
9937     El._flyweights = {};
9938     /**
9939      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9940      * the dom node can be overwritten by other code.
9941      * @param {String/HTMLElement} el The dom node or id
9942      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9943      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9944      * @static
9945      * @return {Element} The shared Element object
9946      */
9947     El.fly = function(el, named){
9948         named = named || '_global';
9949         el = Roo.getDom(el);
9950         if(!el){
9951             return null;
9952         }
9953         if(!El._flyweights[named]){
9954             El._flyweights[named] = new El.Flyweight();
9955         }
9956         El._flyweights[named].dom = el;
9957         return El._flyweights[named];
9958     };
9959
9960     /**
9961      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9962      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9963      * Shorthand of {@link Roo.Element#get}
9964      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9965      * @return {Element} The Element object
9966      * @member Roo
9967      * @method get
9968      */
9969     Roo.get = El.get;
9970     /**
9971      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9972      * the dom node can be overwritten by other code.
9973      * Shorthand of {@link Roo.Element#fly}
9974      * @param {String/HTMLElement} el The dom node or id
9975      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9976      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9977      * @static
9978      * @return {Element} The shared Element object
9979      * @member Roo
9980      * @method fly
9981      */
9982     Roo.fly = El.fly;
9983
9984     // speedy lookup for elements never to box adjust
9985     var noBoxAdjust = Roo.isStrict ? {
9986         select:1
9987     } : {
9988         input:1, select:1, textarea:1
9989     };
9990     if(Roo.isIE || Roo.isGecko){
9991         noBoxAdjust['button'] = 1;
9992     }
9993
9994
9995     Roo.EventManager.on(window, 'unload', function(){
9996         delete El.cache;
9997         delete El._flyweights;
9998     });
9999 })();
10000
10001
10002
10003
10004 if(Roo.DomQuery){
10005     Roo.Element.selectorFunction = Roo.DomQuery.select;
10006 }
10007
10008 Roo.Element.select = function(selector, unique, root){
10009     var els;
10010     if(typeof selector == "string"){
10011         els = Roo.Element.selectorFunction(selector, root);
10012     }else if(selector.length !== undefined){
10013         els = selector;
10014     }else{
10015         throw "Invalid selector";
10016     }
10017     if(unique === true){
10018         return new Roo.CompositeElement(els);
10019     }else{
10020         return new Roo.CompositeElementLite(els);
10021     }
10022 };
10023 /**
10024  * Selects elements based on the passed CSS selector to enable working on them as 1.
10025  * @param {String/Array} selector The CSS selector or an array of elements
10026  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10027  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10028  * @return {CompositeElementLite/CompositeElement}
10029  * @member Roo
10030  * @method select
10031  */
10032 Roo.select = Roo.Element.select;
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047 /*
10048  * Based on:
10049  * Ext JS Library 1.1.1
10050  * Copyright(c) 2006-2007, Ext JS, LLC.
10051  *
10052  * Originally Released Under LGPL - original licence link has changed is not relivant.
10053  *
10054  * Fork - LGPL
10055  * <script type="text/javascript">
10056  */
10057
10058
10059
10060 //Notifies Element that fx methods are available
10061 Roo.enableFx = true;
10062
10063 /**
10064  * @class Roo.Fx
10065  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10066  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10067  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10068  * Element effects to work.</p><br/>
10069  *
10070  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10071  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10072  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10073  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10074  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10075  * expected results and should be done with care.</p><br/>
10076  *
10077  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10078  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10079 <pre>
10080 Value  Description
10081 -----  -----------------------------
10082 tl     The top left corner
10083 t      The center of the top edge
10084 tr     The top right corner
10085 l      The center of the left edge
10086 r      The center of the right edge
10087 bl     The bottom left corner
10088 b      The center of the bottom edge
10089 br     The bottom right corner
10090 </pre>
10091  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10092  * below are common options that can be passed to any Fx method.</b>
10093  * @cfg {Function} callback A function called when the effect is finished
10094  * @cfg {Object} scope The scope of the effect function
10095  * @cfg {String} easing A valid Easing value for the effect
10096  * @cfg {String} afterCls A css class to apply after the effect
10097  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10098  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10099  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10100  * effects that end with the element being visually hidden, ignored otherwise)
10101  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10102  * a function which returns such a specification that will be applied to the Element after the effect finishes
10103  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10104  * @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
10105  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10106  */
10107 Roo.Fx = {
10108         /**
10109          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10110          * origin for the slide effect.  This function automatically handles wrapping the element with
10111          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10112          * Usage:
10113          *<pre><code>
10114 // default: slide the element in from the top
10115 el.slideIn();
10116
10117 // custom: slide the element in from the right with a 2-second duration
10118 el.slideIn('r', { duration: 2 });
10119
10120 // common config options shown with default values
10121 el.slideIn('t', {
10122     easing: 'easeOut',
10123     duration: .5
10124 });
10125 </code></pre>
10126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10127          * @param {Object} options (optional) Object literal with any of the Fx config options
10128          * @return {Roo.Element} The Element
10129          */
10130     slideIn : function(anchor, o){
10131         var el = this.getFxEl();
10132         o = o || {};
10133
10134         el.queueFx(o, function(){
10135
10136             anchor = anchor || "t";
10137
10138             // fix display to visibility
10139             this.fixDisplay();
10140
10141             // restore values after effect
10142             var r = this.getFxRestore();
10143             var b = this.getBox();
10144             // fixed size for slide
10145             this.setSize(b);
10146
10147             // wrap if needed
10148             var wrap = this.fxWrap(r.pos, o, "hidden");
10149
10150             var st = this.dom.style;
10151             st.visibility = "visible";
10152             st.position = "absolute";
10153
10154             // clear out temp styles after slide and unwrap
10155             var after = function(){
10156                 el.fxUnwrap(wrap, r.pos, o);
10157                 st.width = r.width;
10158                 st.height = r.height;
10159                 el.afterFx(o);
10160             };
10161             // time to calc the positions
10162             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10163
10164             switch(anchor.toLowerCase()){
10165                 case "t":
10166                     wrap.setSize(b.width, 0);
10167                     st.left = st.bottom = "0";
10168                     a = {height: bh};
10169                 break;
10170                 case "l":
10171                     wrap.setSize(0, b.height);
10172                     st.right = st.top = "0";
10173                     a = {width: bw};
10174                 break;
10175                 case "r":
10176                     wrap.setSize(0, b.height);
10177                     wrap.setX(b.right);
10178                     st.left = st.top = "0";
10179                     a = {width: bw, points: pt};
10180                 break;
10181                 case "b":
10182                     wrap.setSize(b.width, 0);
10183                     wrap.setY(b.bottom);
10184                     st.left = st.top = "0";
10185                     a = {height: bh, points: pt};
10186                 break;
10187                 case "tl":
10188                     wrap.setSize(0, 0);
10189                     st.right = st.bottom = "0";
10190                     a = {width: bw, height: bh};
10191                 break;
10192                 case "bl":
10193                     wrap.setSize(0, 0);
10194                     wrap.setY(b.y+b.height);
10195                     st.right = st.top = "0";
10196                     a = {width: bw, height: bh, points: pt};
10197                 break;
10198                 case "br":
10199                     wrap.setSize(0, 0);
10200                     wrap.setXY([b.right, b.bottom]);
10201                     st.left = st.top = "0";
10202                     a = {width: bw, height: bh, points: pt};
10203                 break;
10204                 case "tr":
10205                     wrap.setSize(0, 0);
10206                     wrap.setX(b.x+b.width);
10207                     st.left = st.bottom = "0";
10208                     a = {width: bw, height: bh, points: pt};
10209                 break;
10210             }
10211             this.dom.style.visibility = "visible";
10212             wrap.show();
10213
10214             arguments.callee.anim = wrap.fxanim(a,
10215                 o,
10216                 'motion',
10217                 .5,
10218                 'easeOut', after);
10219         });
10220         return this;
10221     },
10222     
10223         /**
10224          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10225          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10226          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10227          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10228          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10229          * Usage:
10230          *<pre><code>
10231 // default: slide the element out to the top
10232 el.slideOut();
10233
10234 // custom: slide the element out to the right with a 2-second duration
10235 el.slideOut('r', { duration: 2 });
10236
10237 // common config options shown with default values
10238 el.slideOut('t', {
10239     easing: 'easeOut',
10240     duration: .5,
10241     remove: false,
10242     useDisplay: false
10243 });
10244 </code></pre>
10245          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10246          * @param {Object} options (optional) Object literal with any of the Fx config options
10247          * @return {Roo.Element} The Element
10248          */
10249     slideOut : function(anchor, o){
10250         var el = this.getFxEl();
10251         o = o || {};
10252
10253         el.queueFx(o, function(){
10254
10255             anchor = anchor || "t";
10256
10257             // restore values after effect
10258             var r = this.getFxRestore();
10259             
10260             var b = this.getBox();
10261             // fixed size for slide
10262             this.setSize(b);
10263
10264             // wrap if needed
10265             var wrap = this.fxWrap(r.pos, o, "visible");
10266
10267             var st = this.dom.style;
10268             st.visibility = "visible";
10269             st.position = "absolute";
10270
10271             wrap.setSize(b);
10272
10273             var after = function(){
10274                 if(o.useDisplay){
10275                     el.setDisplayed(false);
10276                 }else{
10277                     el.hide();
10278                 }
10279
10280                 el.fxUnwrap(wrap, r.pos, o);
10281
10282                 st.width = r.width;
10283                 st.height = r.height;
10284
10285                 el.afterFx(o);
10286             };
10287
10288             var a, zero = {to: 0};
10289             switch(anchor.toLowerCase()){
10290                 case "t":
10291                     st.left = st.bottom = "0";
10292                     a = {height: zero};
10293                 break;
10294                 case "l":
10295                     st.right = st.top = "0";
10296                     a = {width: zero};
10297                 break;
10298                 case "r":
10299                     st.left = st.top = "0";
10300                     a = {width: zero, points: {to:[b.right, b.y]}};
10301                 break;
10302                 case "b":
10303                     st.left = st.top = "0";
10304                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10305                 break;
10306                 case "tl":
10307                     st.right = st.bottom = "0";
10308                     a = {width: zero, height: zero};
10309                 break;
10310                 case "bl":
10311                     st.right = st.top = "0";
10312                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10313                 break;
10314                 case "br":
10315                     st.left = st.top = "0";
10316                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10317                 break;
10318                 case "tr":
10319                     st.left = st.bottom = "0";
10320                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10321                 break;
10322             }
10323
10324             arguments.callee.anim = wrap.fxanim(a,
10325                 o,
10326                 'motion',
10327                 .5,
10328                 "easeOut", after);
10329         });
10330         return this;
10331     },
10332
10333         /**
10334          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10335          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10336          * The element must be removed from the DOM using the 'remove' config option if desired.
10337          * Usage:
10338          *<pre><code>
10339 // default
10340 el.puff();
10341
10342 // common config options shown with default values
10343 el.puff({
10344     easing: 'easeOut',
10345     duration: .5,
10346     remove: false,
10347     useDisplay: false
10348 });
10349 </code></pre>
10350          * @param {Object} options (optional) Object literal with any of the Fx config options
10351          * @return {Roo.Element} The Element
10352          */
10353     puff : function(o){
10354         var el = this.getFxEl();
10355         o = o || {};
10356
10357         el.queueFx(o, function(){
10358             this.clearOpacity();
10359             this.show();
10360
10361             // restore values after effect
10362             var r = this.getFxRestore();
10363             var st = this.dom.style;
10364
10365             var after = function(){
10366                 if(o.useDisplay){
10367                     el.setDisplayed(false);
10368                 }else{
10369                     el.hide();
10370                 }
10371
10372                 el.clearOpacity();
10373
10374                 el.setPositioning(r.pos);
10375                 st.width = r.width;
10376                 st.height = r.height;
10377                 st.fontSize = '';
10378                 el.afterFx(o);
10379             };
10380
10381             var width = this.getWidth();
10382             var height = this.getHeight();
10383
10384             arguments.callee.anim = this.fxanim({
10385                     width : {to: this.adjustWidth(width * 2)},
10386                     height : {to: this.adjustHeight(height * 2)},
10387                     points : {by: [-(width * .5), -(height * .5)]},
10388                     opacity : {to: 0},
10389                     fontSize: {to:200, unit: "%"}
10390                 },
10391                 o,
10392                 'motion',
10393                 .5,
10394                 "easeOut", after);
10395         });
10396         return this;
10397     },
10398
10399         /**
10400          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10401          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10402          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10403          * Usage:
10404          *<pre><code>
10405 // default
10406 el.switchOff();
10407
10408 // all config options shown with default values
10409 el.switchOff({
10410     easing: 'easeIn',
10411     duration: .3,
10412     remove: false,
10413     useDisplay: false
10414 });
10415 </code></pre>
10416          * @param {Object} options (optional) Object literal with any of the Fx config options
10417          * @return {Roo.Element} The Element
10418          */
10419     switchOff : function(o){
10420         var el = this.getFxEl();
10421         o = o || {};
10422
10423         el.queueFx(o, function(){
10424             this.clearOpacity();
10425             this.clip();
10426
10427             // restore values after effect
10428             var r = this.getFxRestore();
10429             var st = this.dom.style;
10430
10431             var after = function(){
10432                 if(o.useDisplay){
10433                     el.setDisplayed(false);
10434                 }else{
10435                     el.hide();
10436                 }
10437
10438                 el.clearOpacity();
10439                 el.setPositioning(r.pos);
10440                 st.width = r.width;
10441                 st.height = r.height;
10442
10443                 el.afterFx(o);
10444             };
10445
10446             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10447                 this.clearOpacity();
10448                 (function(){
10449                     this.fxanim({
10450                         height:{to:1},
10451                         points:{by:[0, this.getHeight() * .5]}
10452                     }, o, 'motion', 0.3, 'easeIn', after);
10453                 }).defer(100, this);
10454             });
10455         });
10456         return this;
10457     },
10458
10459     /**
10460      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10461      * changed using the "attr" config option) and then fading back to the original color. If no original
10462      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10463      * Usage:
10464 <pre><code>
10465 // default: highlight background to yellow
10466 el.highlight();
10467
10468 // custom: highlight foreground text to blue for 2 seconds
10469 el.highlight("0000ff", { attr: 'color', duration: 2 });
10470
10471 // common config options shown with default values
10472 el.highlight("ffff9c", {
10473     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10474     endColor: (current color) or "ffffff",
10475     easing: 'easeIn',
10476     duration: 1
10477 });
10478 </code></pre>
10479      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10480      * @param {Object} options (optional) Object literal with any of the Fx config options
10481      * @return {Roo.Element} The Element
10482      */ 
10483     highlight : function(color, o){
10484         var el = this.getFxEl();
10485         o = o || {};
10486
10487         el.queueFx(o, function(){
10488             color = color || "ffff9c";
10489             attr = o.attr || "backgroundColor";
10490
10491             this.clearOpacity();
10492             this.show();
10493
10494             var origColor = this.getColor(attr);
10495             var restoreColor = this.dom.style[attr];
10496             endColor = (o.endColor || origColor) || "ffffff";
10497
10498             var after = function(){
10499                 el.dom.style[attr] = restoreColor;
10500                 el.afterFx(o);
10501             };
10502
10503             var a = {};
10504             a[attr] = {from: color, to: endColor};
10505             arguments.callee.anim = this.fxanim(a,
10506                 o,
10507                 'color',
10508                 1,
10509                 'easeIn', after);
10510         });
10511         return this;
10512     },
10513
10514    /**
10515     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10516     * Usage:
10517 <pre><code>
10518 // default: a single light blue ripple
10519 el.frame();
10520
10521 // custom: 3 red ripples lasting 3 seconds total
10522 el.frame("ff0000", 3, { duration: 3 });
10523
10524 // common config options shown with default values
10525 el.frame("C3DAF9", 1, {
10526     duration: 1 //duration of entire animation (not each individual ripple)
10527     // Note: Easing is not configurable and will be ignored if included
10528 });
10529 </code></pre>
10530     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10531     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10532     * @param {Object} options (optional) Object literal with any of the Fx config options
10533     * @return {Roo.Element} The Element
10534     */
10535     frame : function(color, count, o){
10536         var el = this.getFxEl();
10537         o = o || {};
10538
10539         el.queueFx(o, function(){
10540             color = color || "#C3DAF9";
10541             if(color.length == 6){
10542                 color = "#" + color;
10543             }
10544             count = count || 1;
10545             duration = o.duration || 1;
10546             this.show();
10547
10548             var b = this.getBox();
10549             var animFn = function(){
10550                 var proxy = this.createProxy({
10551
10552                      style:{
10553                         visbility:"hidden",
10554                         position:"absolute",
10555                         "z-index":"35000", // yee haw
10556                         border:"0px solid " + color
10557                      }
10558                   });
10559                 var scale = Roo.isBorderBox ? 2 : 1;
10560                 proxy.animate({
10561                     top:{from:b.y, to:b.y - 20},
10562                     left:{from:b.x, to:b.x - 20},
10563                     borderWidth:{from:0, to:10},
10564                     opacity:{from:1, to:0},
10565                     height:{from:b.height, to:(b.height + (20*scale))},
10566                     width:{from:b.width, to:(b.width + (20*scale))}
10567                 }, duration, function(){
10568                     proxy.remove();
10569                 });
10570                 if(--count > 0){
10571                      animFn.defer((duration/2)*1000, this);
10572                 }else{
10573                     el.afterFx(o);
10574                 }
10575             };
10576             animFn.call(this);
10577         });
10578         return this;
10579     },
10580
10581    /**
10582     * Creates a pause before any subsequent queued effects begin.  If there are
10583     * no effects queued after the pause it will have no effect.
10584     * Usage:
10585 <pre><code>
10586 el.pause(1);
10587 </code></pre>
10588     * @param {Number} seconds The length of time to pause (in seconds)
10589     * @return {Roo.Element} The Element
10590     */
10591     pause : function(seconds){
10592         var el = this.getFxEl();
10593         var o = {};
10594
10595         el.queueFx(o, function(){
10596             setTimeout(function(){
10597                 el.afterFx(o);
10598             }, seconds * 1000);
10599         });
10600         return this;
10601     },
10602
10603    /**
10604     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10605     * using the "endOpacity" config option.
10606     * Usage:
10607 <pre><code>
10608 // default: fade in from opacity 0 to 100%
10609 el.fadeIn();
10610
10611 // custom: fade in from opacity 0 to 75% over 2 seconds
10612 el.fadeIn({ endOpacity: .75, duration: 2});
10613
10614 // common config options shown with default values
10615 el.fadeIn({
10616     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10617     easing: 'easeOut',
10618     duration: .5
10619 });
10620 </code></pre>
10621     * @param {Object} options (optional) Object literal with any of the Fx config options
10622     * @return {Roo.Element} The Element
10623     */
10624     fadeIn : function(o){
10625         var el = this.getFxEl();
10626         o = o || {};
10627         el.queueFx(o, function(){
10628             this.setOpacity(0);
10629             this.fixDisplay();
10630             this.dom.style.visibility = 'visible';
10631             var to = o.endOpacity || 1;
10632             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10633                 o, null, .5, "easeOut", function(){
10634                 if(to == 1){
10635                     this.clearOpacity();
10636                 }
10637                 el.afterFx(o);
10638             });
10639         });
10640         return this;
10641     },
10642
10643    /**
10644     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10645     * using the "endOpacity" config option.
10646     * Usage:
10647 <pre><code>
10648 // default: fade out from the element's current opacity to 0
10649 el.fadeOut();
10650
10651 // custom: fade out from the element's current opacity to 25% over 2 seconds
10652 el.fadeOut({ endOpacity: .25, duration: 2});
10653
10654 // common config options shown with default values
10655 el.fadeOut({
10656     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10657     easing: 'easeOut',
10658     duration: .5
10659     remove: false,
10660     useDisplay: false
10661 });
10662 </code></pre>
10663     * @param {Object} options (optional) Object literal with any of the Fx config options
10664     * @return {Roo.Element} The Element
10665     */
10666     fadeOut : function(o){
10667         var el = this.getFxEl();
10668         o = o || {};
10669         el.queueFx(o, function(){
10670             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10671                 o, null, .5, "easeOut", function(){
10672                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10673                      this.dom.style.display = "none";
10674                 }else{
10675                      this.dom.style.visibility = "hidden";
10676                 }
10677                 this.clearOpacity();
10678                 el.afterFx(o);
10679             });
10680         });
10681         return this;
10682     },
10683
10684    /**
10685     * Animates the transition of an element's dimensions from a starting height/width
10686     * to an ending height/width.
10687     * Usage:
10688 <pre><code>
10689 // change height and width to 100x100 pixels
10690 el.scale(100, 100);
10691
10692 // common config options shown with default values.  The height and width will default to
10693 // the element's existing values if passed as null.
10694 el.scale(
10695     [element's width],
10696     [element's height], {
10697     easing: 'easeOut',
10698     duration: .35
10699 });
10700 </code></pre>
10701     * @param {Number} width  The new width (pass undefined to keep the original width)
10702     * @param {Number} height  The new height (pass undefined to keep the original height)
10703     * @param {Object} options (optional) Object literal with any of the Fx config options
10704     * @return {Roo.Element} The Element
10705     */
10706     scale : function(w, h, o){
10707         this.shift(Roo.apply({}, o, {
10708             width: w,
10709             height: h
10710         }));
10711         return this;
10712     },
10713
10714    /**
10715     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10716     * Any of these properties not specified in the config object will not be changed.  This effect 
10717     * requires that at least one new dimension, position or opacity setting must be passed in on
10718     * the config object in order for the function to have any effect.
10719     * Usage:
10720 <pre><code>
10721 // slide the element horizontally to x position 200 while changing the height and opacity
10722 el.shift({ x: 200, height: 50, opacity: .8 });
10723
10724 // common config options shown with default values.
10725 el.shift({
10726     width: [element's width],
10727     height: [element's height],
10728     x: [element's x position],
10729     y: [element's y position],
10730     opacity: [element's opacity],
10731     easing: 'easeOut',
10732     duration: .35
10733 });
10734 </code></pre>
10735     * @param {Object} options  Object literal with any of the Fx config options
10736     * @return {Roo.Element} The Element
10737     */
10738     shift : function(o){
10739         var el = this.getFxEl();
10740         o = o || {};
10741         el.queueFx(o, function(){
10742             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10743             if(w !== undefined){
10744                 a.width = {to: this.adjustWidth(w)};
10745             }
10746             if(h !== undefined){
10747                 a.height = {to: this.adjustHeight(h)};
10748             }
10749             if(x !== undefined || y !== undefined){
10750                 a.points = {to: [
10751                     x !== undefined ? x : this.getX(),
10752                     y !== undefined ? y : this.getY()
10753                 ]};
10754             }
10755             if(op !== undefined){
10756                 a.opacity = {to: op};
10757             }
10758             if(o.xy !== undefined){
10759                 a.points = {to: o.xy};
10760             }
10761             arguments.callee.anim = this.fxanim(a,
10762                 o, 'motion', .35, "easeOut", function(){
10763                 el.afterFx(o);
10764             });
10765         });
10766         return this;
10767     },
10768
10769         /**
10770          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10771          * ending point of the effect.
10772          * Usage:
10773          *<pre><code>
10774 // default: slide the element downward while fading out
10775 el.ghost();
10776
10777 // custom: slide the element out to the right with a 2-second duration
10778 el.ghost('r', { duration: 2 });
10779
10780 // common config options shown with default values
10781 el.ghost('b', {
10782     easing: 'easeOut',
10783     duration: .5
10784     remove: false,
10785     useDisplay: false
10786 });
10787 </code></pre>
10788          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10789          * @param {Object} options (optional) Object literal with any of the Fx config options
10790          * @return {Roo.Element} The Element
10791          */
10792     ghost : function(anchor, o){
10793         var el = this.getFxEl();
10794         o = o || {};
10795
10796         el.queueFx(o, function(){
10797             anchor = anchor || "b";
10798
10799             // restore values after effect
10800             var r = this.getFxRestore();
10801             var w = this.getWidth(),
10802                 h = this.getHeight();
10803
10804             var st = this.dom.style;
10805
10806             var after = function(){
10807                 if(o.useDisplay){
10808                     el.setDisplayed(false);
10809                 }else{
10810                     el.hide();
10811                 }
10812
10813                 el.clearOpacity();
10814                 el.setPositioning(r.pos);
10815                 st.width = r.width;
10816                 st.height = r.height;
10817
10818                 el.afterFx(o);
10819             };
10820
10821             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10822             switch(anchor.toLowerCase()){
10823                 case "t":
10824                     pt.by = [0, -h];
10825                 break;
10826                 case "l":
10827                     pt.by = [-w, 0];
10828                 break;
10829                 case "r":
10830                     pt.by = [w, 0];
10831                 break;
10832                 case "b":
10833                     pt.by = [0, h];
10834                 break;
10835                 case "tl":
10836                     pt.by = [-w, -h];
10837                 break;
10838                 case "bl":
10839                     pt.by = [-w, h];
10840                 break;
10841                 case "br":
10842                     pt.by = [w, h];
10843                 break;
10844                 case "tr":
10845                     pt.by = [w, -h];
10846                 break;
10847             }
10848
10849             arguments.callee.anim = this.fxanim(a,
10850                 o,
10851                 'motion',
10852                 .5,
10853                 "easeOut", after);
10854         });
10855         return this;
10856     },
10857
10858         /**
10859          * Ensures that all effects queued after syncFx is called on the element are
10860          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10861          * @return {Roo.Element} The Element
10862          */
10863     syncFx : function(){
10864         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10865             block : false,
10866             concurrent : true,
10867             stopFx : false
10868         });
10869         return this;
10870     },
10871
10872         /**
10873          * Ensures that all effects queued after sequenceFx is called on the element are
10874          * run in sequence.  This is the opposite of {@link #syncFx}.
10875          * @return {Roo.Element} The Element
10876          */
10877     sequenceFx : function(){
10878         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10879             block : false,
10880             concurrent : false,
10881             stopFx : false
10882         });
10883         return this;
10884     },
10885
10886         /* @private */
10887     nextFx : function(){
10888         var ef = this.fxQueue[0];
10889         if(ef){
10890             ef.call(this);
10891         }
10892     },
10893
10894         /**
10895          * Returns true if the element has any effects actively running or queued, else returns false.
10896          * @return {Boolean} True if element has active effects, else false
10897          */
10898     hasActiveFx : function(){
10899         return this.fxQueue && this.fxQueue[0];
10900     },
10901
10902         /**
10903          * Stops any running effects and clears the element's internal effects queue if it contains
10904          * any additional effects that haven't started yet.
10905          * @return {Roo.Element} The Element
10906          */
10907     stopFx : function(){
10908         if(this.hasActiveFx()){
10909             var cur = this.fxQueue[0];
10910             if(cur && cur.anim && cur.anim.isAnimated()){
10911                 this.fxQueue = [cur]; // clear out others
10912                 cur.anim.stop(true);
10913             }
10914         }
10915         return this;
10916     },
10917
10918         /* @private */
10919     beforeFx : function(o){
10920         if(this.hasActiveFx() && !o.concurrent){
10921            if(o.stopFx){
10922                this.stopFx();
10923                return true;
10924            }
10925            return false;
10926         }
10927         return true;
10928     },
10929
10930         /**
10931          * Returns true if the element is currently blocking so that no other effect can be queued
10932          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10933          * used to ensure that an effect initiated by a user action runs to completion prior to the
10934          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10935          * @return {Boolean} True if blocking, else false
10936          */
10937     hasFxBlock : function(){
10938         var q = this.fxQueue;
10939         return q && q[0] && q[0].block;
10940     },
10941
10942         /* @private */
10943     queueFx : function(o, fn){
10944         if(!this.fxQueue){
10945             this.fxQueue = [];
10946         }
10947         if(!this.hasFxBlock()){
10948             Roo.applyIf(o, this.fxDefaults);
10949             if(!o.concurrent){
10950                 var run = this.beforeFx(o);
10951                 fn.block = o.block;
10952                 this.fxQueue.push(fn);
10953                 if(run){
10954                     this.nextFx();
10955                 }
10956             }else{
10957                 fn.call(this);
10958             }
10959         }
10960         return this;
10961     },
10962
10963         /* @private */
10964     fxWrap : function(pos, o, vis){
10965         var wrap;
10966         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10967             var wrapXY;
10968             if(o.fixPosition){
10969                 wrapXY = this.getXY();
10970             }
10971             var div = document.createElement("div");
10972             div.style.visibility = vis;
10973             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10974             wrap.setPositioning(pos);
10975             if(wrap.getStyle("position") == "static"){
10976                 wrap.position("relative");
10977             }
10978             this.clearPositioning('auto');
10979             wrap.clip();
10980             wrap.dom.appendChild(this.dom);
10981             if(wrapXY){
10982                 wrap.setXY(wrapXY);
10983             }
10984         }
10985         return wrap;
10986     },
10987
10988         /* @private */
10989     fxUnwrap : function(wrap, pos, o){
10990         this.clearPositioning();
10991         this.setPositioning(pos);
10992         if(!o.wrap){
10993             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10994             wrap.remove();
10995         }
10996     },
10997
10998         /* @private */
10999     getFxRestore : function(){
11000         var st = this.dom.style;
11001         return {pos: this.getPositioning(), width: st.width, height : st.height};
11002     },
11003
11004         /* @private */
11005     afterFx : function(o){
11006         if(o.afterStyle){
11007             this.applyStyles(o.afterStyle);
11008         }
11009         if(o.afterCls){
11010             this.addClass(o.afterCls);
11011         }
11012         if(o.remove === true){
11013             this.remove();
11014         }
11015         Roo.callback(o.callback, o.scope, [this]);
11016         if(!o.concurrent){
11017             this.fxQueue.shift();
11018             this.nextFx();
11019         }
11020     },
11021
11022         /* @private */
11023     getFxEl : function(){ // support for composite element fx
11024         return Roo.get(this.dom);
11025     },
11026
11027         /* @private */
11028     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11029         animType = animType || 'run';
11030         opt = opt || {};
11031         var anim = Roo.lib.Anim[animType](
11032             this.dom, args,
11033             (opt.duration || defaultDur) || .35,
11034             (opt.easing || defaultEase) || 'easeOut',
11035             function(){
11036                 Roo.callback(cb, this);
11037             },
11038             this
11039         );
11040         opt.anim = anim;
11041         return anim;
11042     }
11043 };
11044
11045 // backwords compat
11046 Roo.Fx.resize = Roo.Fx.scale;
11047
11048 //When included, Roo.Fx is automatically applied to Element so that all basic
11049 //effects are available directly via the Element API
11050 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11051  * Based on:
11052  * Ext JS Library 1.1.1
11053  * Copyright(c) 2006-2007, Ext JS, LLC.
11054  *
11055  * Originally Released Under LGPL - original licence link has changed is not relivant.
11056  *
11057  * Fork - LGPL
11058  * <script type="text/javascript">
11059  */
11060
11061
11062 /**
11063  * @class Roo.CompositeElement
11064  * Standard composite class. Creates a Roo.Element for every element in the collection.
11065  * <br><br>
11066  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11067  * actions will be performed on all the elements in this collection.</b>
11068  * <br><br>
11069  * All methods return <i>this</i> and can be chained.
11070  <pre><code>
11071  var els = Roo.select("#some-el div.some-class", true);
11072  // or select directly from an existing element
11073  var el = Roo.get('some-el');
11074  el.select('div.some-class', true);
11075
11076  els.setWidth(100); // all elements become 100 width
11077  els.hide(true); // all elements fade out and hide
11078  // or
11079  els.setWidth(100).hide(true);
11080  </code></pre>
11081  */
11082 Roo.CompositeElement = function(els){
11083     this.elements = [];
11084     this.addElements(els);
11085 };
11086 Roo.CompositeElement.prototype = {
11087     isComposite: true,
11088     addElements : function(els){
11089         if(!els) {
11090             return this;
11091         }
11092         if(typeof els == "string"){
11093             els = Roo.Element.selectorFunction(els);
11094         }
11095         var yels = this.elements;
11096         var index = yels.length-1;
11097         for(var i = 0, len = els.length; i < len; i++) {
11098                 yels[++index] = Roo.get(els[i]);
11099         }
11100         return this;
11101     },
11102
11103     /**
11104     * Clears this composite and adds the elements returned by the passed selector.
11105     * @param {String/Array} els A string CSS selector, an array of elements or an element
11106     * @return {CompositeElement} this
11107     */
11108     fill : function(els){
11109         this.elements = [];
11110         this.add(els);
11111         return this;
11112     },
11113
11114     /**
11115     * Filters this composite to only elements that match the passed selector.
11116     * @param {String} selector A string CSS selector
11117     * @param {Boolean} inverse return inverse filter (not matches)
11118     * @return {CompositeElement} this
11119     */
11120     filter : function(selector, inverse){
11121         var els = [];
11122         inverse = inverse || false;
11123         this.each(function(el){
11124             var match = inverse ? !el.is(selector) : el.is(selector);
11125             if(match){
11126                 els[els.length] = el.dom;
11127             }
11128         });
11129         this.fill(els);
11130         return this;
11131     },
11132
11133     invoke : function(fn, args){
11134         var els = this.elements;
11135         for(var i = 0, len = els.length; i < len; i++) {
11136                 Roo.Element.prototype[fn].apply(els[i], args);
11137         }
11138         return this;
11139     },
11140     /**
11141     * Adds elements to this composite.
11142     * @param {String/Array} els A string CSS selector, an array of elements or an element
11143     * @return {CompositeElement} this
11144     */
11145     add : function(els){
11146         if(typeof els == "string"){
11147             this.addElements(Roo.Element.selectorFunction(els));
11148         }else if(els.length !== undefined){
11149             this.addElements(els);
11150         }else{
11151             this.addElements([els]);
11152         }
11153         return this;
11154     },
11155     /**
11156     * Calls the passed function passing (el, this, index) for each element in this composite.
11157     * @param {Function} fn The function to call
11158     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11159     * @return {CompositeElement} this
11160     */
11161     each : function(fn, scope){
11162         var els = this.elements;
11163         for(var i = 0, len = els.length; i < len; i++){
11164             if(fn.call(scope || els[i], els[i], this, i) === false) {
11165                 break;
11166             }
11167         }
11168         return this;
11169     },
11170
11171     /**
11172      * Returns the Element object at the specified index
11173      * @param {Number} index
11174      * @return {Roo.Element}
11175      */
11176     item : function(index){
11177         return this.elements[index] || null;
11178     },
11179
11180     /**
11181      * Returns the first Element
11182      * @return {Roo.Element}
11183      */
11184     first : function(){
11185         return this.item(0);
11186     },
11187
11188     /**
11189      * Returns the last Element
11190      * @return {Roo.Element}
11191      */
11192     last : function(){
11193         return this.item(this.elements.length-1);
11194     },
11195
11196     /**
11197      * Returns the number of elements in this composite
11198      * @return Number
11199      */
11200     getCount : function(){
11201         return this.elements.length;
11202     },
11203
11204     /**
11205      * Returns true if this composite contains the passed element
11206      * @return Boolean
11207      */
11208     contains : function(el){
11209         return this.indexOf(el) !== -1;
11210     },
11211
11212     /**
11213      * Returns true if this composite contains the passed element
11214      * @return Boolean
11215      */
11216     indexOf : function(el){
11217         return this.elements.indexOf(Roo.get(el));
11218     },
11219
11220
11221     /**
11222     * Removes the specified element(s).
11223     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11224     * or an array of any of those.
11225     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11226     * @return {CompositeElement} this
11227     */
11228     removeElement : function(el, removeDom){
11229         if(el instanceof Array){
11230             for(var i = 0, len = el.length; i < len; i++){
11231                 this.removeElement(el[i]);
11232             }
11233             return this;
11234         }
11235         var index = typeof el == 'number' ? el : this.indexOf(el);
11236         if(index !== -1){
11237             if(removeDom){
11238                 var d = this.elements[index];
11239                 if(d.dom){
11240                     d.remove();
11241                 }else{
11242                     d.parentNode.removeChild(d);
11243                 }
11244             }
11245             this.elements.splice(index, 1);
11246         }
11247         return this;
11248     },
11249
11250     /**
11251     * Replaces the specified element with the passed element.
11252     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11253     * to replace.
11254     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11255     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11256     * @return {CompositeElement} this
11257     */
11258     replaceElement : function(el, replacement, domReplace){
11259         var index = typeof el == 'number' ? el : this.indexOf(el);
11260         if(index !== -1){
11261             if(domReplace){
11262                 this.elements[index].replaceWith(replacement);
11263             }else{
11264                 this.elements.splice(index, 1, Roo.get(replacement))
11265             }
11266         }
11267         return this;
11268     },
11269
11270     /**
11271      * Removes all elements.
11272      */
11273     clear : function(){
11274         this.elements = [];
11275     }
11276 };
11277 (function(){
11278     Roo.CompositeElement.createCall = function(proto, fnName){
11279         if(!proto[fnName]){
11280             proto[fnName] = function(){
11281                 return this.invoke(fnName, arguments);
11282             };
11283         }
11284     };
11285     for(var fnName in Roo.Element.prototype){
11286         if(typeof Roo.Element.prototype[fnName] == "function"){
11287             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11288         }
11289     };
11290 })();
11291 /*
11292  * Based on:
11293  * Ext JS Library 1.1.1
11294  * Copyright(c) 2006-2007, Ext JS, LLC.
11295  *
11296  * Originally Released Under LGPL - original licence link has changed is not relivant.
11297  *
11298  * Fork - LGPL
11299  * <script type="text/javascript">
11300  */
11301
11302 /**
11303  * @class Roo.CompositeElementLite
11304  * @extends Roo.CompositeElement
11305  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11306  <pre><code>
11307  var els = Roo.select("#some-el div.some-class");
11308  // or select directly from an existing element
11309  var el = Roo.get('some-el');
11310  el.select('div.some-class');
11311
11312  els.setWidth(100); // all elements become 100 width
11313  els.hide(true); // all elements fade out and hide
11314  // or
11315  els.setWidth(100).hide(true);
11316  </code></pre><br><br>
11317  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11318  * actions will be performed on all the elements in this collection.</b>
11319  */
11320 Roo.CompositeElementLite = function(els){
11321     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11322     this.el = new Roo.Element.Flyweight();
11323 };
11324 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11325     addElements : function(els){
11326         if(els){
11327             if(els instanceof Array){
11328                 this.elements = this.elements.concat(els);
11329             }else{
11330                 var yels = this.elements;
11331                 var index = yels.length-1;
11332                 for(var i = 0, len = els.length; i < len; i++) {
11333                     yels[++index] = els[i];
11334                 }
11335             }
11336         }
11337         return this;
11338     },
11339     invoke : function(fn, args){
11340         var els = this.elements;
11341         var el = this.el;
11342         for(var i = 0, len = els.length; i < len; i++) {
11343             el.dom = els[i];
11344                 Roo.Element.prototype[fn].apply(el, args);
11345         }
11346         return this;
11347     },
11348     /**
11349      * Returns a flyweight Element of the dom element object at the specified index
11350      * @param {Number} index
11351      * @return {Roo.Element}
11352      */
11353     item : function(index){
11354         if(!this.elements[index]){
11355             return null;
11356         }
11357         this.el.dom = this.elements[index];
11358         return this.el;
11359     },
11360
11361     // fixes scope with flyweight
11362     addListener : function(eventName, handler, scope, opt){
11363         var els = this.elements;
11364         for(var i = 0, len = els.length; i < len; i++) {
11365             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11366         }
11367         return this;
11368     },
11369
11370     /**
11371     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11372     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11373     * a reference to the dom node, use el.dom.</b>
11374     * @param {Function} fn The function to call
11375     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11376     * @return {CompositeElement} this
11377     */
11378     each : function(fn, scope){
11379         var els = this.elements;
11380         var el = this.el;
11381         for(var i = 0, len = els.length; i < len; i++){
11382             el.dom = els[i];
11383                 if(fn.call(scope || el, el, this, i) === false){
11384                 break;
11385             }
11386         }
11387         return this;
11388     },
11389
11390     indexOf : function(el){
11391         return this.elements.indexOf(Roo.getDom(el));
11392     },
11393
11394     replaceElement : function(el, replacement, domReplace){
11395         var index = typeof el == 'number' ? el : this.indexOf(el);
11396         if(index !== -1){
11397             replacement = Roo.getDom(replacement);
11398             if(domReplace){
11399                 var d = this.elements[index];
11400                 d.parentNode.insertBefore(replacement, d);
11401                 d.parentNode.removeChild(d);
11402             }
11403             this.elements.splice(index, 1, replacement);
11404         }
11405         return this;
11406     }
11407 });
11408 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11409
11410 /*
11411  * Based on:
11412  * Ext JS Library 1.1.1
11413  * Copyright(c) 2006-2007, Ext JS, LLC.
11414  *
11415  * Originally Released Under LGPL - original licence link has changed is not relivant.
11416  *
11417  * Fork - LGPL
11418  * <script type="text/javascript">
11419  */
11420
11421  
11422
11423 /**
11424  * @class Roo.data.Connection
11425  * @extends Roo.util.Observable
11426  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11427  * either to a configured URL, or to a URL specified at request time.<br><br>
11428  * <p>
11429  * Requests made by this class are asynchronous, and will return immediately. No data from
11430  * the server will be available to the statement immediately following the {@link #request} call.
11431  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11432  * <p>
11433  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11434  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11435  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11436  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11437  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11438  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11439  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11440  * standard DOM methods.
11441  * @constructor
11442  * @param {Object} config a configuration object.
11443  */
11444 Roo.data.Connection = function(config){
11445     Roo.apply(this, config);
11446     this.addEvents({
11447         /**
11448          * @event beforerequest
11449          * Fires before a network request is made to retrieve a data object.
11450          * @param {Connection} conn This Connection object.
11451          * @param {Object} options The options config object passed to the {@link #request} method.
11452          */
11453         "beforerequest" : true,
11454         /**
11455          * @event requestcomplete
11456          * Fires if the request was successfully completed.
11457          * @param {Connection} conn This Connection object.
11458          * @param {Object} response The XHR object containing the response data.
11459          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11460          * @param {Object} options The options config object passed to the {@link #request} method.
11461          */
11462         "requestcomplete" : true,
11463         /**
11464          * @event requestexception
11465          * Fires if an error HTTP status was returned from the server.
11466          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11467          * @param {Connection} conn This Connection object.
11468          * @param {Object} response The XHR object containing the response data.
11469          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11470          * @param {Object} options The options config object passed to the {@link #request} method.
11471          */
11472         "requestexception" : true
11473     });
11474     Roo.data.Connection.superclass.constructor.call(this);
11475 };
11476
11477 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11478     /**
11479      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11480      */
11481     /**
11482      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11483      * extra parameters to each request made by this object. (defaults to undefined)
11484      */
11485     /**
11486      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11487      *  to each request made by this object. (defaults to undefined)
11488      */
11489     /**
11490      * @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)
11491      */
11492     /**
11493      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11494      */
11495     timeout : 30000,
11496     /**
11497      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11498      * @type Boolean
11499      */
11500     autoAbort:false,
11501
11502     /**
11503      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11504      * @type Boolean
11505      */
11506     disableCaching: true,
11507
11508     /**
11509      * Sends an HTTP request to a remote server.
11510      * @param {Object} options An object which may contain the following properties:<ul>
11511      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11512      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11513      * request, a url encoded string or a function to call to get either.</li>
11514      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11515      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11516      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11517      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11518      * <li>options {Object} The parameter to the request call.</li>
11519      * <li>success {Boolean} True if the request succeeded.</li>
11520      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11521      * </ul></li>
11522      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11523      * The callback is passed the following parameters:<ul>
11524      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11525      * <li>options {Object} The parameter to the request call.</li>
11526      * </ul></li>
11527      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11528      * The callback is passed the following parameters:<ul>
11529      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11530      * <li>options {Object} The parameter to the request call.</li>
11531      * </ul></li>
11532      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11533      * for the callback function. Defaults to the browser window.</li>
11534      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11535      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11536      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11537      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11538      * params for the post data. Any params will be appended to the URL.</li>
11539      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11540      * </ul>
11541      * @return {Number} transactionId
11542      */
11543     request : function(o){
11544         if(this.fireEvent("beforerequest", this, o) !== false){
11545             var p = o.params;
11546
11547             if(typeof p == "function"){
11548                 p = p.call(o.scope||window, o);
11549             }
11550             if(typeof p == "object"){
11551                 p = Roo.urlEncode(o.params);
11552             }
11553             if(this.extraParams){
11554                 var extras = Roo.urlEncode(this.extraParams);
11555                 p = p ? (p + '&' + extras) : extras;
11556             }
11557
11558             var url = o.url || this.url;
11559             if(typeof url == 'function'){
11560                 url = url.call(o.scope||window, o);
11561             }
11562
11563             if(o.form){
11564                 var form = Roo.getDom(o.form);
11565                 url = url || form.action;
11566
11567                 var enctype = form.getAttribute("enctype");
11568                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11569                     return this.doFormUpload(o, p, url);
11570                 }
11571                 var f = Roo.lib.Ajax.serializeForm(form);
11572                 p = p ? (p + '&' + f) : f;
11573             }
11574
11575             var hs = o.headers;
11576             if(this.defaultHeaders){
11577                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11578                 if(!o.headers){
11579                     o.headers = hs;
11580                 }
11581             }
11582
11583             var cb = {
11584                 success: this.handleResponse,
11585                 failure: this.handleFailure,
11586                 scope: this,
11587                 argument: {options: o},
11588                 timeout : o.timeout || this.timeout
11589             };
11590
11591             var method = o.method||this.method||(p ? "POST" : "GET");
11592
11593             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11594                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11595             }
11596
11597             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11598                 if(o.autoAbort){
11599                     this.abort();
11600                 }
11601             }else if(this.autoAbort !== false){
11602                 this.abort();
11603             }
11604
11605             if((method == 'GET' && p) || o.xmlData){
11606                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11607                 p = '';
11608             }
11609             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11610             return this.transId;
11611         }else{
11612             Roo.callback(o.callback, o.scope, [o, null, null]);
11613             return null;
11614         }
11615     },
11616
11617     /**
11618      * Determine whether this object has a request outstanding.
11619      * @param {Number} transactionId (Optional) defaults to the last transaction
11620      * @return {Boolean} True if there is an outstanding request.
11621      */
11622     isLoading : function(transId){
11623         if(transId){
11624             return Roo.lib.Ajax.isCallInProgress(transId);
11625         }else{
11626             return this.transId ? true : false;
11627         }
11628     },
11629
11630     /**
11631      * Aborts any outstanding request.
11632      * @param {Number} transactionId (Optional) defaults to the last transaction
11633      */
11634     abort : function(transId){
11635         if(transId || this.isLoading()){
11636             Roo.lib.Ajax.abort(transId || this.transId);
11637         }
11638     },
11639
11640     // private
11641     handleResponse : function(response){
11642         this.transId = false;
11643         var options = response.argument.options;
11644         response.argument = options ? options.argument : null;
11645         this.fireEvent("requestcomplete", this, response, options);
11646         Roo.callback(options.success, options.scope, [response, options]);
11647         Roo.callback(options.callback, options.scope, [options, true, response]);
11648     },
11649
11650     // private
11651     handleFailure : function(response, e){
11652         this.transId = false;
11653         var options = response.argument.options;
11654         response.argument = options ? options.argument : null;
11655         this.fireEvent("requestexception", this, response, options, e);
11656         Roo.callback(options.failure, options.scope, [response, options]);
11657         Roo.callback(options.callback, options.scope, [options, false, response]);
11658     },
11659
11660     // private
11661     doFormUpload : function(o, ps, url){
11662         var id = Roo.id();
11663         var frame = document.createElement('iframe');
11664         frame.id = id;
11665         frame.name = id;
11666         frame.className = 'x-hidden';
11667         if(Roo.isIE){
11668             frame.src = Roo.SSL_SECURE_URL;
11669         }
11670         document.body.appendChild(frame);
11671
11672         if(Roo.isIE){
11673            document.frames[id].name = id;
11674         }
11675
11676         var form = Roo.getDom(o.form);
11677         form.target = id;
11678         form.method = 'POST';
11679         form.enctype = form.encoding = 'multipart/form-data';
11680         if(url){
11681             form.action = url;
11682         }
11683
11684         var hiddens, hd;
11685         if(ps){ // add dynamic params
11686             hiddens = [];
11687             ps = Roo.urlDecode(ps, false);
11688             for(var k in ps){
11689                 if(ps.hasOwnProperty(k)){
11690                     hd = document.createElement('input');
11691                     hd.type = 'hidden';
11692                     hd.name = k;
11693                     hd.value = ps[k];
11694                     form.appendChild(hd);
11695                     hiddens.push(hd);
11696                 }
11697             }
11698         }
11699
11700         function cb(){
11701             var r = {  // bogus response object
11702                 responseText : '',
11703                 responseXML : null
11704             };
11705
11706             r.argument = o ? o.argument : null;
11707
11708             try { //
11709                 var doc;
11710                 if(Roo.isIE){
11711                     doc = frame.contentWindow.document;
11712                 }else {
11713                     doc = (frame.contentDocument || window.frames[id].document);
11714                 }
11715                 if(doc && doc.body){
11716                     r.responseText = doc.body.innerHTML;
11717                 }
11718                 if(doc && doc.XMLDocument){
11719                     r.responseXML = doc.XMLDocument;
11720                 }else {
11721                     r.responseXML = doc;
11722                 }
11723             }
11724             catch(e) {
11725                 // ignore
11726             }
11727
11728             Roo.EventManager.removeListener(frame, 'load', cb, this);
11729
11730             this.fireEvent("requestcomplete", this, r, o);
11731             Roo.callback(o.success, o.scope, [r, o]);
11732             Roo.callback(o.callback, o.scope, [o, true, r]);
11733
11734             setTimeout(function(){document.body.removeChild(frame);}, 100);
11735         }
11736
11737         Roo.EventManager.on(frame, 'load', cb, this);
11738         form.submit();
11739
11740         if(hiddens){ // remove dynamic params
11741             for(var i = 0, len = hiddens.length; i < len; i++){
11742                 form.removeChild(hiddens[i]);
11743             }
11744         }
11745     }
11746 });
11747 /*
11748  * Based on:
11749  * Ext JS Library 1.1.1
11750  * Copyright(c) 2006-2007, Ext JS, LLC.
11751  *
11752  * Originally Released Under LGPL - original licence link has changed is not relivant.
11753  *
11754  * Fork - LGPL
11755  * <script type="text/javascript">
11756  */
11757  
11758 /**
11759  * Global Ajax request class.
11760  * 
11761  * @class Roo.Ajax
11762  * @extends Roo.data.Connection
11763  * @static
11764  * 
11765  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11766  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11767  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11768  * @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)
11769  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11770  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11771  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11772  */
11773 Roo.Ajax = new Roo.data.Connection({
11774     // fix up the docs
11775     /**
11776      * @scope Roo.Ajax
11777      * @type {Boolear} 
11778      */
11779     autoAbort : false,
11780
11781     /**
11782      * Serialize the passed form into a url encoded string
11783      * @scope Roo.Ajax
11784      * @param {String/HTMLElement} form
11785      * @return {String}
11786      */
11787     serializeForm : function(form){
11788         return Roo.lib.Ajax.serializeForm(form);
11789     }
11790 });/*
11791  * Based on:
11792  * Ext JS Library 1.1.1
11793  * Copyright(c) 2006-2007, Ext JS, LLC.
11794  *
11795  * Originally Released Under LGPL - original licence link has changed is not relivant.
11796  *
11797  * Fork - LGPL
11798  * <script type="text/javascript">
11799  */
11800
11801  
11802 /**
11803  * @class Roo.UpdateManager
11804  * @extends Roo.util.Observable
11805  * Provides AJAX-style update for Element object.<br><br>
11806  * Usage:<br>
11807  * <pre><code>
11808  * // Get it from a Roo.Element object
11809  * var el = Roo.get("foo");
11810  * var mgr = el.getUpdateManager();
11811  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11812  * ...
11813  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11814  * <br>
11815  * // or directly (returns the same UpdateManager instance)
11816  * var mgr = new Roo.UpdateManager("myElementId");
11817  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11818  * mgr.on("update", myFcnNeedsToKnow);
11819  * <br>
11820    // short handed call directly from the element object
11821    Roo.get("foo").load({
11822         url: "bar.php",
11823         scripts:true,
11824         params: "for=bar",
11825         text: "Loading Foo..."
11826    });
11827  * </code></pre>
11828  * @constructor
11829  * Create new UpdateManager directly.
11830  * @param {String/HTMLElement/Roo.Element} el The element to update
11831  * @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).
11832  */
11833 Roo.UpdateManager = function(el, forceNew){
11834     el = Roo.get(el);
11835     if(!forceNew && el.updateManager){
11836         return el.updateManager;
11837     }
11838     /**
11839      * The Element object
11840      * @type Roo.Element
11841      */
11842     this.el = el;
11843     /**
11844      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11845      * @type String
11846      */
11847     this.defaultUrl = null;
11848
11849     this.addEvents({
11850         /**
11851          * @event beforeupdate
11852          * Fired before an update is made, return false from your handler and the update is cancelled.
11853          * @param {Roo.Element} el
11854          * @param {String/Object/Function} url
11855          * @param {String/Object} params
11856          */
11857         "beforeupdate": true,
11858         /**
11859          * @event update
11860          * Fired after successful update is made.
11861          * @param {Roo.Element} el
11862          * @param {Object} oResponseObject The response Object
11863          */
11864         "update": true,
11865         /**
11866          * @event failure
11867          * Fired on update failure.
11868          * @param {Roo.Element} el
11869          * @param {Object} oResponseObject The response Object
11870          */
11871         "failure": true
11872     });
11873     var d = Roo.UpdateManager.defaults;
11874     /**
11875      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11876      * @type String
11877      */
11878     this.sslBlankUrl = d.sslBlankUrl;
11879     /**
11880      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11881      * @type Boolean
11882      */
11883     this.disableCaching = d.disableCaching;
11884     /**
11885      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11886      * @type String
11887      */
11888     this.indicatorText = d.indicatorText;
11889     /**
11890      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11891      * @type String
11892      */
11893     this.showLoadIndicator = d.showLoadIndicator;
11894     /**
11895      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11896      * @type Number
11897      */
11898     this.timeout = d.timeout;
11899
11900     /**
11901      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11902      * @type Boolean
11903      */
11904     this.loadScripts = d.loadScripts;
11905
11906     /**
11907      * Transaction object of current executing transaction
11908      */
11909     this.transaction = null;
11910
11911     /**
11912      * @private
11913      */
11914     this.autoRefreshProcId = null;
11915     /**
11916      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11917      * @type Function
11918      */
11919     this.refreshDelegate = this.refresh.createDelegate(this);
11920     /**
11921      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11922      * @type Function
11923      */
11924     this.updateDelegate = this.update.createDelegate(this);
11925     /**
11926      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11927      * @type Function
11928      */
11929     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11930     /**
11931      * @private
11932      */
11933     this.successDelegate = this.processSuccess.createDelegate(this);
11934     /**
11935      * @private
11936      */
11937     this.failureDelegate = this.processFailure.createDelegate(this);
11938
11939     if(!this.renderer){
11940      /**
11941       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11942       */
11943     this.renderer = new Roo.UpdateManager.BasicRenderer();
11944     }
11945     
11946     Roo.UpdateManager.superclass.constructor.call(this);
11947 };
11948
11949 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11950     /**
11951      * Get the Element this UpdateManager is bound to
11952      * @return {Roo.Element} The element
11953      */
11954     getEl : function(){
11955         return this.el;
11956     },
11957     /**
11958      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11959      * @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:
11960 <pre><code>
11961 um.update({<br/>
11962     url: "your-url.php",<br/>
11963     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11964     callback: yourFunction,<br/>
11965     scope: yourObject, //(optional scope)  <br/>
11966     discardUrl: false, <br/>
11967     nocache: false,<br/>
11968     text: "Loading...",<br/>
11969     timeout: 30,<br/>
11970     scripts: false<br/>
11971 });
11972 </code></pre>
11973      * The only required property is url. The optional properties nocache, text and scripts
11974      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11975      * @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}
11976      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11977      * @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.
11978      */
11979     update : function(url, params, callback, discardUrl){
11980         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11981             var method = this.method,
11982                 cfg;
11983             if(typeof url == "object"){ // must be config object
11984                 cfg = url;
11985                 url = cfg.url;
11986                 params = params || cfg.params;
11987                 callback = callback || cfg.callback;
11988                 discardUrl = discardUrl || cfg.discardUrl;
11989                 if(callback && cfg.scope){
11990                     callback = callback.createDelegate(cfg.scope);
11991                 }
11992                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11993                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11994                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11995                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11996                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11997             }
11998             this.showLoading();
11999             if(!discardUrl){
12000                 this.defaultUrl = url;
12001             }
12002             if(typeof url == "function"){
12003                 url = url.call(this);
12004             }
12005
12006             method = method || (params ? "POST" : "GET");
12007             if(method == "GET"){
12008                 url = this.prepareUrl(url);
12009             }
12010
12011             var o = Roo.apply(cfg ||{}, {
12012                 url : url,
12013                 params: params,
12014                 success: this.successDelegate,
12015                 failure: this.failureDelegate,
12016                 callback: undefined,
12017                 timeout: (this.timeout*1000),
12018                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12019             });
12020             Roo.log("updated manager called with timeout of " + o.timeout);
12021             this.transaction = Roo.Ajax.request(o);
12022         }
12023     },
12024
12025     /**
12026      * 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.
12027      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12028      * @param {String/HTMLElement} form The form Id or form element
12029      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12030      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12031      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12032      */
12033     formUpdate : function(form, url, reset, callback){
12034         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12035             if(typeof url == "function"){
12036                 url = url.call(this);
12037             }
12038             form = Roo.getDom(form);
12039             this.transaction = Roo.Ajax.request({
12040                 form: form,
12041                 url:url,
12042                 success: this.successDelegate,
12043                 failure: this.failureDelegate,
12044                 timeout: (this.timeout*1000),
12045                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12046             });
12047             this.showLoading.defer(1, this);
12048         }
12049     },
12050
12051     /**
12052      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12053      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12054      */
12055     refresh : function(callback){
12056         if(this.defaultUrl == null){
12057             return;
12058         }
12059         this.update(this.defaultUrl, null, callback, true);
12060     },
12061
12062     /**
12063      * Set this element to auto refresh.
12064      * @param {Number} interval How often to update (in seconds).
12065      * @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)
12066      * @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}
12067      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12068      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12069      */
12070     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12071         if(refreshNow){
12072             this.update(url || this.defaultUrl, params, callback, true);
12073         }
12074         if(this.autoRefreshProcId){
12075             clearInterval(this.autoRefreshProcId);
12076         }
12077         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12078     },
12079
12080     /**
12081      * Stop auto refresh on this element.
12082      */
12083      stopAutoRefresh : function(){
12084         if(this.autoRefreshProcId){
12085             clearInterval(this.autoRefreshProcId);
12086             delete this.autoRefreshProcId;
12087         }
12088     },
12089
12090     isAutoRefreshing : function(){
12091        return this.autoRefreshProcId ? true : false;
12092     },
12093     /**
12094      * Called to update the element to "Loading" state. Override to perform custom action.
12095      */
12096     showLoading : function(){
12097         if(this.showLoadIndicator){
12098             this.el.update(this.indicatorText);
12099         }
12100     },
12101
12102     /**
12103      * Adds unique parameter to query string if disableCaching = true
12104      * @private
12105      */
12106     prepareUrl : function(url){
12107         if(this.disableCaching){
12108             var append = "_dc=" + (new Date().getTime());
12109             if(url.indexOf("?") !== -1){
12110                 url += "&" + append;
12111             }else{
12112                 url += "?" + append;
12113             }
12114         }
12115         return url;
12116     },
12117
12118     /**
12119      * @private
12120      */
12121     processSuccess : function(response){
12122         this.transaction = null;
12123         if(response.argument.form && response.argument.reset){
12124             try{ // put in try/catch since some older FF releases had problems with this
12125                 response.argument.form.reset();
12126             }catch(e){}
12127         }
12128         if(this.loadScripts){
12129             this.renderer.render(this.el, response, this,
12130                 this.updateComplete.createDelegate(this, [response]));
12131         }else{
12132             this.renderer.render(this.el, response, this);
12133             this.updateComplete(response);
12134         }
12135     },
12136
12137     updateComplete : function(response){
12138         this.fireEvent("update", this.el, response);
12139         if(typeof response.argument.callback == "function"){
12140             response.argument.callback(this.el, true, response);
12141         }
12142     },
12143
12144     /**
12145      * @private
12146      */
12147     processFailure : function(response){
12148         this.transaction = null;
12149         this.fireEvent("failure", this.el, response);
12150         if(typeof response.argument.callback == "function"){
12151             response.argument.callback(this.el, false, response);
12152         }
12153     },
12154
12155     /**
12156      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12157      * @param {Object} renderer The object implementing the render() method
12158      */
12159     setRenderer : function(renderer){
12160         this.renderer = renderer;
12161     },
12162
12163     getRenderer : function(){
12164        return this.renderer;
12165     },
12166
12167     /**
12168      * Set the defaultUrl used for updates
12169      * @param {String/Function} defaultUrl The url or a function to call to get the url
12170      */
12171     setDefaultUrl : function(defaultUrl){
12172         this.defaultUrl = defaultUrl;
12173     },
12174
12175     /**
12176      * Aborts the executing transaction
12177      */
12178     abort : function(){
12179         if(this.transaction){
12180             Roo.Ajax.abort(this.transaction);
12181         }
12182     },
12183
12184     /**
12185      * Returns true if an update is in progress
12186      * @return {Boolean}
12187      */
12188     isUpdating : function(){
12189         if(this.transaction){
12190             return Roo.Ajax.isLoading(this.transaction);
12191         }
12192         return false;
12193     }
12194 });
12195
12196 /**
12197  * @class Roo.UpdateManager.defaults
12198  * @static (not really - but it helps the doc tool)
12199  * The defaults collection enables customizing the default properties of UpdateManager
12200  */
12201    Roo.UpdateManager.defaults = {
12202        /**
12203          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12204          * @type Number
12205          */
12206          timeout : 30,
12207
12208          /**
12209          * True to process scripts by default (Defaults to false).
12210          * @type Boolean
12211          */
12212         loadScripts : false,
12213
12214         /**
12215         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12216         * @type String
12217         */
12218         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12219         /**
12220          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12221          * @type Boolean
12222          */
12223         disableCaching : false,
12224         /**
12225          * Whether to show indicatorText when loading (Defaults to true).
12226          * @type Boolean
12227          */
12228         showLoadIndicator : true,
12229         /**
12230          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12231          * @type String
12232          */
12233         indicatorText : '<div class="loading-indicator">Loading...</div>'
12234    };
12235
12236 /**
12237  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12238  *Usage:
12239  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12240  * @param {String/HTMLElement/Roo.Element} el The element to update
12241  * @param {String} url The url
12242  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12243  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12244  * @static
12245  * @deprecated
12246  * @member Roo.UpdateManager
12247  */
12248 Roo.UpdateManager.updateElement = function(el, url, params, options){
12249     var um = Roo.get(el, true).getUpdateManager();
12250     Roo.apply(um, options);
12251     um.update(url, params, options ? options.callback : null);
12252 };
12253 // alias for backwards compat
12254 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12255 /**
12256  * @class Roo.UpdateManager.BasicRenderer
12257  * Default Content renderer. Updates the elements innerHTML with the responseText.
12258  */
12259 Roo.UpdateManager.BasicRenderer = function(){};
12260
12261 Roo.UpdateManager.BasicRenderer.prototype = {
12262     /**
12263      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12264      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12265      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12266      * @param {Roo.Element} el The element being rendered
12267      * @param {Object} response The YUI Connect response object
12268      * @param {UpdateManager} updateManager The calling update manager
12269      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12270      */
12271      render : function(el, response, updateManager, callback){
12272         el.update(response.responseText, updateManager.loadScripts, callback);
12273     }
12274 };
12275 /*
12276  * Based on:
12277  * Roo JS
12278  * (c)) Alan Knowles
12279  * Licence : LGPL
12280  */
12281
12282
12283 /**
12284  * @class Roo.DomTemplate
12285  * @extends Roo.Template
12286  * An effort at a dom based template engine..
12287  *
12288  * Similar to XTemplate, except it uses dom parsing to create the template..
12289  *
12290  * Supported features:
12291  *
12292  *  Tags:
12293
12294 <pre><code>
12295       {a_variable} - output encoded.
12296       {a_variable.format:("Y-m-d")} - call a method on the variable
12297       {a_variable:raw} - unencoded output
12298       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12299       {a_variable:this.method_on_template(...)} - call a method on the template object.
12300  
12301 </code></pre>
12302  *  The tpl tag:
12303 <pre><code>
12304         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12305         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12306         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12307         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12308   
12309 </code></pre>
12310  *      
12311  */
12312 Roo.DomTemplate = function()
12313 {
12314      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12315      if (this.html) {
12316         this.compile();
12317      }
12318 };
12319
12320
12321 Roo.extend(Roo.DomTemplate, Roo.Template, {
12322     /**
12323      * id counter for sub templates.
12324      */
12325     id : 0,
12326     /**
12327      * flag to indicate if dom parser is inside a pre,
12328      * it will strip whitespace if not.
12329      */
12330     inPre : false,
12331     
12332     /**
12333      * The various sub templates
12334      */
12335     tpls : false,
12336     
12337     
12338     
12339     /**
12340      *
12341      * basic tag replacing syntax
12342      * WORD:WORD()
12343      *
12344      * // you can fake an object call by doing this
12345      *  x.t:(test,tesT) 
12346      * 
12347      */
12348     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12349     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12350     
12351     iterChild : function (node, method) {
12352         
12353         var oldPre = this.inPre;
12354         if (node.tagName == 'PRE') {
12355             this.inPre = true;
12356         }
12357         for( var i = 0; i < node.childNodes.length; i++) {
12358             method.call(this, node.childNodes[i]);
12359         }
12360         this.inPre = oldPre;
12361     },
12362     
12363     
12364     
12365     /**
12366      * compile the template
12367      *
12368      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12369      *
12370      */
12371     compile: function()
12372     {
12373         var s = this.html;
12374         
12375         // covert the html into DOM...
12376         var doc = false;
12377         var div =false;
12378         try {
12379             doc = document.implementation.createHTMLDocument("");
12380             doc.documentElement.innerHTML =   this.html  ;
12381             div = doc.documentElement;
12382         } catch (e) {
12383             // old IE... - nasty -- it causes all sorts of issues.. with
12384             // images getting pulled from server..
12385             div = document.createElement('div');
12386             div.innerHTML = this.html;
12387         }
12388         //doc.documentElement.innerHTML = htmlBody
12389          
12390         
12391         
12392         this.tpls = [];
12393         var _t = this;
12394         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12395         
12396         var tpls = this.tpls;
12397         
12398         // create a top level template from the snippet..
12399         
12400         //Roo.log(div.innerHTML);
12401         
12402         var tpl = {
12403             uid : 'master',
12404             id : this.id++,
12405             attr : false,
12406             value : false,
12407             body : div.innerHTML,
12408             
12409             forCall : false,
12410             execCall : false,
12411             dom : div,
12412             isTop : true
12413             
12414         };
12415         tpls.unshift(tpl);
12416         
12417         
12418         // compile them...
12419         this.tpls = [];
12420         Roo.each(tpls, function(tp){
12421             this.compileTpl(tp);
12422             this.tpls[tp.id] = tp;
12423         }, this);
12424         
12425         this.master = tpls[0];
12426         return this;
12427         
12428         
12429     },
12430     
12431     compileNode : function(node, istop) {
12432         // test for
12433         //Roo.log(node);
12434         
12435         
12436         // skip anything not a tag..
12437         if (node.nodeType != 1) {
12438             if (node.nodeType == 3 && !this.inPre) {
12439                 // reduce white space..
12440                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12441                 
12442             }
12443             return;
12444         }
12445         
12446         var tpl = {
12447             uid : false,
12448             id : false,
12449             attr : false,
12450             value : false,
12451             body : '',
12452             
12453             forCall : false,
12454             execCall : false,
12455             dom : false,
12456             isTop : istop
12457             
12458             
12459         };
12460         
12461         
12462         switch(true) {
12463             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12464             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12465             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12466             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12467             // no default..
12468         }
12469         
12470         
12471         if (!tpl.attr) {
12472             // just itterate children..
12473             this.iterChild(node,this.compileNode);
12474             return;
12475         }
12476         tpl.uid = this.id++;
12477         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12478         node.removeAttribute('roo-'+ tpl.attr);
12479         if (tpl.attr != 'name') {
12480             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12481             node.parentNode.replaceChild(placeholder,  node);
12482         } else {
12483             
12484             var placeholder =  document.createElement('span');
12485             placeholder.className = 'roo-tpl-' + tpl.value;
12486             node.parentNode.replaceChild(placeholder,  node);
12487         }
12488         
12489         // parent now sees '{domtplXXXX}
12490         this.iterChild(node,this.compileNode);
12491         
12492         // we should now have node body...
12493         var div = document.createElement('div');
12494         div.appendChild(node);
12495         tpl.dom = node;
12496         // this has the unfortunate side effect of converting tagged attributes
12497         // eg. href="{...}" into %7C...%7D
12498         // this has been fixed by searching for those combo's although it's a bit hacky..
12499         
12500         
12501         tpl.body = div.innerHTML;
12502         
12503         
12504          
12505         tpl.id = tpl.uid;
12506         switch(tpl.attr) {
12507             case 'for' :
12508                 switch (tpl.value) {
12509                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12510                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12511                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12512                 }
12513                 break;
12514             
12515             case 'exec':
12516                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12517                 break;
12518             
12519             case 'if':     
12520                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12521                 break;
12522             
12523             case 'name':
12524                 tpl.id  = tpl.value; // replace non characters???
12525                 break;
12526             
12527         }
12528         
12529         
12530         this.tpls.push(tpl);
12531         
12532         
12533         
12534     },
12535     
12536     
12537     
12538     
12539     /**
12540      * Compile a segment of the template into a 'sub-template'
12541      *
12542      * 
12543      * 
12544      *
12545      */
12546     compileTpl : function(tpl)
12547     {
12548         var fm = Roo.util.Format;
12549         var useF = this.disableFormats !== true;
12550         
12551         var sep = Roo.isGecko ? "+\n" : ",\n";
12552         
12553         var undef = function(str) {
12554             Roo.debug && Roo.log("Property not found :"  + str);
12555             return '';
12556         };
12557           
12558         //Roo.log(tpl.body);
12559         
12560         
12561         
12562         var fn = function(m, lbrace, name, format, args)
12563         {
12564             //Roo.log("ARGS");
12565             //Roo.log(arguments);
12566             args = args ? args.replace(/\\'/g,"'") : args;
12567             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12568             if (typeof(format) == 'undefined') {
12569                 format =  'htmlEncode'; 
12570             }
12571             if (format == 'raw' ) {
12572                 format = false;
12573             }
12574             
12575             if(name.substr(0, 6) == 'domtpl'){
12576                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12577             }
12578             
12579             // build an array of options to determine if value is undefined..
12580             
12581             // basically get 'xxxx.yyyy' then do
12582             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12583             //    (function () { Roo.log("Property not found"); return ''; })() :
12584             //    ......
12585             
12586             var udef_ar = [];
12587             var lookfor = '';
12588             Roo.each(name.split('.'), function(st) {
12589                 lookfor += (lookfor.length ? '.': '') + st;
12590                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12591             });
12592             
12593             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12594             
12595             
12596             if(format && useF){
12597                 
12598                 args = args ? ',' + args : "";
12599                  
12600                 if(format.substr(0, 5) != "this."){
12601                     format = "fm." + format + '(';
12602                 }else{
12603                     format = 'this.call("'+ format.substr(5) + '", ';
12604                     args = ", values";
12605                 }
12606                 
12607                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12608             }
12609              
12610             if (args && args.length) {
12611                 // called with xxyx.yuu:(test,test)
12612                 // change to ()
12613                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12614             }
12615             // raw.. - :raw modifier..
12616             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12617             
12618         };
12619         var body;
12620         // branched to use + in gecko and [].join() in others
12621         if(Roo.isGecko){
12622             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12623                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12624                     "';};};";
12625         }else{
12626             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12627             body.push(tpl.body.replace(/(\r\n|\n)/g,
12628                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12629             body.push("'].join('');};};");
12630             body = body.join('');
12631         }
12632         
12633         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12634        
12635         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12636         eval(body);
12637         
12638         return this;
12639     },
12640      
12641     /**
12642      * same as applyTemplate, except it's done to one of the subTemplates
12643      * when using named templates, you can do:
12644      *
12645      * var str = pl.applySubTemplate('your-name', values);
12646      *
12647      * 
12648      * @param {Number} id of the template
12649      * @param {Object} values to apply to template
12650      * @param {Object} parent (normaly the instance of this object)
12651      */
12652     applySubTemplate : function(id, values, parent)
12653     {
12654         
12655         
12656         var t = this.tpls[id];
12657         
12658         
12659         try { 
12660             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12661                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12662                 return '';
12663             }
12664         } catch(e) {
12665             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12666             Roo.log(values);
12667           
12668             return '';
12669         }
12670         try { 
12671             
12672             if(t.execCall && t.execCall.call(this, values, parent)){
12673                 return '';
12674             }
12675         } catch(e) {
12676             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12677             Roo.log(values);
12678             return '';
12679         }
12680         
12681         try {
12682             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12683             parent = t.target ? values : parent;
12684             if(t.forCall && vs instanceof Array){
12685                 var buf = [];
12686                 for(var i = 0, len = vs.length; i < len; i++){
12687                     try {
12688                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12689                     } catch (e) {
12690                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12691                         Roo.log(e.body);
12692                         //Roo.log(t.compiled);
12693                         Roo.log(vs[i]);
12694                     }   
12695                 }
12696                 return buf.join('');
12697             }
12698         } catch (e) {
12699             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12700             Roo.log(values);
12701             return '';
12702         }
12703         try {
12704             return t.compiled.call(this, vs, parent);
12705         } catch (e) {
12706             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12707             Roo.log(e.body);
12708             //Roo.log(t.compiled);
12709             Roo.log(values);
12710             return '';
12711         }
12712     },
12713
12714    
12715
12716     applyTemplate : function(values){
12717         return this.master.compiled.call(this, values, {});
12718         //var s = this.subs;
12719     },
12720
12721     apply : function(){
12722         return this.applyTemplate.apply(this, arguments);
12723     }
12724
12725  });
12726
12727 Roo.DomTemplate.from = function(el){
12728     el = Roo.getDom(el);
12729     return new Roo.Domtemplate(el.value || el.innerHTML);
12730 };/*
12731  * Based on:
12732  * Ext JS Library 1.1.1
12733  * Copyright(c) 2006-2007, Ext JS, LLC.
12734  *
12735  * Originally Released Under LGPL - original licence link has changed is not relivant.
12736  *
12737  * Fork - LGPL
12738  * <script type="text/javascript">
12739  */
12740
12741 /**
12742  * @class Roo.util.DelayedTask
12743  * Provides a convenient method of performing setTimeout where a new
12744  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12745  * You can use this class to buffer
12746  * the keypress events for a certain number of milliseconds, and perform only if they stop
12747  * for that amount of time.
12748  * @constructor The parameters to this constructor serve as defaults and are not required.
12749  * @param {Function} fn (optional) The default function to timeout
12750  * @param {Object} scope (optional) The default scope of that timeout
12751  * @param {Array} args (optional) The default Array of arguments
12752  */
12753 Roo.util.DelayedTask = function(fn, scope, args){
12754     var id = null, d, t;
12755
12756     var call = function(){
12757         var now = new Date().getTime();
12758         if(now - t >= d){
12759             clearInterval(id);
12760             id = null;
12761             fn.apply(scope, args || []);
12762         }
12763     };
12764     /**
12765      * Cancels any pending timeout and queues a new one
12766      * @param {Number} delay The milliseconds to delay
12767      * @param {Function} newFn (optional) Overrides function passed to constructor
12768      * @param {Object} newScope (optional) Overrides scope passed to constructor
12769      * @param {Array} newArgs (optional) Overrides args passed to constructor
12770      */
12771     this.delay = function(delay, newFn, newScope, newArgs){
12772         if(id && delay != d){
12773             this.cancel();
12774         }
12775         d = delay;
12776         t = new Date().getTime();
12777         fn = newFn || fn;
12778         scope = newScope || scope;
12779         args = newArgs || args;
12780         if(!id){
12781             id = setInterval(call, d);
12782         }
12783     };
12784
12785     /**
12786      * Cancel the last queued timeout
12787      */
12788     this.cancel = function(){
12789         if(id){
12790             clearInterval(id);
12791             id = null;
12792         }
12793     };
12794 };/*
12795  * Based on:
12796  * Ext JS Library 1.1.1
12797  * Copyright(c) 2006-2007, Ext JS, LLC.
12798  *
12799  * Originally Released Under LGPL - original licence link has changed is not relivant.
12800  *
12801  * Fork - LGPL
12802  * <script type="text/javascript">
12803  */
12804  
12805  
12806 Roo.util.TaskRunner = function(interval){
12807     interval = interval || 10;
12808     var tasks = [], removeQueue = [];
12809     var id = 0;
12810     var running = false;
12811
12812     var stopThread = function(){
12813         running = false;
12814         clearInterval(id);
12815         id = 0;
12816     };
12817
12818     var startThread = function(){
12819         if(!running){
12820             running = true;
12821             id = setInterval(runTasks, interval);
12822         }
12823     };
12824
12825     var removeTask = function(task){
12826         removeQueue.push(task);
12827         if(task.onStop){
12828             task.onStop();
12829         }
12830     };
12831
12832     var runTasks = function(){
12833         if(removeQueue.length > 0){
12834             for(var i = 0, len = removeQueue.length; i < len; i++){
12835                 tasks.remove(removeQueue[i]);
12836             }
12837             removeQueue = [];
12838             if(tasks.length < 1){
12839                 stopThread();
12840                 return;
12841             }
12842         }
12843         var now = new Date().getTime();
12844         for(var i = 0, len = tasks.length; i < len; ++i){
12845             var t = tasks[i];
12846             var itime = now - t.taskRunTime;
12847             if(t.interval <= itime){
12848                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12849                 t.taskRunTime = now;
12850                 if(rt === false || t.taskRunCount === t.repeat){
12851                     removeTask(t);
12852                     return;
12853                 }
12854             }
12855             if(t.duration && t.duration <= (now - t.taskStartTime)){
12856                 removeTask(t);
12857             }
12858         }
12859     };
12860
12861     /**
12862      * Queues a new task.
12863      * @param {Object} task
12864      */
12865     this.start = function(task){
12866         tasks.push(task);
12867         task.taskStartTime = new Date().getTime();
12868         task.taskRunTime = 0;
12869         task.taskRunCount = 0;
12870         startThread();
12871         return task;
12872     };
12873
12874     this.stop = function(task){
12875         removeTask(task);
12876         return task;
12877     };
12878
12879     this.stopAll = function(){
12880         stopThread();
12881         for(var i = 0, len = tasks.length; i < len; i++){
12882             if(tasks[i].onStop){
12883                 tasks[i].onStop();
12884             }
12885         }
12886         tasks = [];
12887         removeQueue = [];
12888     };
12889 };
12890
12891 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12892  * Based on:
12893  * Ext JS Library 1.1.1
12894  * Copyright(c) 2006-2007, Ext JS, LLC.
12895  *
12896  * Originally Released Under LGPL - original licence link has changed is not relivant.
12897  *
12898  * Fork - LGPL
12899  * <script type="text/javascript">
12900  */
12901
12902  
12903 /**
12904  * @class Roo.util.MixedCollection
12905  * @extends Roo.util.Observable
12906  * A Collection class that maintains both numeric indexes and keys and exposes events.
12907  * @constructor
12908  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12909  * collection (defaults to false)
12910  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12911  * and return the key value for that item.  This is used when available to look up the key on items that
12912  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12913  * equivalent to providing an implementation for the {@link #getKey} method.
12914  */
12915 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12916     this.items = [];
12917     this.map = {};
12918     this.keys = [];
12919     this.length = 0;
12920     this.addEvents({
12921         /**
12922          * @event clear
12923          * Fires when the collection is cleared.
12924          */
12925         "clear" : true,
12926         /**
12927          * @event add
12928          * Fires when an item is added to the collection.
12929          * @param {Number} index The index at which the item was added.
12930          * @param {Object} o The item added.
12931          * @param {String} key The key associated with the added item.
12932          */
12933         "add" : true,
12934         /**
12935          * @event replace
12936          * Fires when an item is replaced in the collection.
12937          * @param {String} key he key associated with the new added.
12938          * @param {Object} old The item being replaced.
12939          * @param {Object} new The new item.
12940          */
12941         "replace" : true,
12942         /**
12943          * @event remove
12944          * Fires when an item is removed from the collection.
12945          * @param {Object} o The item being removed.
12946          * @param {String} key (optional) The key associated with the removed item.
12947          */
12948         "remove" : true,
12949         "sort" : true
12950     });
12951     this.allowFunctions = allowFunctions === true;
12952     if(keyFn){
12953         this.getKey = keyFn;
12954     }
12955     Roo.util.MixedCollection.superclass.constructor.call(this);
12956 };
12957
12958 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12959     allowFunctions : false,
12960     
12961 /**
12962  * Adds an item to the collection.
12963  * @param {String} key The key to associate with the item
12964  * @param {Object} o The item to add.
12965  * @return {Object} The item added.
12966  */
12967     add : function(key, o){
12968         if(arguments.length == 1){
12969             o = arguments[0];
12970             key = this.getKey(o);
12971         }
12972         if(typeof key == "undefined" || key === null){
12973             this.length++;
12974             this.items.push(o);
12975             this.keys.push(null);
12976         }else{
12977             var old = this.map[key];
12978             if(old){
12979                 return this.replace(key, o);
12980             }
12981             this.length++;
12982             this.items.push(o);
12983             this.map[key] = o;
12984             this.keys.push(key);
12985         }
12986         this.fireEvent("add", this.length-1, o, key);
12987         return o;
12988     },
12989        
12990 /**
12991   * MixedCollection has a generic way to fetch keys if you implement getKey.
12992 <pre><code>
12993 // normal way
12994 var mc = new Roo.util.MixedCollection();
12995 mc.add(someEl.dom.id, someEl);
12996 mc.add(otherEl.dom.id, otherEl);
12997 //and so on
12998
12999 // using getKey
13000 var mc = new Roo.util.MixedCollection();
13001 mc.getKey = function(el){
13002    return el.dom.id;
13003 };
13004 mc.add(someEl);
13005 mc.add(otherEl);
13006
13007 // or via the constructor
13008 var mc = new Roo.util.MixedCollection(false, function(el){
13009    return el.dom.id;
13010 });
13011 mc.add(someEl);
13012 mc.add(otherEl);
13013 </code></pre>
13014  * @param o {Object} The item for which to find the key.
13015  * @return {Object} The key for the passed item.
13016  */
13017     getKey : function(o){
13018          return o.id; 
13019     },
13020    
13021 /**
13022  * Replaces an item in the collection.
13023  * @param {String} key The key associated with the item to replace, or the item to replace.
13024  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13025  * @return {Object}  The new item.
13026  */
13027     replace : function(key, o){
13028         if(arguments.length == 1){
13029             o = arguments[0];
13030             key = this.getKey(o);
13031         }
13032         var old = this.item(key);
13033         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13034              return this.add(key, o);
13035         }
13036         var index = this.indexOfKey(key);
13037         this.items[index] = o;
13038         this.map[key] = o;
13039         this.fireEvent("replace", key, old, o);
13040         return o;
13041     },
13042    
13043 /**
13044  * Adds all elements of an Array or an Object to the collection.
13045  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13046  * an Array of values, each of which are added to the collection.
13047  */
13048     addAll : function(objs){
13049         if(arguments.length > 1 || objs instanceof Array){
13050             var args = arguments.length > 1 ? arguments : objs;
13051             for(var i = 0, len = args.length; i < len; i++){
13052                 this.add(args[i]);
13053             }
13054         }else{
13055             for(var key in objs){
13056                 if(this.allowFunctions || typeof objs[key] != "function"){
13057                     this.add(key, objs[key]);
13058                 }
13059             }
13060         }
13061     },
13062    
13063 /**
13064  * Executes the specified function once for every item in the collection, passing each
13065  * item as the first and only parameter. returning false from the function will stop the iteration.
13066  * @param {Function} fn The function to execute for each item.
13067  * @param {Object} scope (optional) The scope in which to execute the function.
13068  */
13069     each : function(fn, scope){
13070         var items = [].concat(this.items); // each safe for removal
13071         for(var i = 0, len = items.length; i < len; i++){
13072             if(fn.call(scope || items[i], items[i], i, len) === false){
13073                 break;
13074             }
13075         }
13076     },
13077    
13078 /**
13079  * Executes the specified function once for every key in the collection, passing each
13080  * key, and its associated item as the first two parameters.
13081  * @param {Function} fn The function to execute for each item.
13082  * @param {Object} scope (optional) The scope in which to execute the function.
13083  */
13084     eachKey : function(fn, scope){
13085         for(var i = 0, len = this.keys.length; i < len; i++){
13086             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13087         }
13088     },
13089    
13090 /**
13091  * Returns the first item in the collection which elicits a true return value from the
13092  * passed selection function.
13093  * @param {Function} fn The selection function to execute for each item.
13094  * @param {Object} scope (optional) The scope in which to execute the function.
13095  * @return {Object} The first item in the collection which returned true from the selection function.
13096  */
13097     find : function(fn, scope){
13098         for(var i = 0, len = this.items.length; i < len; i++){
13099             if(fn.call(scope || window, this.items[i], this.keys[i])){
13100                 return this.items[i];
13101             }
13102         }
13103         return null;
13104     },
13105    
13106 /**
13107  * Inserts an item at the specified index in the collection.
13108  * @param {Number} index The index to insert the item at.
13109  * @param {String} key The key to associate with the new item, or the item itself.
13110  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13111  * @return {Object} The item inserted.
13112  */
13113     insert : function(index, key, o){
13114         if(arguments.length == 2){
13115             o = arguments[1];
13116             key = this.getKey(o);
13117         }
13118         if(index >= this.length){
13119             return this.add(key, o);
13120         }
13121         this.length++;
13122         this.items.splice(index, 0, o);
13123         if(typeof key != "undefined" && key != null){
13124             this.map[key] = o;
13125         }
13126         this.keys.splice(index, 0, key);
13127         this.fireEvent("add", index, o, key);
13128         return o;
13129     },
13130    
13131 /**
13132  * Removed an item from the collection.
13133  * @param {Object} o The item to remove.
13134  * @return {Object} The item removed.
13135  */
13136     remove : function(o){
13137         return this.removeAt(this.indexOf(o));
13138     },
13139    
13140 /**
13141  * Remove an item from a specified index in the collection.
13142  * @param {Number} index The index within the collection of the item to remove.
13143  */
13144     removeAt : function(index){
13145         if(index < this.length && index >= 0){
13146             this.length--;
13147             var o = this.items[index];
13148             this.items.splice(index, 1);
13149             var key = this.keys[index];
13150             if(typeof key != "undefined"){
13151                 delete this.map[key];
13152             }
13153             this.keys.splice(index, 1);
13154             this.fireEvent("remove", o, key);
13155         }
13156     },
13157    
13158 /**
13159  * Removed an item associated with the passed key fom the collection.
13160  * @param {String} key The key of the item to remove.
13161  */
13162     removeKey : function(key){
13163         return this.removeAt(this.indexOfKey(key));
13164     },
13165    
13166 /**
13167  * Returns the number of items in the collection.
13168  * @return {Number} the number of items in the collection.
13169  */
13170     getCount : function(){
13171         return this.length; 
13172     },
13173    
13174 /**
13175  * Returns index within the collection of the passed Object.
13176  * @param {Object} o The item to find the index of.
13177  * @return {Number} index of the item.
13178  */
13179     indexOf : function(o){
13180         if(!this.items.indexOf){
13181             for(var i = 0, len = this.items.length; i < len; i++){
13182                 if(this.items[i] == o) {
13183                     return i;
13184                 }
13185             }
13186             return -1;
13187         }else{
13188             return this.items.indexOf(o);
13189         }
13190     },
13191    
13192 /**
13193  * Returns index within the collection of the passed key.
13194  * @param {String} key The key to find the index of.
13195  * @return {Number} index of the key.
13196  */
13197     indexOfKey : function(key){
13198         if(!this.keys.indexOf){
13199             for(var i = 0, len = this.keys.length; i < len; i++){
13200                 if(this.keys[i] == key) {
13201                     return i;
13202                 }
13203             }
13204             return -1;
13205         }else{
13206             return this.keys.indexOf(key);
13207         }
13208     },
13209    
13210 /**
13211  * Returns the item associated with the passed key OR index. Key has priority over index.
13212  * @param {String/Number} key The key or index of the item.
13213  * @return {Object} The item associated with the passed key.
13214  */
13215     item : function(key){
13216         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13217         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13218     },
13219     
13220 /**
13221  * Returns the item at the specified index.
13222  * @param {Number} index The index of the item.
13223  * @return {Object}
13224  */
13225     itemAt : function(index){
13226         return this.items[index];
13227     },
13228     
13229 /**
13230  * Returns the item associated with the passed key.
13231  * @param {String/Number} key The key of the item.
13232  * @return {Object} The item associated with the passed key.
13233  */
13234     key : function(key){
13235         return this.map[key];
13236     },
13237    
13238 /**
13239  * Returns true if the collection contains the passed Object as an item.
13240  * @param {Object} o  The Object to look for in the collection.
13241  * @return {Boolean} True if the collection contains the Object as an item.
13242  */
13243     contains : function(o){
13244         return this.indexOf(o) != -1;
13245     },
13246    
13247 /**
13248  * Returns true if the collection contains the passed Object as a key.
13249  * @param {String} key The key to look for in the collection.
13250  * @return {Boolean} True if the collection contains the Object as a key.
13251  */
13252     containsKey : function(key){
13253         return typeof this.map[key] != "undefined";
13254     },
13255    
13256 /**
13257  * Removes all items from the collection.
13258  */
13259     clear : function(){
13260         this.length = 0;
13261         this.items = [];
13262         this.keys = [];
13263         this.map = {};
13264         this.fireEvent("clear");
13265     },
13266    
13267 /**
13268  * Returns the first item in the collection.
13269  * @return {Object} the first item in the collection..
13270  */
13271     first : function(){
13272         return this.items[0]; 
13273     },
13274    
13275 /**
13276  * Returns the last item in the collection.
13277  * @return {Object} the last item in the collection..
13278  */
13279     last : function(){
13280         return this.items[this.length-1];   
13281     },
13282     
13283     _sort : function(property, dir, fn){
13284         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13285         fn = fn || function(a, b){
13286             return a-b;
13287         };
13288         var c = [], k = this.keys, items = this.items;
13289         for(var i = 0, len = items.length; i < len; i++){
13290             c[c.length] = {key: k[i], value: items[i], index: i};
13291         }
13292         c.sort(function(a, b){
13293             var v = fn(a[property], b[property]) * dsc;
13294             if(v == 0){
13295                 v = (a.index < b.index ? -1 : 1);
13296             }
13297             return v;
13298         });
13299         for(var i = 0, len = c.length; i < len; i++){
13300             items[i] = c[i].value;
13301             k[i] = c[i].key;
13302         }
13303         this.fireEvent("sort", this);
13304     },
13305     
13306     /**
13307      * Sorts this collection with the passed comparison function
13308      * @param {String} direction (optional) "ASC" or "DESC"
13309      * @param {Function} fn (optional) comparison function
13310      */
13311     sort : function(dir, fn){
13312         this._sort("value", dir, fn);
13313     },
13314     
13315     /**
13316      * Sorts this collection by keys
13317      * @param {String} direction (optional) "ASC" or "DESC"
13318      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13319      */
13320     keySort : function(dir, fn){
13321         this._sort("key", dir, fn || function(a, b){
13322             return String(a).toUpperCase()-String(b).toUpperCase();
13323         });
13324     },
13325     
13326     /**
13327      * Returns a range of items in this collection
13328      * @param {Number} startIndex (optional) defaults to 0
13329      * @param {Number} endIndex (optional) default to the last item
13330      * @return {Array} An array of items
13331      */
13332     getRange : function(start, end){
13333         var items = this.items;
13334         if(items.length < 1){
13335             return [];
13336         }
13337         start = start || 0;
13338         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13339         var r = [];
13340         if(start <= end){
13341             for(var i = start; i <= end; i++) {
13342                     r[r.length] = items[i];
13343             }
13344         }else{
13345             for(var i = start; i >= end; i--) {
13346                     r[r.length] = items[i];
13347             }
13348         }
13349         return r;
13350     },
13351         
13352     /**
13353      * Filter the <i>objects</i> in this collection by a specific property. 
13354      * Returns a new collection that has been filtered.
13355      * @param {String} property A property on your objects
13356      * @param {String/RegExp} value Either string that the property values 
13357      * should start with or a RegExp to test against the property
13358      * @return {MixedCollection} The new filtered collection
13359      */
13360     filter : function(property, value){
13361         if(!value.exec){ // not a regex
13362             value = String(value);
13363             if(value.length == 0){
13364                 return this.clone();
13365             }
13366             value = new RegExp("^" + Roo.escapeRe(value), "i");
13367         }
13368         return this.filterBy(function(o){
13369             return o && value.test(o[property]);
13370         });
13371         },
13372     
13373     /**
13374      * Filter by a function. * Returns a new collection that has been filtered.
13375      * The passed function will be called with each 
13376      * object in the collection. If the function returns true, the value is included 
13377      * otherwise it is filtered.
13378      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13379      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13380      * @return {MixedCollection} The new filtered collection
13381      */
13382     filterBy : function(fn, scope){
13383         var r = new Roo.util.MixedCollection();
13384         r.getKey = this.getKey;
13385         var k = this.keys, it = this.items;
13386         for(var i = 0, len = it.length; i < len; i++){
13387             if(fn.call(scope||this, it[i], k[i])){
13388                                 r.add(k[i], it[i]);
13389                         }
13390         }
13391         return r;
13392     },
13393     
13394     /**
13395      * Creates a duplicate of this collection
13396      * @return {MixedCollection}
13397      */
13398     clone : function(){
13399         var r = new Roo.util.MixedCollection();
13400         var k = this.keys, it = this.items;
13401         for(var i = 0, len = it.length; i < len; i++){
13402             r.add(k[i], it[i]);
13403         }
13404         r.getKey = this.getKey;
13405         return r;
13406     }
13407 });
13408 /**
13409  * Returns the item associated with the passed key or index.
13410  * @method
13411  * @param {String/Number} key The key or index of the item.
13412  * @return {Object} The item associated with the passed key.
13413  */
13414 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13415  * Based on:
13416  * Ext JS Library 1.1.1
13417  * Copyright(c) 2006-2007, Ext JS, LLC.
13418  *
13419  * Originally Released Under LGPL - original licence link has changed is not relivant.
13420  *
13421  * Fork - LGPL
13422  * <script type="text/javascript">
13423  */
13424 /**
13425  * @class Roo.util.JSON
13426  * Modified version of Douglas Crockford"s json.js that doesn"t
13427  * mess with the Object prototype 
13428  * http://www.json.org/js.html
13429  * @singleton
13430  */
13431 Roo.util.JSON = new (function(){
13432     var useHasOwn = {}.hasOwnProperty ? true : false;
13433     
13434     // crashes Safari in some instances
13435     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13436     
13437     var pad = function(n) {
13438         return n < 10 ? "0" + n : n;
13439     };
13440     
13441     var m = {
13442         "\b": '\\b',
13443         "\t": '\\t',
13444         "\n": '\\n',
13445         "\f": '\\f',
13446         "\r": '\\r',
13447         '"' : '\\"',
13448         "\\": '\\\\'
13449     };
13450
13451     var encodeString = function(s){
13452         if (/["\\\x00-\x1f]/.test(s)) {
13453             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13454                 var c = m[b];
13455                 if(c){
13456                     return c;
13457                 }
13458                 c = b.charCodeAt();
13459                 return "\\u00" +
13460                     Math.floor(c / 16).toString(16) +
13461                     (c % 16).toString(16);
13462             }) + '"';
13463         }
13464         return '"' + s + '"';
13465     };
13466     
13467     var encodeArray = function(o){
13468         var a = ["["], b, i, l = o.length, v;
13469             for (i = 0; i < l; i += 1) {
13470                 v = o[i];
13471                 switch (typeof v) {
13472                     case "undefined":
13473                     case "function":
13474                     case "unknown":
13475                         break;
13476                     default:
13477                         if (b) {
13478                             a.push(',');
13479                         }
13480                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13481                         b = true;
13482                 }
13483             }
13484             a.push("]");
13485             return a.join("");
13486     };
13487     
13488     var encodeDate = function(o){
13489         return '"' + o.getFullYear() + "-" +
13490                 pad(o.getMonth() + 1) + "-" +
13491                 pad(o.getDate()) + "T" +
13492                 pad(o.getHours()) + ":" +
13493                 pad(o.getMinutes()) + ":" +
13494                 pad(o.getSeconds()) + '"';
13495     };
13496     
13497     /**
13498      * Encodes an Object, Array or other value
13499      * @param {Mixed} o The variable to encode
13500      * @return {String} The JSON string
13501      */
13502     this.encode = function(o)
13503     {
13504         // should this be extended to fully wrap stringify..
13505         
13506         if(typeof o == "undefined" || o === null){
13507             return "null";
13508         }else if(o instanceof Array){
13509             return encodeArray(o);
13510         }else if(o instanceof Date){
13511             return encodeDate(o);
13512         }else if(typeof o == "string"){
13513             return encodeString(o);
13514         }else if(typeof o == "number"){
13515             return isFinite(o) ? String(o) : "null";
13516         }else if(typeof o == "boolean"){
13517             return String(o);
13518         }else {
13519             var a = ["{"], b, i, v;
13520             for (i in o) {
13521                 if(!useHasOwn || o.hasOwnProperty(i)) {
13522                     v = o[i];
13523                     switch (typeof v) {
13524                     case "undefined":
13525                     case "function":
13526                     case "unknown":
13527                         break;
13528                     default:
13529                         if(b){
13530                             a.push(',');
13531                         }
13532                         a.push(this.encode(i), ":",
13533                                 v === null ? "null" : this.encode(v));
13534                         b = true;
13535                     }
13536                 }
13537             }
13538             a.push("}");
13539             return a.join("");
13540         }
13541     };
13542     
13543     /**
13544      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13545      * @param {String} json The JSON string
13546      * @return {Object} The resulting object
13547      */
13548     this.decode = function(json){
13549         
13550         return  /** eval:var:json */ eval("(" + json + ')');
13551     };
13552 })();
13553 /** 
13554  * Shorthand for {@link Roo.util.JSON#encode}
13555  * @member Roo encode 
13556  * @method */
13557 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13558 /** 
13559  * Shorthand for {@link Roo.util.JSON#decode}
13560  * @member Roo decode 
13561  * @method */
13562 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13563 /*
13564  * Based on:
13565  * Ext JS Library 1.1.1
13566  * Copyright(c) 2006-2007, Ext JS, LLC.
13567  *
13568  * Originally Released Under LGPL - original licence link has changed is not relivant.
13569  *
13570  * Fork - LGPL
13571  * <script type="text/javascript">
13572  */
13573  
13574 /**
13575  * @class Roo.util.Format
13576  * Reusable data formatting functions
13577  * @singleton
13578  */
13579 Roo.util.Format = function(){
13580     var trimRe = /^\s+|\s+$/g;
13581     return {
13582         /**
13583          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13584          * @param {String} value The string to truncate
13585          * @param {Number} length The maximum length to allow before truncating
13586          * @return {String} The converted text
13587          */
13588         ellipsis : function(value, len){
13589             if(value && value.length > len){
13590                 return value.substr(0, len-3)+"...";
13591             }
13592             return value;
13593         },
13594
13595         /**
13596          * Checks a reference and converts it to empty string if it is undefined
13597          * @param {Mixed} value Reference to check
13598          * @return {Mixed} Empty string if converted, otherwise the original value
13599          */
13600         undef : function(value){
13601             return typeof value != "undefined" ? value : "";
13602         },
13603
13604         /**
13605          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13606          * @param {String} value The string to encode
13607          * @return {String} The encoded text
13608          */
13609         htmlEncode : function(value){
13610             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13611         },
13612
13613         /**
13614          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13615          * @param {String} value The string to decode
13616          * @return {String} The decoded text
13617          */
13618         htmlDecode : function(value){
13619             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13620         },
13621
13622         /**
13623          * Trims any whitespace from either side of a string
13624          * @param {String} value The text to trim
13625          * @return {String} The trimmed text
13626          */
13627         trim : function(value){
13628             return String(value).replace(trimRe, "");
13629         },
13630
13631         /**
13632          * Returns a substring from within an original string
13633          * @param {String} value The original text
13634          * @param {Number} start The start index of the substring
13635          * @param {Number} length The length of the substring
13636          * @return {String} The substring
13637          */
13638         substr : function(value, start, length){
13639             return String(value).substr(start, length);
13640         },
13641
13642         /**
13643          * Converts a string to all lower case letters
13644          * @param {String} value The text to convert
13645          * @return {String} The converted text
13646          */
13647         lowercase : function(value){
13648             return String(value).toLowerCase();
13649         },
13650
13651         /**
13652          * Converts a string to all upper case letters
13653          * @param {String} value The text to convert
13654          * @return {String} The converted text
13655          */
13656         uppercase : function(value){
13657             return String(value).toUpperCase();
13658         },
13659
13660         /**
13661          * Converts the first character only of a string to upper case
13662          * @param {String} value The text to convert
13663          * @return {String} The converted text
13664          */
13665         capitalize : function(value){
13666             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13667         },
13668
13669         // private
13670         call : function(value, fn){
13671             if(arguments.length > 2){
13672                 var args = Array.prototype.slice.call(arguments, 2);
13673                 args.unshift(value);
13674                  
13675                 return /** eval:var:value */  eval(fn).apply(window, args);
13676             }else{
13677                 /** eval:var:value */
13678                 return /** eval:var:value */ eval(fn).call(window, value);
13679             }
13680         },
13681
13682        
13683         /**
13684          * safer version of Math.toFixed..??/
13685          * @param {Number/String} value The numeric value to format
13686          * @param {Number/String} value Decimal places 
13687          * @return {String} The formatted currency string
13688          */
13689         toFixed : function(v, n)
13690         {
13691             // why not use to fixed - precision is buggered???
13692             if (!n) {
13693                 return Math.round(v-0);
13694             }
13695             var fact = Math.pow(10,n+1);
13696             v = (Math.round((v-0)*fact))/fact;
13697             var z = (''+fact).substring(2);
13698             if (v == Math.floor(v)) {
13699                 return Math.floor(v) + '.' + z;
13700             }
13701             
13702             // now just padd decimals..
13703             var ps = String(v).split('.');
13704             var fd = (ps[1] + z);
13705             var r = fd.substring(0,n); 
13706             var rm = fd.substring(n); 
13707             if (rm < 5) {
13708                 return ps[0] + '.' + r;
13709             }
13710             r*=1; // turn it into a number;
13711             r++;
13712             if (String(r).length != n) {
13713                 ps[0]*=1;
13714                 ps[0]++;
13715                 r = String(r).substring(1); // chop the end off.
13716             }
13717             
13718             return ps[0] + '.' + r;
13719              
13720         },
13721         
13722         /**
13723          * Format a number as US currency
13724          * @param {Number/String} value The numeric value to format
13725          * @return {String} The formatted currency string
13726          */
13727         usMoney : function(v){
13728             return '$' + Roo.util.Format.number(v);
13729         },
13730         
13731         /**
13732          * Format a number
13733          * eventually this should probably emulate php's number_format
13734          * @param {Number/String} value The numeric value to format
13735          * @param {Number} decimals number of decimal places
13736          * @return {String} The formatted currency string
13737          */
13738         number : function(v,decimals)
13739         {
13740             // multiply and round.
13741             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13742             var mul = Math.pow(10, decimals);
13743             var zero = String(mul).substring(1);
13744             v = (Math.round((v-0)*mul))/mul;
13745             
13746             // if it's '0' number.. then
13747             
13748             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13749             v = String(v);
13750             var ps = v.split('.');
13751             var whole = ps[0];
13752             
13753             
13754             var r = /(\d+)(\d{3})/;
13755             // add comma's
13756             while (r.test(whole)) {
13757                 whole = whole.replace(r, '$1' + ',' + '$2');
13758             }
13759             
13760             
13761             var sub = ps[1] ?
13762                     // has decimals..
13763                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13764                     // does not have decimals
13765                     (decimals ? ('.' + zero) : '');
13766             
13767             
13768             return whole + sub ;
13769         },
13770         
13771         /**
13772          * Parse a value into a formatted date using the specified format pattern.
13773          * @param {Mixed} value The value to format
13774          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13775          * @return {String} The formatted date string
13776          */
13777         date : function(v, format){
13778             if(!v){
13779                 return "";
13780             }
13781             if(!(v instanceof Date)){
13782                 v = new Date(Date.parse(v));
13783             }
13784             return v.dateFormat(format || Roo.util.Format.defaults.date);
13785         },
13786
13787         /**
13788          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13789          * @param {String} format Any valid date format string
13790          * @return {Function} The date formatting function
13791          */
13792         dateRenderer : function(format){
13793             return function(v){
13794                 return Roo.util.Format.date(v, format);  
13795             };
13796         },
13797
13798         // private
13799         stripTagsRE : /<\/?[^>]+>/gi,
13800         
13801         /**
13802          * Strips all HTML tags
13803          * @param {Mixed} value The text from which to strip tags
13804          * @return {String} The stripped text
13805          */
13806         stripTags : function(v){
13807             return !v ? v : String(v).replace(this.stripTagsRE, "");
13808         }
13809     };
13810 }();
13811 Roo.util.Format.defaults = {
13812     date : 'd/M/Y'
13813 };/*
13814  * Based on:
13815  * Ext JS Library 1.1.1
13816  * Copyright(c) 2006-2007, Ext JS, LLC.
13817  *
13818  * Originally Released Under LGPL - original licence link has changed is not relivant.
13819  *
13820  * Fork - LGPL
13821  * <script type="text/javascript">
13822  */
13823
13824
13825  
13826
13827 /**
13828  * @class Roo.MasterTemplate
13829  * @extends Roo.Template
13830  * Provides a template that can have child templates. The syntax is:
13831 <pre><code>
13832 var t = new Roo.MasterTemplate(
13833         '&lt;select name="{name}"&gt;',
13834                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13835         '&lt;/select&gt;'
13836 );
13837 t.add('options', {value: 'foo', text: 'bar'});
13838 // or you can add multiple child elements in one shot
13839 t.addAll('options', [
13840     {value: 'foo', text: 'bar'},
13841     {value: 'foo2', text: 'bar2'},
13842     {value: 'foo3', text: 'bar3'}
13843 ]);
13844 // then append, applying the master template values
13845 t.append('my-form', {name: 'my-select'});
13846 </code></pre>
13847 * A name attribute for the child template is not required if you have only one child
13848 * template or you want to refer to them by index.
13849  */
13850 Roo.MasterTemplate = function(){
13851     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13852     this.originalHtml = this.html;
13853     var st = {};
13854     var m, re = this.subTemplateRe;
13855     re.lastIndex = 0;
13856     var subIndex = 0;
13857     while(m = re.exec(this.html)){
13858         var name = m[1], content = m[2];
13859         st[subIndex] = {
13860             name: name,
13861             index: subIndex,
13862             buffer: [],
13863             tpl : new Roo.Template(content)
13864         };
13865         if(name){
13866             st[name] = st[subIndex];
13867         }
13868         st[subIndex].tpl.compile();
13869         st[subIndex].tpl.call = this.call.createDelegate(this);
13870         subIndex++;
13871     }
13872     this.subCount = subIndex;
13873     this.subs = st;
13874 };
13875 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13876     /**
13877     * The regular expression used to match sub templates
13878     * @type RegExp
13879     * @property
13880     */
13881     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13882
13883     /**
13884      * Applies the passed values to a child template.
13885      * @param {String/Number} name (optional) The name or index of the child template
13886      * @param {Array/Object} values The values to be applied to the template
13887      * @return {MasterTemplate} this
13888      */
13889      add : function(name, values){
13890         if(arguments.length == 1){
13891             values = arguments[0];
13892             name = 0;
13893         }
13894         var s = this.subs[name];
13895         s.buffer[s.buffer.length] = s.tpl.apply(values);
13896         return this;
13897     },
13898
13899     /**
13900      * Applies all the passed values to a child template.
13901      * @param {String/Number} name (optional) The name or index of the child template
13902      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13903      * @param {Boolean} reset (optional) True to reset the template first
13904      * @return {MasterTemplate} this
13905      */
13906     fill : function(name, values, reset){
13907         var a = arguments;
13908         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13909             values = a[0];
13910             name = 0;
13911             reset = a[1];
13912         }
13913         if(reset){
13914             this.reset();
13915         }
13916         for(var i = 0, len = values.length; i < len; i++){
13917             this.add(name, values[i]);
13918         }
13919         return this;
13920     },
13921
13922     /**
13923      * Resets the template for reuse
13924      * @return {MasterTemplate} this
13925      */
13926      reset : function(){
13927         var s = this.subs;
13928         for(var i = 0; i < this.subCount; i++){
13929             s[i].buffer = [];
13930         }
13931         return this;
13932     },
13933
13934     applyTemplate : function(values){
13935         var s = this.subs;
13936         var replaceIndex = -1;
13937         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13938             return s[++replaceIndex].buffer.join("");
13939         });
13940         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13941     },
13942
13943     apply : function(){
13944         return this.applyTemplate.apply(this, arguments);
13945     },
13946
13947     compile : function(){return this;}
13948 });
13949
13950 /**
13951  * Alias for fill().
13952  * @method
13953  */
13954 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13955  /**
13956  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13957  * var tpl = Roo.MasterTemplate.from('element-id');
13958  * @param {String/HTMLElement} el
13959  * @param {Object} config
13960  * @static
13961  */
13962 Roo.MasterTemplate.from = function(el, config){
13963     el = Roo.getDom(el);
13964     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13965 };/*
13966  * Based on:
13967  * Ext JS Library 1.1.1
13968  * Copyright(c) 2006-2007, Ext JS, LLC.
13969  *
13970  * Originally Released Under LGPL - original licence link has changed is not relivant.
13971  *
13972  * Fork - LGPL
13973  * <script type="text/javascript">
13974  */
13975
13976  
13977 /**
13978  * @class Roo.util.CSS
13979  * Utility class for manipulating CSS rules
13980  * @singleton
13981  */
13982 Roo.util.CSS = function(){
13983         var rules = null;
13984         var doc = document;
13985
13986     var camelRe = /(-[a-z])/gi;
13987     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13988
13989    return {
13990    /**
13991     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13992     * tag and appended to the HEAD of the document.
13993     * @param {String|Object} cssText The text containing the css rules
13994     * @param {String} id An id to add to the stylesheet for later removal
13995     * @return {StyleSheet}
13996     */
13997     createStyleSheet : function(cssText, id){
13998         var ss;
13999         var head = doc.getElementsByTagName("head")[0];
14000         var nrules = doc.createElement("style");
14001         nrules.setAttribute("type", "text/css");
14002         if(id){
14003             nrules.setAttribute("id", id);
14004         }
14005         if (typeof(cssText) != 'string') {
14006             // support object maps..
14007             // not sure if this a good idea.. 
14008             // perhaps it should be merged with the general css handling
14009             // and handle js style props.
14010             var cssTextNew = [];
14011             for(var n in cssText) {
14012                 var citems = [];
14013                 for(var k in cssText[n]) {
14014                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14015                 }
14016                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14017                 
14018             }
14019             cssText = cssTextNew.join("\n");
14020             
14021         }
14022        
14023        
14024        if(Roo.isIE){
14025            head.appendChild(nrules);
14026            ss = nrules.styleSheet;
14027            ss.cssText = cssText;
14028        }else{
14029            try{
14030                 nrules.appendChild(doc.createTextNode(cssText));
14031            }catch(e){
14032                nrules.cssText = cssText; 
14033            }
14034            head.appendChild(nrules);
14035            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14036        }
14037        this.cacheStyleSheet(ss);
14038        return ss;
14039    },
14040
14041    /**
14042     * Removes a style or link tag by id
14043     * @param {String} id The id of the tag
14044     */
14045    removeStyleSheet : function(id){
14046        var existing = doc.getElementById(id);
14047        if(existing){
14048            existing.parentNode.removeChild(existing);
14049        }
14050    },
14051
14052    /**
14053     * Dynamically swaps an existing stylesheet reference for a new one
14054     * @param {String} id The id of an existing link tag to remove
14055     * @param {String} url The href of the new stylesheet to include
14056     */
14057    swapStyleSheet : function(id, url){
14058        this.removeStyleSheet(id);
14059        var ss = doc.createElement("link");
14060        ss.setAttribute("rel", "stylesheet");
14061        ss.setAttribute("type", "text/css");
14062        ss.setAttribute("id", id);
14063        ss.setAttribute("href", url);
14064        doc.getElementsByTagName("head")[0].appendChild(ss);
14065    },
14066    
14067    /**
14068     * Refresh the rule cache if you have dynamically added stylesheets
14069     * @return {Object} An object (hash) of rules indexed by selector
14070     */
14071    refreshCache : function(){
14072        return this.getRules(true);
14073    },
14074
14075    // private
14076    cacheStyleSheet : function(stylesheet){
14077        if(!rules){
14078            rules = {};
14079        }
14080        try{// try catch for cross domain access issue
14081            var ssRules = stylesheet.cssRules || stylesheet.rules;
14082            for(var j = ssRules.length-1; j >= 0; --j){
14083                rules[ssRules[j].selectorText] = ssRules[j];
14084            }
14085        }catch(e){}
14086    },
14087    
14088    /**
14089     * Gets all css rules for the document
14090     * @param {Boolean} refreshCache true to refresh the internal cache
14091     * @return {Object} An object (hash) of rules indexed by selector
14092     */
14093    getRules : function(refreshCache){
14094                 if(rules == null || refreshCache){
14095                         rules = {};
14096                         var ds = doc.styleSheets;
14097                         for(var i =0, len = ds.length; i < len; i++){
14098                             try{
14099                         this.cacheStyleSheet(ds[i]);
14100                     }catch(e){} 
14101                 }
14102                 }
14103                 return rules;
14104         },
14105         
14106         /**
14107     * Gets an an individual CSS rule by selector(s)
14108     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14109     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14110     * @return {CSSRule} The CSS rule or null if one is not found
14111     */
14112    getRule : function(selector, refreshCache){
14113                 var rs = this.getRules(refreshCache);
14114                 if(!(selector instanceof Array)){
14115                     return rs[selector];
14116                 }
14117                 for(var i = 0; i < selector.length; i++){
14118                         if(rs[selector[i]]){
14119                                 return rs[selector[i]];
14120                         }
14121                 }
14122                 return null;
14123         },
14124         
14125         
14126         /**
14127     * Updates a rule property
14128     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14129     * @param {String} property The css property
14130     * @param {String} value The new value for the property
14131     * @return {Boolean} true If a rule was found and updated
14132     */
14133    updateRule : function(selector, property, value){
14134                 if(!(selector instanceof Array)){
14135                         var rule = this.getRule(selector);
14136                         if(rule){
14137                                 rule.style[property.replace(camelRe, camelFn)] = value;
14138                                 return true;
14139                         }
14140                 }else{
14141                         for(var i = 0; i < selector.length; i++){
14142                                 if(this.updateRule(selector[i], property, value)){
14143                                         return true;
14144                                 }
14145                         }
14146                 }
14147                 return false;
14148         }
14149    };   
14150 }();/*
14151  * Based on:
14152  * Ext JS Library 1.1.1
14153  * Copyright(c) 2006-2007, Ext JS, LLC.
14154  *
14155  * Originally Released Under LGPL - original licence link has changed is not relivant.
14156  *
14157  * Fork - LGPL
14158  * <script type="text/javascript">
14159  */
14160
14161  
14162
14163 /**
14164  * @class Roo.util.ClickRepeater
14165  * @extends Roo.util.Observable
14166  * 
14167  * A wrapper class which can be applied to any element. Fires a "click" event while the
14168  * mouse is pressed. The interval between firings may be specified in the config but
14169  * defaults to 10 milliseconds.
14170  * 
14171  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14172  * 
14173  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14174  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14175  * Similar to an autorepeat key delay.
14176  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14177  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14178  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14179  *           "interval" and "delay" are ignored. "immediate" is honored.
14180  * @cfg {Boolean} preventDefault True to prevent the default click event
14181  * @cfg {Boolean} stopDefault True to stop the default click event
14182  * 
14183  * @history
14184  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14185  *     2007-02-02 jvs Renamed to ClickRepeater
14186  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14187  *
14188  *  @constructor
14189  * @param {String/HTMLElement/Element} el The element to listen on
14190  * @param {Object} config
14191  **/
14192 Roo.util.ClickRepeater = function(el, config)
14193 {
14194     this.el = Roo.get(el);
14195     this.el.unselectable();
14196
14197     Roo.apply(this, config);
14198
14199     this.addEvents({
14200     /**
14201      * @event mousedown
14202      * Fires when the mouse button is depressed.
14203      * @param {Roo.util.ClickRepeater} this
14204      */
14205         "mousedown" : true,
14206     /**
14207      * @event click
14208      * Fires on a specified interval during the time the element is pressed.
14209      * @param {Roo.util.ClickRepeater} this
14210      */
14211         "click" : true,
14212     /**
14213      * @event mouseup
14214      * Fires when the mouse key is released.
14215      * @param {Roo.util.ClickRepeater} this
14216      */
14217         "mouseup" : true
14218     });
14219
14220     this.el.on("mousedown", this.handleMouseDown, this);
14221     if(this.preventDefault || this.stopDefault){
14222         this.el.on("click", function(e){
14223             if(this.preventDefault){
14224                 e.preventDefault();
14225             }
14226             if(this.stopDefault){
14227                 e.stopEvent();
14228             }
14229         }, this);
14230     }
14231
14232     // allow inline handler
14233     if(this.handler){
14234         this.on("click", this.handler,  this.scope || this);
14235     }
14236
14237     Roo.util.ClickRepeater.superclass.constructor.call(this);
14238 };
14239
14240 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14241     interval : 20,
14242     delay: 250,
14243     preventDefault : true,
14244     stopDefault : false,
14245     timer : 0,
14246
14247     // private
14248     handleMouseDown : function(){
14249         clearTimeout(this.timer);
14250         this.el.blur();
14251         if(this.pressClass){
14252             this.el.addClass(this.pressClass);
14253         }
14254         this.mousedownTime = new Date();
14255
14256         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14257         this.el.on("mouseout", this.handleMouseOut, this);
14258
14259         this.fireEvent("mousedown", this);
14260         this.fireEvent("click", this);
14261         
14262         this.timer = this.click.defer(this.delay || this.interval, this);
14263     },
14264
14265     // private
14266     click : function(){
14267         this.fireEvent("click", this);
14268         this.timer = this.click.defer(this.getInterval(), this);
14269     },
14270
14271     // private
14272     getInterval: function(){
14273         if(!this.accelerate){
14274             return this.interval;
14275         }
14276         var pressTime = this.mousedownTime.getElapsed();
14277         if(pressTime < 500){
14278             return 400;
14279         }else if(pressTime < 1700){
14280             return 320;
14281         }else if(pressTime < 2600){
14282             return 250;
14283         }else if(pressTime < 3500){
14284             return 180;
14285         }else if(pressTime < 4400){
14286             return 140;
14287         }else if(pressTime < 5300){
14288             return 80;
14289         }else if(pressTime < 6200){
14290             return 50;
14291         }else{
14292             return 10;
14293         }
14294     },
14295
14296     // private
14297     handleMouseOut : function(){
14298         clearTimeout(this.timer);
14299         if(this.pressClass){
14300             this.el.removeClass(this.pressClass);
14301         }
14302         this.el.on("mouseover", this.handleMouseReturn, this);
14303     },
14304
14305     // private
14306     handleMouseReturn : function(){
14307         this.el.un("mouseover", this.handleMouseReturn);
14308         if(this.pressClass){
14309             this.el.addClass(this.pressClass);
14310         }
14311         this.click();
14312     },
14313
14314     // private
14315     handleMouseUp : function(){
14316         clearTimeout(this.timer);
14317         this.el.un("mouseover", this.handleMouseReturn);
14318         this.el.un("mouseout", this.handleMouseOut);
14319         Roo.get(document).un("mouseup", this.handleMouseUp);
14320         this.el.removeClass(this.pressClass);
14321         this.fireEvent("mouseup", this);
14322     }
14323 });/*
14324  * Based on:
14325  * Ext JS Library 1.1.1
14326  * Copyright(c) 2006-2007, Ext JS, LLC.
14327  *
14328  * Originally Released Under LGPL - original licence link has changed is not relivant.
14329  *
14330  * Fork - LGPL
14331  * <script type="text/javascript">
14332  */
14333
14334  
14335 /**
14336  * @class Roo.KeyNav
14337  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14338  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14339  * way to implement custom navigation schemes for any UI component.</p>
14340  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14341  * pageUp, pageDown, del, home, end.  Usage:</p>
14342  <pre><code>
14343 var nav = new Roo.KeyNav("my-element", {
14344     "left" : function(e){
14345         this.moveLeft(e.ctrlKey);
14346     },
14347     "right" : function(e){
14348         this.moveRight(e.ctrlKey);
14349     },
14350     "enter" : function(e){
14351         this.save();
14352     },
14353     scope : this
14354 });
14355 </code></pre>
14356  * @constructor
14357  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14358  * @param {Object} config The config
14359  */
14360 Roo.KeyNav = function(el, config){
14361     this.el = Roo.get(el);
14362     Roo.apply(this, config);
14363     if(!this.disabled){
14364         this.disabled = true;
14365         this.enable();
14366     }
14367 };
14368
14369 Roo.KeyNav.prototype = {
14370     /**
14371      * @cfg {Boolean} disabled
14372      * True to disable this KeyNav instance (defaults to false)
14373      */
14374     disabled : false,
14375     /**
14376      * @cfg {String} defaultEventAction
14377      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14378      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14379      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14380      */
14381     defaultEventAction: "stopEvent",
14382     /**
14383      * @cfg {Boolean} forceKeyDown
14384      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14385      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14386      * handle keydown instead of keypress.
14387      */
14388     forceKeyDown : false,
14389
14390     // private
14391     prepareEvent : function(e){
14392         var k = e.getKey();
14393         var h = this.keyToHandler[k];
14394         //if(h && this[h]){
14395         //    e.stopPropagation();
14396         //}
14397         if(Roo.isSafari && h && k >= 37 && k <= 40){
14398             e.stopEvent();
14399         }
14400     },
14401
14402     // private
14403     relay : function(e){
14404         var k = e.getKey();
14405         var h = this.keyToHandler[k];
14406         if(h && this[h]){
14407             if(this.doRelay(e, this[h], h) !== true){
14408                 e[this.defaultEventAction]();
14409             }
14410         }
14411     },
14412
14413     // private
14414     doRelay : function(e, h, hname){
14415         return h.call(this.scope || this, e);
14416     },
14417
14418     // possible handlers
14419     enter : false,
14420     left : false,
14421     right : false,
14422     up : false,
14423     down : false,
14424     tab : false,
14425     esc : false,
14426     pageUp : false,
14427     pageDown : false,
14428     del : false,
14429     home : false,
14430     end : false,
14431
14432     // quick lookup hash
14433     keyToHandler : {
14434         37 : "left",
14435         39 : "right",
14436         38 : "up",
14437         40 : "down",
14438         33 : "pageUp",
14439         34 : "pageDown",
14440         46 : "del",
14441         36 : "home",
14442         35 : "end",
14443         13 : "enter",
14444         27 : "esc",
14445         9  : "tab"
14446     },
14447
14448         /**
14449          * Enable this KeyNav
14450          */
14451         enable: function(){
14452                 if(this.disabled){
14453             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14454             // the EventObject will normalize Safari automatically
14455             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14456                 this.el.on("keydown", this.relay,  this);
14457             }else{
14458                 this.el.on("keydown", this.prepareEvent,  this);
14459                 this.el.on("keypress", this.relay,  this);
14460             }
14461                     this.disabled = false;
14462                 }
14463         },
14464
14465         /**
14466          * Disable this KeyNav
14467          */
14468         disable: function(){
14469                 if(!this.disabled){
14470                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14471                 this.el.un("keydown", this.relay);
14472             }else{
14473                 this.el.un("keydown", this.prepareEvent);
14474                 this.el.un("keypress", this.relay);
14475             }
14476                     this.disabled = true;
14477                 }
14478         }
14479 };/*
14480  * Based on:
14481  * Ext JS Library 1.1.1
14482  * Copyright(c) 2006-2007, Ext JS, LLC.
14483  *
14484  * Originally Released Under LGPL - original licence link has changed is not relivant.
14485  *
14486  * Fork - LGPL
14487  * <script type="text/javascript">
14488  */
14489
14490  
14491 /**
14492  * @class Roo.KeyMap
14493  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14494  * The constructor accepts the same config object as defined by {@link #addBinding}.
14495  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14496  * combination it will call the function with this signature (if the match is a multi-key
14497  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14498  * A KeyMap can also handle a string representation of keys.<br />
14499  * Usage:
14500  <pre><code>
14501 // map one key by key code
14502 var map = new Roo.KeyMap("my-element", {
14503     key: 13, // or Roo.EventObject.ENTER
14504     fn: myHandler,
14505     scope: myObject
14506 });
14507
14508 // map multiple keys to one action by string
14509 var map = new Roo.KeyMap("my-element", {
14510     key: "a\r\n\t",
14511     fn: myHandler,
14512     scope: myObject
14513 });
14514
14515 // map multiple keys to multiple actions by strings and array of codes
14516 var map = new Roo.KeyMap("my-element", [
14517     {
14518         key: [10,13],
14519         fn: function(){ alert("Return was pressed"); }
14520     }, {
14521         key: "abc",
14522         fn: function(){ alert('a, b or c was pressed'); }
14523     }, {
14524         key: "\t",
14525         ctrl:true,
14526         shift:true,
14527         fn: function(){ alert('Control + shift + tab was pressed.'); }
14528     }
14529 ]);
14530 </code></pre>
14531  * <b>Note: A KeyMap starts enabled</b>
14532  * @constructor
14533  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14534  * @param {Object} config The config (see {@link #addBinding})
14535  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14536  */
14537 Roo.KeyMap = function(el, config, eventName){
14538     this.el  = Roo.get(el);
14539     this.eventName = eventName || "keydown";
14540     this.bindings = [];
14541     if(config){
14542         this.addBinding(config);
14543     }
14544     this.enable();
14545 };
14546
14547 Roo.KeyMap.prototype = {
14548     /**
14549      * True to stop the event from bubbling and prevent the default browser action if the
14550      * key was handled by the KeyMap (defaults to false)
14551      * @type Boolean
14552      */
14553     stopEvent : false,
14554
14555     /**
14556      * Add a new binding to this KeyMap. The following config object properties are supported:
14557      * <pre>
14558 Property    Type             Description
14559 ----------  ---------------  ----------------------------------------------------------------------
14560 key         String/Array     A single keycode or an array of keycodes to handle
14561 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14562 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14563 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14564 fn          Function         The function to call when KeyMap finds the expected key combination
14565 scope       Object           The scope of the callback function
14566 </pre>
14567      *
14568      * Usage:
14569      * <pre><code>
14570 // Create a KeyMap
14571 var map = new Roo.KeyMap(document, {
14572     key: Roo.EventObject.ENTER,
14573     fn: handleKey,
14574     scope: this
14575 });
14576
14577 //Add a new binding to the existing KeyMap later
14578 map.addBinding({
14579     key: 'abc',
14580     shift: true,
14581     fn: handleKey,
14582     scope: this
14583 });
14584 </code></pre>
14585      * @param {Object/Array} config A single KeyMap config or an array of configs
14586      */
14587         addBinding : function(config){
14588         if(config instanceof Array){
14589             for(var i = 0, len = config.length; i < len; i++){
14590                 this.addBinding(config[i]);
14591             }
14592             return;
14593         }
14594         var keyCode = config.key,
14595             shift = config.shift, 
14596             ctrl = config.ctrl, 
14597             alt = config.alt,
14598             fn = config.fn,
14599             scope = config.scope;
14600         if(typeof keyCode == "string"){
14601             var ks = [];
14602             var keyString = keyCode.toUpperCase();
14603             for(var j = 0, len = keyString.length; j < len; j++){
14604                 ks.push(keyString.charCodeAt(j));
14605             }
14606             keyCode = ks;
14607         }
14608         var keyArray = keyCode instanceof Array;
14609         var handler = function(e){
14610             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14611                 var k = e.getKey();
14612                 if(keyArray){
14613                     for(var i = 0, len = keyCode.length; i < len; i++){
14614                         if(keyCode[i] == k){
14615                           if(this.stopEvent){
14616                               e.stopEvent();
14617                           }
14618                           fn.call(scope || window, k, e);
14619                           return;
14620                         }
14621                     }
14622                 }else{
14623                     if(k == keyCode){
14624                         if(this.stopEvent){
14625                            e.stopEvent();
14626                         }
14627                         fn.call(scope || window, k, e);
14628                     }
14629                 }
14630             }
14631         };
14632         this.bindings.push(handler);  
14633         },
14634
14635     /**
14636      * Shorthand for adding a single key listener
14637      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14638      * following options:
14639      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14640      * @param {Function} fn The function to call
14641      * @param {Object} scope (optional) The scope of the function
14642      */
14643     on : function(key, fn, scope){
14644         var keyCode, shift, ctrl, alt;
14645         if(typeof key == "object" && !(key instanceof Array)){
14646             keyCode = key.key;
14647             shift = key.shift;
14648             ctrl = key.ctrl;
14649             alt = key.alt;
14650         }else{
14651             keyCode = key;
14652         }
14653         this.addBinding({
14654             key: keyCode,
14655             shift: shift,
14656             ctrl: ctrl,
14657             alt: alt,
14658             fn: fn,
14659             scope: scope
14660         })
14661     },
14662
14663     // private
14664     handleKeyDown : function(e){
14665             if(this.enabled){ //just in case
14666             var b = this.bindings;
14667             for(var i = 0, len = b.length; i < len; i++){
14668                 b[i].call(this, e);
14669             }
14670             }
14671         },
14672         
14673         /**
14674          * Returns true if this KeyMap is enabled
14675          * @return {Boolean} 
14676          */
14677         isEnabled : function(){
14678             return this.enabled;  
14679         },
14680         
14681         /**
14682          * Enables this KeyMap
14683          */
14684         enable: function(){
14685                 if(!this.enabled){
14686                     this.el.on(this.eventName, this.handleKeyDown, this);
14687                     this.enabled = true;
14688                 }
14689         },
14690
14691         /**
14692          * Disable this KeyMap
14693          */
14694         disable: function(){
14695                 if(this.enabled){
14696                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14697                     this.enabled = false;
14698                 }
14699         }
14700 };/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711  
14712 /**
14713  * @class Roo.util.TextMetrics
14714  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14715  * wide, in pixels, a given block of text will be.
14716  * @singleton
14717  */
14718 Roo.util.TextMetrics = function(){
14719     var shared;
14720     return {
14721         /**
14722          * Measures the size of the specified text
14723          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14724          * that can affect the size of the rendered text
14725          * @param {String} text The text to measure
14726          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14727          * in order to accurately measure the text height
14728          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14729          */
14730         measure : function(el, text, fixedWidth){
14731             if(!shared){
14732                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14733             }
14734             shared.bind(el);
14735             shared.setFixedWidth(fixedWidth || 'auto');
14736             return shared.getSize(text);
14737         },
14738
14739         /**
14740          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14741          * the overhead of multiple calls to initialize the style properties on each measurement.
14742          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14743          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14744          * in order to accurately measure the text height
14745          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14746          */
14747         createInstance : function(el, fixedWidth){
14748             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14749         }
14750     };
14751 }();
14752
14753  
14754
14755 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14756     var ml = new Roo.Element(document.createElement('div'));
14757     document.body.appendChild(ml.dom);
14758     ml.position('absolute');
14759     ml.setLeftTop(-1000, -1000);
14760     ml.hide();
14761
14762     if(fixedWidth){
14763         ml.setWidth(fixedWidth);
14764     }
14765      
14766     var instance = {
14767         /**
14768          * Returns the size of the specified text based on the internal element's style and width properties
14769          * @memberOf Roo.util.TextMetrics.Instance#
14770          * @param {String} text The text to measure
14771          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14772          */
14773         getSize : function(text){
14774             ml.update(text);
14775             var s = ml.getSize();
14776             ml.update('');
14777             return s;
14778         },
14779
14780         /**
14781          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14782          * that can affect the size of the rendered text
14783          * @memberOf Roo.util.TextMetrics.Instance#
14784          * @param {String/HTMLElement} el The element, dom node or id
14785          */
14786         bind : function(el){
14787             ml.setStyle(
14788                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14789             );
14790         },
14791
14792         /**
14793          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14794          * to set a fixed width in order to accurately measure the text height.
14795          * @memberOf Roo.util.TextMetrics.Instance#
14796          * @param {Number} width The width to set on the element
14797          */
14798         setFixedWidth : function(width){
14799             ml.setWidth(width);
14800         },
14801
14802         /**
14803          * Returns the measured width of the specified text
14804          * @memberOf Roo.util.TextMetrics.Instance#
14805          * @param {String} text The text to measure
14806          * @return {Number} width The width in pixels
14807          */
14808         getWidth : function(text){
14809             ml.dom.style.width = 'auto';
14810             return this.getSize(text).width;
14811         },
14812
14813         /**
14814          * Returns the measured height of the specified text.  For multiline text, be sure to call
14815          * {@link #setFixedWidth} if necessary.
14816          * @memberOf Roo.util.TextMetrics.Instance#
14817          * @param {String} text The text to measure
14818          * @return {Number} height The height in pixels
14819          */
14820         getHeight : function(text){
14821             return this.getSize(text).height;
14822         }
14823     };
14824
14825     instance.bind(bindTo);
14826
14827     return instance;
14828 };
14829
14830 // backwards compat
14831 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14832  * Based on:
14833  * Ext JS Library 1.1.1
14834  * Copyright(c) 2006-2007, Ext JS, LLC.
14835  *
14836  * Originally Released Under LGPL - original licence link has changed is not relivant.
14837  *
14838  * Fork - LGPL
14839  * <script type="text/javascript">
14840  */
14841
14842 /**
14843  * @class Roo.state.Provider
14844  * Abstract base class for state provider implementations. This class provides methods
14845  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14846  * Provider interface.
14847  */
14848 Roo.state.Provider = function(){
14849     /**
14850      * @event statechange
14851      * Fires when a state change occurs.
14852      * @param {Provider} this This state provider
14853      * @param {String} key The state key which was changed
14854      * @param {String} value The encoded value for the state
14855      */
14856     this.addEvents({
14857         "statechange": true
14858     });
14859     this.state = {};
14860     Roo.state.Provider.superclass.constructor.call(this);
14861 };
14862 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14863     /**
14864      * Returns the current value for a key
14865      * @param {String} name The key name
14866      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14867      * @return {Mixed} The state data
14868      */
14869     get : function(name, defaultValue){
14870         return typeof this.state[name] == "undefined" ?
14871             defaultValue : this.state[name];
14872     },
14873     
14874     /**
14875      * Clears a value from the state
14876      * @param {String} name The key name
14877      */
14878     clear : function(name){
14879         delete this.state[name];
14880         this.fireEvent("statechange", this, name, null);
14881     },
14882     
14883     /**
14884      * Sets the value for a key
14885      * @param {String} name The key name
14886      * @param {Mixed} value The value to set
14887      */
14888     set : function(name, value){
14889         this.state[name] = value;
14890         this.fireEvent("statechange", this, name, value);
14891     },
14892     
14893     /**
14894      * Decodes a string previously encoded with {@link #encodeValue}.
14895      * @param {String} value The value to decode
14896      * @return {Mixed} The decoded value
14897      */
14898     decodeValue : function(cookie){
14899         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14900         var matches = re.exec(unescape(cookie));
14901         if(!matches || !matches[1]) {
14902             return; // non state cookie
14903         }
14904         var type = matches[1];
14905         var v = matches[2];
14906         switch(type){
14907             case "n":
14908                 return parseFloat(v);
14909             case "d":
14910                 return new Date(Date.parse(v));
14911             case "b":
14912                 return (v == "1");
14913             case "a":
14914                 var all = [];
14915                 var values = v.split("^");
14916                 for(var i = 0, len = values.length; i < len; i++){
14917                     all.push(this.decodeValue(values[i]));
14918                 }
14919                 return all;
14920            case "o":
14921                 var all = {};
14922                 var values = v.split("^");
14923                 for(var i = 0, len = values.length; i < len; i++){
14924                     var kv = values[i].split("=");
14925                     all[kv[0]] = this.decodeValue(kv[1]);
14926                 }
14927                 return all;
14928            default:
14929                 return v;
14930         }
14931     },
14932     
14933     /**
14934      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14935      * @param {Mixed} value The value to encode
14936      * @return {String} The encoded value
14937      */
14938     encodeValue : function(v){
14939         var enc;
14940         if(typeof v == "number"){
14941             enc = "n:" + v;
14942         }else if(typeof v == "boolean"){
14943             enc = "b:" + (v ? "1" : "0");
14944         }else if(v instanceof Date){
14945             enc = "d:" + v.toGMTString();
14946         }else if(v instanceof Array){
14947             var flat = "";
14948             for(var i = 0, len = v.length; i < len; i++){
14949                 flat += this.encodeValue(v[i]);
14950                 if(i != len-1) {
14951                     flat += "^";
14952                 }
14953             }
14954             enc = "a:" + flat;
14955         }else if(typeof v == "object"){
14956             var flat = "";
14957             for(var key in v){
14958                 if(typeof v[key] != "function"){
14959                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14960                 }
14961             }
14962             enc = "o:" + flat.substring(0, flat.length-1);
14963         }else{
14964             enc = "s:" + v;
14965         }
14966         return escape(enc);        
14967     }
14968 });
14969
14970 /*
14971  * Based on:
14972  * Ext JS Library 1.1.1
14973  * Copyright(c) 2006-2007, Ext JS, LLC.
14974  *
14975  * Originally Released Under LGPL - original licence link has changed is not relivant.
14976  *
14977  * Fork - LGPL
14978  * <script type="text/javascript">
14979  */
14980 /**
14981  * @class Roo.state.Manager
14982  * This is the global state manager. By default all components that are "state aware" check this class
14983  * for state information if you don't pass them a custom state provider. In order for this class
14984  * to be useful, it must be initialized with a provider when your application initializes.
14985  <pre><code>
14986 // in your initialization function
14987 init : function(){
14988    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14989    ...
14990    // supposed you have a {@link Roo.BorderLayout}
14991    var layout = new Roo.BorderLayout(...);
14992    layout.restoreState();
14993    // or a {Roo.BasicDialog}
14994    var dialog = new Roo.BasicDialog(...);
14995    dialog.restoreState();
14996  </code></pre>
14997  * @singleton
14998  */
14999 Roo.state.Manager = function(){
15000     var provider = new Roo.state.Provider();
15001     
15002     return {
15003         /**
15004          * Configures the default state provider for your application
15005          * @param {Provider} stateProvider The state provider to set
15006          */
15007         setProvider : function(stateProvider){
15008             provider = stateProvider;
15009         },
15010         
15011         /**
15012          * Returns the current value for a key
15013          * @param {String} name The key name
15014          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15015          * @return {Mixed} The state data
15016          */
15017         get : function(key, defaultValue){
15018             return provider.get(key, defaultValue);
15019         },
15020         
15021         /**
15022          * Sets the value for a key
15023          * @param {String} name The key name
15024          * @param {Mixed} value The state data
15025          */
15026          set : function(key, value){
15027             provider.set(key, value);
15028         },
15029         
15030         /**
15031          * Clears a value from the state
15032          * @param {String} name The key name
15033          */
15034         clear : function(key){
15035             provider.clear(key);
15036         },
15037         
15038         /**
15039          * Gets the currently configured state provider
15040          * @return {Provider} The state provider
15041          */
15042         getProvider : function(){
15043             return provider;
15044         }
15045     };
15046 }();
15047 /*
15048  * Based on:
15049  * Ext JS Library 1.1.1
15050  * Copyright(c) 2006-2007, Ext JS, LLC.
15051  *
15052  * Originally Released Under LGPL - original licence link has changed is not relivant.
15053  *
15054  * Fork - LGPL
15055  * <script type="text/javascript">
15056  */
15057 /**
15058  * @class Roo.state.CookieProvider
15059  * @extends Roo.state.Provider
15060  * The default Provider implementation which saves state via cookies.
15061  * <br />Usage:
15062  <pre><code>
15063    var cp = new Roo.state.CookieProvider({
15064        path: "/cgi-bin/",
15065        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15066        domain: "roojs.com"
15067    })
15068    Roo.state.Manager.setProvider(cp);
15069  </code></pre>
15070  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15071  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15072  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15073  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15074  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15075  * domain the page is running on including the 'www' like 'www.roojs.com')
15076  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15077  * @constructor
15078  * Create a new CookieProvider
15079  * @param {Object} config The configuration object
15080  */
15081 Roo.state.CookieProvider = function(config){
15082     Roo.state.CookieProvider.superclass.constructor.call(this);
15083     this.path = "/";
15084     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15085     this.domain = null;
15086     this.secure = false;
15087     Roo.apply(this, config);
15088     this.state = this.readCookies();
15089 };
15090
15091 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15092     // private
15093     set : function(name, value){
15094         if(typeof value == "undefined" || value === null){
15095             this.clear(name);
15096             return;
15097         }
15098         this.setCookie(name, value);
15099         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15100     },
15101
15102     // private
15103     clear : function(name){
15104         this.clearCookie(name);
15105         Roo.state.CookieProvider.superclass.clear.call(this, name);
15106     },
15107
15108     // private
15109     readCookies : function(){
15110         var cookies = {};
15111         var c = document.cookie + ";";
15112         var re = /\s?(.*?)=(.*?);/g;
15113         var matches;
15114         while((matches = re.exec(c)) != null){
15115             var name = matches[1];
15116             var value = matches[2];
15117             if(name && name.substring(0,3) == "ys-"){
15118                 cookies[name.substr(3)] = this.decodeValue(value);
15119             }
15120         }
15121         return cookies;
15122     },
15123
15124     // private
15125     setCookie : function(name, value){
15126         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15127            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15128            ((this.path == null) ? "" : ("; path=" + this.path)) +
15129            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15130            ((this.secure == true) ? "; secure" : "");
15131     },
15132
15133     // private
15134     clearCookie : function(name){
15135         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15136            ((this.path == null) ? "" : ("; path=" + this.path)) +
15137            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15138            ((this.secure == true) ? "; secure" : "");
15139     }
15140 });/*
15141  * Based on:
15142  * Ext JS Library 1.1.1
15143  * Copyright(c) 2006-2007, Ext JS, LLC.
15144  *
15145  * Originally Released Under LGPL - original licence link has changed is not relivant.
15146  *
15147  * Fork - LGPL
15148  * <script type="text/javascript">
15149  */
15150  
15151
15152 /**
15153  * @class Roo.ComponentMgr
15154  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15155  * @singleton
15156  */
15157 Roo.ComponentMgr = function(){
15158     var all = new Roo.util.MixedCollection();
15159
15160     return {
15161         /**
15162          * Registers a component.
15163          * @param {Roo.Component} c The component
15164          */
15165         register : function(c){
15166             all.add(c);
15167         },
15168
15169         /**
15170          * Unregisters a component.
15171          * @param {Roo.Component} c The component
15172          */
15173         unregister : function(c){
15174             all.remove(c);
15175         },
15176
15177         /**
15178          * Returns a component by id
15179          * @param {String} id The component id
15180          */
15181         get : function(id){
15182             return all.get(id);
15183         },
15184
15185         /**
15186          * Registers a function that will be called when a specified component is added to ComponentMgr
15187          * @param {String} id The component id
15188          * @param {Funtction} fn The callback function
15189          * @param {Object} scope The scope of the callback
15190          */
15191         onAvailable : function(id, fn, scope){
15192             all.on("add", function(index, o){
15193                 if(o.id == id){
15194                     fn.call(scope || o, o);
15195                     all.un("add", fn, scope);
15196                 }
15197             });
15198         }
15199     };
15200 }();/*
15201  * Based on:
15202  * Ext JS Library 1.1.1
15203  * Copyright(c) 2006-2007, Ext JS, LLC.
15204  *
15205  * Originally Released Under LGPL - original licence link has changed is not relivant.
15206  *
15207  * Fork - LGPL
15208  * <script type="text/javascript">
15209  */
15210  
15211 /**
15212  * @class Roo.Component
15213  * @extends Roo.util.Observable
15214  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15215  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15216  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15217  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15218  * All visual components (widgets) that require rendering into a layout should subclass Component.
15219  * @constructor
15220  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15221  * 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
15222  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15223  */
15224 Roo.Component = function(config){
15225     config = config || {};
15226     if(config.tagName || config.dom || typeof config == "string"){ // element object
15227         config = {el: config, id: config.id || config};
15228     }
15229     this.initialConfig = config;
15230
15231     Roo.apply(this, config);
15232     this.addEvents({
15233         /**
15234          * @event disable
15235          * Fires after the component is disabled.
15236              * @param {Roo.Component} this
15237              */
15238         disable : true,
15239         /**
15240          * @event enable
15241          * Fires after the component is enabled.
15242              * @param {Roo.Component} this
15243              */
15244         enable : true,
15245         /**
15246          * @event beforeshow
15247          * Fires before the component is shown.  Return false to stop the show.
15248              * @param {Roo.Component} this
15249              */
15250         beforeshow : true,
15251         /**
15252          * @event show
15253          * Fires after the component is shown.
15254              * @param {Roo.Component} this
15255              */
15256         show : true,
15257         /**
15258          * @event beforehide
15259          * Fires before the component is hidden. Return false to stop the hide.
15260              * @param {Roo.Component} this
15261              */
15262         beforehide : true,
15263         /**
15264          * @event hide
15265          * Fires after the component is hidden.
15266              * @param {Roo.Component} this
15267              */
15268         hide : true,
15269         /**
15270          * @event beforerender
15271          * Fires before the component is rendered. Return false to stop the render.
15272              * @param {Roo.Component} this
15273              */
15274         beforerender : true,
15275         /**
15276          * @event render
15277          * Fires after the component is rendered.
15278              * @param {Roo.Component} this
15279              */
15280         render : true,
15281         /**
15282          * @event beforedestroy
15283          * Fires before the component is destroyed. Return false to stop the destroy.
15284              * @param {Roo.Component} this
15285              */
15286         beforedestroy : true,
15287         /**
15288          * @event destroy
15289          * Fires after the component is destroyed.
15290              * @param {Roo.Component} this
15291              */
15292         destroy : true
15293     });
15294     if(!this.id){
15295         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15296     }
15297     Roo.ComponentMgr.register(this);
15298     Roo.Component.superclass.constructor.call(this);
15299     this.initComponent();
15300     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15301         this.render(this.renderTo);
15302         delete this.renderTo;
15303     }
15304 };
15305
15306 /** @private */
15307 Roo.Component.AUTO_ID = 1000;
15308
15309 Roo.extend(Roo.Component, Roo.util.Observable, {
15310     /**
15311      * @scope Roo.Component.prototype
15312      * @type {Boolean}
15313      * true if this component is hidden. Read-only.
15314      */
15315     hidden : false,
15316     /**
15317      * @type {Boolean}
15318      * true if this component is disabled. Read-only.
15319      */
15320     disabled : false,
15321     /**
15322      * @type {Boolean}
15323      * true if this component has been rendered. Read-only.
15324      */
15325     rendered : false,
15326     
15327     /** @cfg {String} disableClass
15328      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15329      */
15330     disabledClass : "x-item-disabled",
15331         /** @cfg {Boolean} allowDomMove
15332          * Whether the component can move the Dom node when rendering (defaults to true).
15333          */
15334     allowDomMove : true,
15335     /** @cfg {String} hideMode (display|visibility)
15336      * How this component should hidden. Supported values are
15337      * "visibility" (css visibility), "offsets" (negative offset position) and
15338      * "display" (css display) - defaults to "display".
15339      */
15340     hideMode: 'display',
15341
15342     /** @private */
15343     ctype : "Roo.Component",
15344
15345     /**
15346      * @cfg {String} actionMode 
15347      * which property holds the element that used for  hide() / show() / disable() / enable()
15348      * default is 'el' 
15349      */
15350     actionMode : "el",
15351
15352     /** @private */
15353     getActionEl : function(){
15354         return this[this.actionMode];
15355     },
15356
15357     initComponent : Roo.emptyFn,
15358     /**
15359      * If this is a lazy rendering component, render it to its container element.
15360      * @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.
15361      */
15362     render : function(container, position){
15363         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15364             if(!container && this.el){
15365                 this.el = Roo.get(this.el);
15366                 container = this.el.dom.parentNode;
15367                 this.allowDomMove = false;
15368             }
15369             this.container = Roo.get(container);
15370             this.rendered = true;
15371             if(position !== undefined){
15372                 if(typeof position == 'number'){
15373                     position = this.container.dom.childNodes[position];
15374                 }else{
15375                     position = Roo.getDom(position);
15376                 }
15377             }
15378             this.onRender(this.container, position || null);
15379             if(this.cls){
15380                 this.el.addClass(this.cls);
15381                 delete this.cls;
15382             }
15383             if(this.style){
15384                 this.el.applyStyles(this.style);
15385                 delete this.style;
15386             }
15387             this.fireEvent("render", this);
15388             this.afterRender(this.container);
15389             if(this.hidden){
15390                 this.hide();
15391             }
15392             if(this.disabled){
15393                 this.disable();
15394             }
15395         }
15396         return this;
15397     },
15398
15399     /** @private */
15400     // default function is not really useful
15401     onRender : function(ct, position){
15402         if(this.el){
15403             this.el = Roo.get(this.el);
15404             if(this.allowDomMove !== false){
15405                 ct.dom.insertBefore(this.el.dom, position);
15406             }
15407         }
15408     },
15409
15410     /** @private */
15411     getAutoCreate : function(){
15412         var cfg = typeof this.autoCreate == "object" ?
15413                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15414         if(this.id && !cfg.id){
15415             cfg.id = this.id;
15416         }
15417         return cfg;
15418     },
15419
15420     /** @private */
15421     afterRender : Roo.emptyFn,
15422
15423     /**
15424      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15425      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15426      */
15427     destroy : function(){
15428         if(this.fireEvent("beforedestroy", this) !== false){
15429             this.purgeListeners();
15430             this.beforeDestroy();
15431             if(this.rendered){
15432                 this.el.removeAllListeners();
15433                 this.el.remove();
15434                 if(this.actionMode == "container"){
15435                     this.container.remove();
15436                 }
15437             }
15438             this.onDestroy();
15439             Roo.ComponentMgr.unregister(this);
15440             this.fireEvent("destroy", this);
15441         }
15442     },
15443
15444         /** @private */
15445     beforeDestroy : function(){
15446
15447     },
15448
15449         /** @private */
15450         onDestroy : function(){
15451
15452     },
15453
15454     /**
15455      * Returns the underlying {@link Roo.Element}.
15456      * @return {Roo.Element} The element
15457      */
15458     getEl : function(){
15459         return this.el;
15460     },
15461
15462     /**
15463      * Returns the id of this component.
15464      * @return {String}
15465      */
15466     getId : function(){
15467         return this.id;
15468     },
15469
15470     /**
15471      * Try to focus this component.
15472      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15473      * @return {Roo.Component} this
15474      */
15475     focus : function(selectText){
15476         if(this.rendered){
15477             this.el.focus();
15478             if(selectText === true){
15479                 this.el.dom.select();
15480             }
15481         }
15482         return this;
15483     },
15484
15485     /** @private */
15486     blur : function(){
15487         if(this.rendered){
15488             this.el.blur();
15489         }
15490         return this;
15491     },
15492
15493     /**
15494      * Disable this component.
15495      * @return {Roo.Component} this
15496      */
15497     disable : function(){
15498         if(this.rendered){
15499             this.onDisable();
15500         }
15501         this.disabled = true;
15502         this.fireEvent("disable", this);
15503         return this;
15504     },
15505
15506         // private
15507     onDisable : function(){
15508         this.getActionEl().addClass(this.disabledClass);
15509         this.el.dom.disabled = true;
15510     },
15511
15512     /**
15513      * Enable this component.
15514      * @return {Roo.Component} this
15515      */
15516     enable : function(){
15517         if(this.rendered){
15518             this.onEnable();
15519         }
15520         this.disabled = false;
15521         this.fireEvent("enable", this);
15522         return this;
15523     },
15524
15525         // private
15526     onEnable : function(){
15527         this.getActionEl().removeClass(this.disabledClass);
15528         this.el.dom.disabled = false;
15529     },
15530
15531     /**
15532      * Convenience function for setting disabled/enabled by boolean.
15533      * @param {Boolean} disabled
15534      */
15535     setDisabled : function(disabled){
15536         this[disabled ? "disable" : "enable"]();
15537     },
15538
15539     /**
15540      * Show this component.
15541      * @return {Roo.Component} this
15542      */
15543     show: function(){
15544         if(this.fireEvent("beforeshow", this) !== false){
15545             this.hidden = false;
15546             if(this.rendered){
15547                 this.onShow();
15548             }
15549             this.fireEvent("show", this);
15550         }
15551         return this;
15552     },
15553
15554     // private
15555     onShow : function(){
15556         var ae = this.getActionEl();
15557         if(this.hideMode == 'visibility'){
15558             ae.dom.style.visibility = "visible";
15559         }else if(this.hideMode == 'offsets'){
15560             ae.removeClass('x-hidden');
15561         }else{
15562             ae.dom.style.display = "";
15563         }
15564     },
15565
15566     /**
15567      * Hide this component.
15568      * @return {Roo.Component} this
15569      */
15570     hide: function(){
15571         if(this.fireEvent("beforehide", this) !== false){
15572             this.hidden = true;
15573             if(this.rendered){
15574                 this.onHide();
15575             }
15576             this.fireEvent("hide", this);
15577         }
15578         return this;
15579     },
15580
15581     // private
15582     onHide : function(){
15583         var ae = this.getActionEl();
15584         if(this.hideMode == 'visibility'){
15585             ae.dom.style.visibility = "hidden";
15586         }else if(this.hideMode == 'offsets'){
15587             ae.addClass('x-hidden');
15588         }else{
15589             ae.dom.style.display = "none";
15590         }
15591     },
15592
15593     /**
15594      * Convenience function to hide or show this component by boolean.
15595      * @param {Boolean} visible True to show, false to hide
15596      * @return {Roo.Component} this
15597      */
15598     setVisible: function(visible){
15599         if(visible) {
15600             this.show();
15601         }else{
15602             this.hide();
15603         }
15604         return this;
15605     },
15606
15607     /**
15608      * Returns true if this component is visible.
15609      */
15610     isVisible : function(){
15611         return this.getActionEl().isVisible();
15612     },
15613
15614     cloneConfig : function(overrides){
15615         overrides = overrides || {};
15616         var id = overrides.id || Roo.id();
15617         var cfg = Roo.applyIf(overrides, this.initialConfig);
15618         cfg.id = id; // prevent dup id
15619         return new this.constructor(cfg);
15620     }
15621 });/*
15622  * Based on:
15623  * Ext JS Library 1.1.1
15624  * Copyright(c) 2006-2007, Ext JS, LLC.
15625  *
15626  * Originally Released Under LGPL - original licence link has changed is not relivant.
15627  *
15628  * Fork - LGPL
15629  * <script type="text/javascript">
15630  */
15631
15632 /**
15633  * @class Roo.BoxComponent
15634  * @extends Roo.Component
15635  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15636  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15637  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15638  * layout containers.
15639  * @constructor
15640  * @param {Roo.Element/String/Object} config The configuration options.
15641  */
15642 Roo.BoxComponent = function(config){
15643     Roo.Component.call(this, config);
15644     this.addEvents({
15645         /**
15646          * @event resize
15647          * Fires after the component is resized.
15648              * @param {Roo.Component} this
15649              * @param {Number} adjWidth The box-adjusted width that was set
15650              * @param {Number} adjHeight The box-adjusted height that was set
15651              * @param {Number} rawWidth The width that was originally specified
15652              * @param {Number} rawHeight The height that was originally specified
15653              */
15654         resize : true,
15655         /**
15656          * @event move
15657          * Fires after the component is moved.
15658              * @param {Roo.Component} this
15659              * @param {Number} x The new x position
15660              * @param {Number} y The new y position
15661              */
15662         move : true
15663     });
15664 };
15665
15666 Roo.extend(Roo.BoxComponent, Roo.Component, {
15667     // private, set in afterRender to signify that the component has been rendered
15668     boxReady : false,
15669     // private, used to defer height settings to subclasses
15670     deferHeight: false,
15671     /** @cfg {Number} width
15672      * width (optional) size of component
15673      */
15674      /** @cfg {Number} height
15675      * height (optional) size of component
15676      */
15677      
15678     /**
15679      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15680      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15681      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15682      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15683      * @return {Roo.BoxComponent} this
15684      */
15685     setSize : function(w, h){
15686         // support for standard size objects
15687         if(typeof w == 'object'){
15688             h = w.height;
15689             w = w.width;
15690         }
15691         // not rendered
15692         if(!this.boxReady){
15693             this.width = w;
15694             this.height = h;
15695             return this;
15696         }
15697
15698         // prevent recalcs when not needed
15699         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15700             return this;
15701         }
15702         this.lastSize = {width: w, height: h};
15703
15704         var adj = this.adjustSize(w, h);
15705         var aw = adj.width, ah = adj.height;
15706         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15707             var rz = this.getResizeEl();
15708             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15709                 rz.setSize(aw, ah);
15710             }else if(!this.deferHeight && ah !== undefined){
15711                 rz.setHeight(ah);
15712             }else if(aw !== undefined){
15713                 rz.setWidth(aw);
15714             }
15715             this.onResize(aw, ah, w, h);
15716             this.fireEvent('resize', this, aw, ah, w, h);
15717         }
15718         return this;
15719     },
15720
15721     /**
15722      * Gets the current size of the component's underlying element.
15723      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15724      */
15725     getSize : function(){
15726         return this.el.getSize();
15727     },
15728
15729     /**
15730      * Gets the current XY position of the component's underlying element.
15731      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15732      * @return {Array} The XY position of the element (e.g., [100, 200])
15733      */
15734     getPosition : function(local){
15735         if(local === true){
15736             return [this.el.getLeft(true), this.el.getTop(true)];
15737         }
15738         return this.xy || this.el.getXY();
15739     },
15740
15741     /**
15742      * Gets the current box measurements of the component's underlying element.
15743      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15744      * @returns {Object} box An object in the format {x, y, width, height}
15745      */
15746     getBox : function(local){
15747         var s = this.el.getSize();
15748         if(local){
15749             s.x = this.el.getLeft(true);
15750             s.y = this.el.getTop(true);
15751         }else{
15752             var xy = this.xy || this.el.getXY();
15753             s.x = xy[0];
15754             s.y = xy[1];
15755         }
15756         return s;
15757     },
15758
15759     /**
15760      * Sets the current box measurements of the component's underlying element.
15761      * @param {Object} box An object in the format {x, y, width, height}
15762      * @returns {Roo.BoxComponent} this
15763      */
15764     updateBox : function(box){
15765         this.setSize(box.width, box.height);
15766         this.setPagePosition(box.x, box.y);
15767         return this;
15768     },
15769
15770     // protected
15771     getResizeEl : function(){
15772         return this.resizeEl || this.el;
15773     },
15774
15775     // protected
15776     getPositionEl : function(){
15777         return this.positionEl || this.el;
15778     },
15779
15780     /**
15781      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15782      * This method fires the move event.
15783      * @param {Number} left The new left
15784      * @param {Number} top The new top
15785      * @returns {Roo.BoxComponent} this
15786      */
15787     setPosition : function(x, y){
15788         this.x = x;
15789         this.y = y;
15790         if(!this.boxReady){
15791             return this;
15792         }
15793         var adj = this.adjustPosition(x, y);
15794         var ax = adj.x, ay = adj.y;
15795
15796         var el = this.getPositionEl();
15797         if(ax !== undefined || ay !== undefined){
15798             if(ax !== undefined && ay !== undefined){
15799                 el.setLeftTop(ax, ay);
15800             }else if(ax !== undefined){
15801                 el.setLeft(ax);
15802             }else if(ay !== undefined){
15803                 el.setTop(ay);
15804             }
15805             this.onPosition(ax, ay);
15806             this.fireEvent('move', this, ax, ay);
15807         }
15808         return this;
15809     },
15810
15811     /**
15812      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15813      * This method fires the move event.
15814      * @param {Number} x The new x position
15815      * @param {Number} y The new y position
15816      * @returns {Roo.BoxComponent} this
15817      */
15818     setPagePosition : function(x, y){
15819         this.pageX = x;
15820         this.pageY = y;
15821         if(!this.boxReady){
15822             return;
15823         }
15824         if(x === undefined || y === undefined){ // cannot translate undefined points
15825             return;
15826         }
15827         var p = this.el.translatePoints(x, y);
15828         this.setPosition(p.left, p.top);
15829         return this;
15830     },
15831
15832     // private
15833     onRender : function(ct, position){
15834         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15835         if(this.resizeEl){
15836             this.resizeEl = Roo.get(this.resizeEl);
15837         }
15838         if(this.positionEl){
15839             this.positionEl = Roo.get(this.positionEl);
15840         }
15841     },
15842
15843     // private
15844     afterRender : function(){
15845         Roo.BoxComponent.superclass.afterRender.call(this);
15846         this.boxReady = true;
15847         this.setSize(this.width, this.height);
15848         if(this.x || this.y){
15849             this.setPosition(this.x, this.y);
15850         }
15851         if(this.pageX || this.pageY){
15852             this.setPagePosition(this.pageX, this.pageY);
15853         }
15854     },
15855
15856     /**
15857      * Force the component's size to recalculate based on the underlying element's current height and width.
15858      * @returns {Roo.BoxComponent} this
15859      */
15860     syncSize : function(){
15861         delete this.lastSize;
15862         this.setSize(this.el.getWidth(), this.el.getHeight());
15863         return this;
15864     },
15865
15866     /**
15867      * Called after the component is resized, this method is empty by default but can be implemented by any
15868      * subclass that needs to perform custom logic after a resize occurs.
15869      * @param {Number} adjWidth The box-adjusted width that was set
15870      * @param {Number} adjHeight The box-adjusted height that was set
15871      * @param {Number} rawWidth The width that was originally specified
15872      * @param {Number} rawHeight The height that was originally specified
15873      */
15874     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15875
15876     },
15877
15878     /**
15879      * Called after the component is moved, this method is empty by default but can be implemented by any
15880      * subclass that needs to perform custom logic after a move occurs.
15881      * @param {Number} x The new x position
15882      * @param {Number} y The new y position
15883      */
15884     onPosition : function(x, y){
15885
15886     },
15887
15888     // private
15889     adjustSize : function(w, h){
15890         if(this.autoWidth){
15891             w = 'auto';
15892         }
15893         if(this.autoHeight){
15894             h = 'auto';
15895         }
15896         return {width : w, height: h};
15897     },
15898
15899     // private
15900     adjustPosition : function(x, y){
15901         return {x : x, y: y};
15902     }
15903 });/*
15904  * Original code for Roojs - LGPL
15905  * <script type="text/javascript">
15906  */
15907  
15908 /**
15909  * @class Roo.XComponent
15910  * A delayed Element creator...
15911  * Or a way to group chunks of interface together.
15912  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15913  *  used in conjunction with XComponent.build() it will create an instance of each element,
15914  *  then call addxtype() to build the User interface.
15915  * 
15916  * Mypart.xyx = new Roo.XComponent({
15917
15918     parent : 'Mypart.xyz', // empty == document.element.!!
15919     order : '001',
15920     name : 'xxxx'
15921     region : 'xxxx'
15922     disabled : function() {} 
15923      
15924     tree : function() { // return an tree of xtype declared components
15925         var MODULE = this;
15926         return 
15927         {
15928             xtype : 'NestedLayoutPanel',
15929             // technicall
15930         }
15931      ]
15932  *})
15933  *
15934  *
15935  * It can be used to build a big heiracy, with parent etc.
15936  * or you can just use this to render a single compoent to a dom element
15937  * MYPART.render(Roo.Element | String(id) | dom_element )
15938  *
15939  *
15940  * Usage patterns.
15941  *
15942  * Classic Roo
15943  *
15944  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15945  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15946  *
15947  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15948  *
15949  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15950  * - if mulitple topModules exist, the last one is defined as the top module.
15951  *
15952  * Embeded Roo
15953  * 
15954  * When the top level or multiple modules are to embedded into a existing HTML page,
15955  * the parent element can container '#id' of the element where the module will be drawn.
15956  *
15957  * Bootstrap Roo
15958  *
15959  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15960  * it relies more on a include mechanism, where sub modules are included into an outer page.
15961  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15962  * 
15963  * Bootstrap Roo Included elements
15964  *
15965  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15966  * hence confusing the component builder as it thinks there are multiple top level elements. 
15967  *
15968  * 
15969  * 
15970  * @extends Roo.util.Observable
15971  * @constructor
15972  * @param cfg {Object} configuration of component
15973  * 
15974  */
15975 Roo.XComponent = function(cfg) {
15976     Roo.apply(this, cfg);
15977     this.addEvents({ 
15978         /**
15979              * @event built
15980              * Fires when this the componnt is built
15981              * @param {Roo.XComponent} c the component
15982              */
15983         'built' : true
15984         
15985     });
15986     this.region = this.region || 'center'; // default..
15987     Roo.XComponent.register(this);
15988     this.modules = false;
15989     this.el = false; // where the layout goes..
15990     
15991     
15992 }
15993 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15994     /**
15995      * @property el
15996      * The created element (with Roo.factory())
15997      * @type {Roo.Layout}
15998      */
15999     el  : false,
16000     
16001     /**
16002      * @property el
16003      * for BC  - use el in new code
16004      * @type {Roo.Layout}
16005      */
16006     panel : false,
16007     
16008     /**
16009      * @property layout
16010      * for BC  - use el in new code
16011      * @type {Roo.Layout}
16012      */
16013     layout : false,
16014     
16015      /**
16016      * @cfg {Function|boolean} disabled
16017      * If this module is disabled by some rule, return true from the funtion
16018      */
16019     disabled : false,
16020     
16021     /**
16022      * @cfg {String} parent 
16023      * Name of parent element which it get xtype added to..
16024      */
16025     parent: false,
16026     
16027     /**
16028      * @cfg {String} order
16029      * Used to set the order in which elements are created (usefull for multiple tabs)
16030      */
16031     
16032     order : false,
16033     /**
16034      * @cfg {String} name
16035      * String to display while loading.
16036      */
16037     name : false,
16038     /**
16039      * @cfg {String} region
16040      * Region to render component to (defaults to center)
16041      */
16042     region : 'center',
16043     
16044     /**
16045      * @cfg {Array} items
16046      * A single item array - the first element is the root of the tree..
16047      * It's done this way to stay compatible with the Xtype system...
16048      */
16049     items : false,
16050     
16051     /**
16052      * @property _tree
16053      * The method that retuns the tree of parts that make up this compoennt 
16054      * @type {function}
16055      */
16056     _tree  : false,
16057     
16058      /**
16059      * render
16060      * render element to dom or tree
16061      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16062      */
16063     
16064     render : function(el)
16065     {
16066         
16067         el = el || false;
16068         var hp = this.parent ? 1 : 0;
16069         Roo.debug &&  Roo.log(this);
16070         
16071         var tree = this._tree ? this._tree() : this.tree();
16072
16073         
16074         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16075             // if parent is a '#.....' string, then let's use that..
16076             var ename = this.parent.substr(1);
16077             this.parent = false;
16078             Roo.debug && Roo.log(ename);
16079             switch (ename) {
16080                 case 'bootstrap-body':
16081                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16082                         // this is the BorderLayout standard?
16083                        this.parent = { el : true };
16084                        break;
16085                     }
16086                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16087                         // need to insert stuff...
16088                         this.parent =  {
16089                              el : new Roo.bootstrap.layout.Border({
16090                                  el : document.body, 
16091                      
16092                                  center: {
16093                                     titlebar: false,
16094                                     autoScroll:false,
16095                                     closeOnTab: true,
16096                                     tabPosition: 'top',
16097                                       //resizeTabs: true,
16098                                     alwaysShowTabs: true,
16099                                     hideTabs: false
16100                                      //minTabWidth: 140
16101                                  }
16102                              })
16103                         
16104                          };
16105                          break;
16106                     }
16107                          
16108                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16109                         this.parent = { el :  new  Roo.bootstrap.Body() };
16110                         Roo.debug && Roo.log("setting el to doc body");
16111                          
16112                     } else {
16113                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16114                     }
16115                     break;
16116                 case 'bootstrap':
16117                     this.parent = { el : true};
16118                     // fall through
16119                 default:
16120                     el = Roo.get(ename);
16121                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16122                         this.parent = { el : true};
16123                     }
16124                     
16125                     break;
16126             }
16127                 
16128             
16129             if (!el && !this.parent) {
16130                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16131                 return;
16132             }
16133         }
16134         
16135         Roo.debug && Roo.log("EL:");
16136         Roo.debug && Roo.log(el);
16137         Roo.debug && Roo.log("this.parent.el:");
16138         Roo.debug && Roo.log(this.parent.el);
16139         
16140
16141         // altertive root elements ??? - we need a better way to indicate these.
16142         var is_alt = Roo.XComponent.is_alt ||
16143                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16144                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16145                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16146         
16147         
16148         
16149         if (!this.parent && is_alt) {
16150             //el = Roo.get(document.body);
16151             this.parent = { el : true };
16152         }
16153             
16154             
16155         
16156         if (!this.parent) {
16157             
16158             Roo.debug && Roo.log("no parent - creating one");
16159             
16160             el = el ? Roo.get(el) : false;      
16161             
16162             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16163                 
16164                 this.parent =  {
16165                     el : new Roo.bootstrap.layout.Border({
16166                         el: el || document.body,
16167                     
16168                         center: {
16169                             titlebar: false,
16170                             autoScroll:false,
16171                             closeOnTab: true,
16172                             tabPosition: 'top',
16173                              //resizeTabs: true,
16174                             alwaysShowTabs: false,
16175                             hideTabs: true,
16176                             minTabWidth: 140,
16177                             overflow: 'visible'
16178                          }
16179                      })
16180                 };
16181             } else {
16182             
16183                 // it's a top level one..
16184                 this.parent =  {
16185                     el : new Roo.BorderLayout(el || document.body, {
16186                         center: {
16187                             titlebar: false,
16188                             autoScroll:false,
16189                             closeOnTab: true,
16190                             tabPosition: 'top',
16191                              //resizeTabs: true,
16192                             alwaysShowTabs: el && hp? false :  true,
16193                             hideTabs: el || !hp ? true :  false,
16194                             minTabWidth: 140
16195                          }
16196                     })
16197                 };
16198             }
16199         }
16200         
16201         if (!this.parent.el) {
16202                 // probably an old style ctor, which has been disabled.
16203                 return;
16204
16205         }
16206                 // The 'tree' method is  '_tree now' 
16207             
16208         tree.region = tree.region || this.region;
16209         var is_body = false;
16210         if (this.parent.el === true) {
16211             // bootstrap... - body..
16212             if (el) {
16213                 tree.el = el;
16214             }
16215             this.parent.el = Roo.factory(tree);
16216             is_body = true;
16217         }
16218         
16219         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16220         this.fireEvent('built', this);
16221         
16222         this.panel = this.el;
16223         this.layout = this.panel.layout;
16224         this.parentLayout = this.parent.layout  || false;  
16225          
16226     }
16227     
16228 });
16229
16230 Roo.apply(Roo.XComponent, {
16231     /**
16232      * @property  hideProgress
16233      * true to disable the building progress bar.. usefull on single page renders.
16234      * @type Boolean
16235      */
16236     hideProgress : false,
16237     /**
16238      * @property  buildCompleted
16239      * True when the builder has completed building the interface.
16240      * @type Boolean
16241      */
16242     buildCompleted : false,
16243      
16244     /**
16245      * @property  topModule
16246      * the upper most module - uses document.element as it's constructor.
16247      * @type Object
16248      */
16249      
16250     topModule  : false,
16251       
16252     /**
16253      * @property  modules
16254      * array of modules to be created by registration system.
16255      * @type {Array} of Roo.XComponent
16256      */
16257     
16258     modules : [],
16259     /**
16260      * @property  elmodules
16261      * array of modules to be created by which use #ID 
16262      * @type {Array} of Roo.XComponent
16263      */
16264      
16265     elmodules : [],
16266
16267      /**
16268      * @property  is_alt
16269      * Is an alternative Root - normally used by bootstrap or other systems,
16270      *    where the top element in the tree can wrap 'body' 
16271      * @type {boolean}  (default false)
16272      */
16273      
16274     is_alt : false,
16275     /**
16276      * @property  build_from_html
16277      * Build elements from html - used by bootstrap HTML stuff 
16278      *    - this is cleared after build is completed
16279      * @type {boolean}    (default false)
16280      */
16281      
16282     build_from_html : false,
16283     /**
16284      * Register components to be built later.
16285      *
16286      * This solves the following issues
16287      * - Building is not done on page load, but after an authentication process has occured.
16288      * - Interface elements are registered on page load
16289      * - Parent Interface elements may not be loaded before child, so this handles that..
16290      * 
16291      *
16292      * example:
16293      * 
16294      * MyApp.register({
16295           order : '000001',
16296           module : 'Pman.Tab.projectMgr',
16297           region : 'center',
16298           parent : 'Pman.layout',
16299           disabled : false,  // or use a function..
16300         })
16301      
16302      * * @param {Object} details about module
16303      */
16304     register : function(obj) {
16305                 
16306         Roo.XComponent.event.fireEvent('register', obj);
16307         switch(typeof(obj.disabled) ) {
16308                 
16309             case 'undefined':
16310                 break;
16311             
16312             case 'function':
16313                 if ( obj.disabled() ) {
16314                         return;
16315                 }
16316                 break;
16317             
16318             default:
16319                 if (obj.disabled) {
16320                         return;
16321                 }
16322                 break;
16323         }
16324                 
16325         this.modules.push(obj);
16326          
16327     },
16328     /**
16329      * convert a string to an object..
16330      * eg. 'AAA.BBB' -> finds AAA.BBB
16331
16332      */
16333     
16334     toObject : function(str)
16335     {
16336         if (!str || typeof(str) == 'object') {
16337             return str;
16338         }
16339         if (str.substring(0,1) == '#') {
16340             return str;
16341         }
16342
16343         var ar = str.split('.');
16344         var rt, o;
16345         rt = ar.shift();
16346             /** eval:var:o */
16347         try {
16348             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16349         } catch (e) {
16350             throw "Module not found : " + str;
16351         }
16352         
16353         if (o === false) {
16354             throw "Module not found : " + str;
16355         }
16356         Roo.each(ar, function(e) {
16357             if (typeof(o[e]) == 'undefined') {
16358                 throw "Module not found : " + str;
16359             }
16360             o = o[e];
16361         });
16362         
16363         return o;
16364         
16365     },
16366     
16367     
16368     /**
16369      * move modules into their correct place in the tree..
16370      * 
16371      */
16372     preBuild : function ()
16373     {
16374         var _t = this;
16375         Roo.each(this.modules , function (obj)
16376         {
16377             Roo.XComponent.event.fireEvent('beforebuild', obj);
16378             
16379             var opar = obj.parent;
16380             try { 
16381                 obj.parent = this.toObject(opar);
16382             } catch(e) {
16383                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16384                 return;
16385             }
16386             
16387             if (!obj.parent) {
16388                 Roo.debug && Roo.log("GOT top level module");
16389                 Roo.debug && Roo.log(obj);
16390                 obj.modules = new Roo.util.MixedCollection(false, 
16391                     function(o) { return o.order + '' }
16392                 );
16393                 this.topModule = obj;
16394                 return;
16395             }
16396                         // parent is a string (usually a dom element name..)
16397             if (typeof(obj.parent) == 'string') {
16398                 this.elmodules.push(obj);
16399                 return;
16400             }
16401             if (obj.parent.constructor != Roo.XComponent) {
16402                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16403             }
16404             if (!obj.parent.modules) {
16405                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16406                     function(o) { return o.order + '' }
16407                 );
16408             }
16409             if (obj.parent.disabled) {
16410                 obj.disabled = true;
16411             }
16412             obj.parent.modules.add(obj);
16413         }, this);
16414     },
16415     
16416      /**
16417      * make a list of modules to build.
16418      * @return {Array} list of modules. 
16419      */ 
16420     
16421     buildOrder : function()
16422     {
16423         var _this = this;
16424         var cmp = function(a,b) {   
16425             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16426         };
16427         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16428             throw "No top level modules to build";
16429         }
16430         
16431         // make a flat list in order of modules to build.
16432         var mods = this.topModule ? [ this.topModule ] : [];
16433                 
16434         
16435         // elmodules (is a list of DOM based modules )
16436         Roo.each(this.elmodules, function(e) {
16437             mods.push(e);
16438             if (!this.topModule &&
16439                 typeof(e.parent) == 'string' &&
16440                 e.parent.substring(0,1) == '#' &&
16441                 Roo.get(e.parent.substr(1))
16442                ) {
16443                 
16444                 _this.topModule = e;
16445             }
16446             
16447         });
16448
16449         
16450         // add modules to their parents..
16451         var addMod = function(m) {
16452             Roo.debug && Roo.log("build Order: add: " + m.name);
16453                 
16454             mods.push(m);
16455             if (m.modules && !m.disabled) {
16456                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16457                 m.modules.keySort('ASC',  cmp );
16458                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16459     
16460                 m.modules.each(addMod);
16461             } else {
16462                 Roo.debug && Roo.log("build Order: no child modules");
16463             }
16464             // not sure if this is used any more..
16465             if (m.finalize) {
16466                 m.finalize.name = m.name + " (clean up) ";
16467                 mods.push(m.finalize);
16468             }
16469             
16470         }
16471         if (this.topModule && this.topModule.modules) { 
16472             this.topModule.modules.keySort('ASC',  cmp );
16473             this.topModule.modules.each(addMod);
16474         } 
16475         return mods;
16476     },
16477     
16478      /**
16479      * Build the registered modules.
16480      * @param {Object} parent element.
16481      * @param {Function} optional method to call after module has been added.
16482      * 
16483      */ 
16484    
16485     build : function(opts) 
16486     {
16487         
16488         if (typeof(opts) != 'undefined') {
16489             Roo.apply(this,opts);
16490         }
16491         
16492         this.preBuild();
16493         var mods = this.buildOrder();
16494       
16495         //this.allmods = mods;
16496         //Roo.debug && Roo.log(mods);
16497         //return;
16498         if (!mods.length) { // should not happen
16499             throw "NO modules!!!";
16500         }
16501         
16502         
16503         var msg = "Building Interface...";
16504         // flash it up as modal - so we store the mask!?
16505         if (!this.hideProgress && Roo.MessageBox) {
16506             Roo.MessageBox.show({ title: 'loading' });
16507             Roo.MessageBox.show({
16508                title: "Please wait...",
16509                msg: msg,
16510                width:450,
16511                progress:true,
16512                closable:false,
16513                modal: false
16514               
16515             });
16516         }
16517         var total = mods.length;
16518         
16519         var _this = this;
16520         var progressRun = function() {
16521             if (!mods.length) {
16522                 Roo.debug && Roo.log('hide?');
16523                 if (!this.hideProgress && Roo.MessageBox) {
16524                     Roo.MessageBox.hide();
16525                 }
16526                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16527                 
16528                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16529                 
16530                 // THE END...
16531                 return false;   
16532             }
16533             
16534             var m = mods.shift();
16535             
16536             
16537             Roo.debug && Roo.log(m);
16538             // not sure if this is supported any more.. - modules that are are just function
16539             if (typeof(m) == 'function') { 
16540                 m.call(this);
16541                 return progressRun.defer(10, _this);
16542             } 
16543             
16544             
16545             msg = "Building Interface " + (total  - mods.length) + 
16546                     " of " + total + 
16547                     (m.name ? (' - ' + m.name) : '');
16548                         Roo.debug && Roo.log(msg);
16549             if (!_this.hideProgress &&  Roo.MessageBox) { 
16550                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16551             }
16552             
16553          
16554             // is the module disabled?
16555             var disabled = (typeof(m.disabled) == 'function') ?
16556                 m.disabled.call(m.module.disabled) : m.disabled;    
16557             
16558             
16559             if (disabled) {
16560                 return progressRun(); // we do not update the display!
16561             }
16562             
16563             // now build 
16564             
16565                         
16566                         
16567             m.render();
16568             // it's 10 on top level, and 1 on others??? why...
16569             return progressRun.defer(10, _this);
16570              
16571         }
16572         progressRun.defer(1, _this);
16573      
16574         
16575         
16576     },
16577         
16578         
16579         /**
16580          * Event Object.
16581          *
16582          *
16583          */
16584         event: false, 
16585     /**
16586          * wrapper for event.on - aliased later..  
16587          * Typically use to register a event handler for register:
16588          *
16589          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16590          *
16591          */
16592     on : false
16593    
16594     
16595     
16596 });
16597
16598 Roo.XComponent.event = new Roo.util.Observable({
16599                 events : { 
16600                         /**
16601                          * @event register
16602                          * Fires when an Component is registered,
16603                          * set the disable property on the Component to stop registration.
16604                          * @param {Roo.XComponent} c the component being registerd.
16605                          * 
16606                          */
16607                         'register' : true,
16608             /**
16609                          * @event beforebuild
16610                          * Fires before each Component is built
16611                          * can be used to apply permissions.
16612                          * @param {Roo.XComponent} c the component being registerd.
16613                          * 
16614                          */
16615                         'beforebuild' : true,
16616                         /**
16617                          * @event buildcomplete
16618                          * Fires on the top level element when all elements have been built
16619                          * @param {Roo.XComponent} the top level component.
16620                          */
16621                         'buildcomplete' : true
16622                         
16623                 }
16624 });
16625
16626 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16627  //
16628  /**
16629  * marked - a markdown parser
16630  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16631  * https://github.com/chjj/marked
16632  */
16633
16634
16635 /**
16636  *
16637  * Roo.Markdown - is a very crude wrapper around marked..
16638  *
16639  * usage:
16640  * 
16641  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16642  * 
16643  * Note: move the sample code to the bottom of this
16644  * file before uncommenting it.
16645  *
16646  */
16647
16648 Roo.Markdown = {};
16649 Roo.Markdown.toHtml = function(text) {
16650     
16651     var c = new Roo.Markdown.marked.setOptions({
16652             renderer: new Roo.Markdown.marked.Renderer(),
16653             gfm: true,
16654             tables: true,
16655             breaks: false,
16656             pedantic: false,
16657             sanitize: false,
16658             smartLists: true,
16659             smartypants: false
16660           });
16661     // A FEW HACKS!!?
16662     
16663     text = text.replace(/\\\n/g,' ');
16664     return Roo.Markdown.marked(text);
16665 };
16666 //
16667 // converter
16668 //
16669 // Wraps all "globals" so that the only thing
16670 // exposed is makeHtml().
16671 //
16672 (function() {
16673     
16674     /**
16675      * Block-Level Grammar
16676      */
16677     
16678     var block = {
16679       newline: /^\n+/,
16680       code: /^( {4}[^\n]+\n*)+/,
16681       fences: noop,
16682       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16683       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16684       nptable: noop,
16685       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16686       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16687       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16688       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16689       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16690       table: noop,
16691       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16692       text: /^[^\n]+/
16693     };
16694     
16695     block.bullet = /(?:[*+-]|\d+\.)/;
16696     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16697     block.item = replace(block.item, 'gm')
16698       (/bull/g, block.bullet)
16699       ();
16700     
16701     block.list = replace(block.list)
16702       (/bull/g, block.bullet)
16703       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16704       ('def', '\\n+(?=' + block.def.source + ')')
16705       ();
16706     
16707     block.blockquote = replace(block.blockquote)
16708       ('def', block.def)
16709       ();
16710     
16711     block._tag = '(?!(?:'
16712       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16713       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16714       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16715     
16716     block.html = replace(block.html)
16717       ('comment', /<!--[\s\S]*?-->/)
16718       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16719       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16720       (/tag/g, block._tag)
16721       ();
16722     
16723     block.paragraph = replace(block.paragraph)
16724       ('hr', block.hr)
16725       ('heading', block.heading)
16726       ('lheading', block.lheading)
16727       ('blockquote', block.blockquote)
16728       ('tag', '<' + block._tag)
16729       ('def', block.def)
16730       ();
16731     
16732     /**
16733      * Normal Block Grammar
16734      */
16735     
16736     block.normal = merge({}, block);
16737     
16738     /**
16739      * GFM Block Grammar
16740      */
16741     
16742     block.gfm = merge({}, block.normal, {
16743       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16744       paragraph: /^/,
16745       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16746     });
16747     
16748     block.gfm.paragraph = replace(block.paragraph)
16749       ('(?!', '(?!'
16750         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16751         + block.list.source.replace('\\1', '\\3') + '|')
16752       ();
16753     
16754     /**
16755      * GFM + Tables Block Grammar
16756      */
16757     
16758     block.tables = merge({}, block.gfm, {
16759       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16760       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16761     });
16762     
16763     /**
16764      * Block Lexer
16765      */
16766     
16767     function Lexer(options) {
16768       this.tokens = [];
16769       this.tokens.links = {};
16770       this.options = options || marked.defaults;
16771       this.rules = block.normal;
16772     
16773       if (this.options.gfm) {
16774         if (this.options.tables) {
16775           this.rules = block.tables;
16776         } else {
16777           this.rules = block.gfm;
16778         }
16779       }
16780     }
16781     
16782     /**
16783      * Expose Block Rules
16784      */
16785     
16786     Lexer.rules = block;
16787     
16788     /**
16789      * Static Lex Method
16790      */
16791     
16792     Lexer.lex = function(src, options) {
16793       var lexer = new Lexer(options);
16794       return lexer.lex(src);
16795     };
16796     
16797     /**
16798      * Preprocessing
16799      */
16800     
16801     Lexer.prototype.lex = function(src) {
16802       src = src
16803         .replace(/\r\n|\r/g, '\n')
16804         .replace(/\t/g, '    ')
16805         .replace(/\u00a0/g, ' ')
16806         .replace(/\u2424/g, '\n');
16807     
16808       return this.token(src, true);
16809     };
16810     
16811     /**
16812      * Lexing
16813      */
16814     
16815     Lexer.prototype.token = function(src, top, bq) {
16816       var src = src.replace(/^ +$/gm, '')
16817         , next
16818         , loose
16819         , cap
16820         , bull
16821         , b
16822         , item
16823         , space
16824         , i
16825         , l;
16826     
16827       while (src) {
16828         // newline
16829         if (cap = this.rules.newline.exec(src)) {
16830           src = src.substring(cap[0].length);
16831           if (cap[0].length > 1) {
16832             this.tokens.push({
16833               type: 'space'
16834             });
16835           }
16836         }
16837     
16838         // code
16839         if (cap = this.rules.code.exec(src)) {
16840           src = src.substring(cap[0].length);
16841           cap = cap[0].replace(/^ {4}/gm, '');
16842           this.tokens.push({
16843             type: 'code',
16844             text: !this.options.pedantic
16845               ? cap.replace(/\n+$/, '')
16846               : cap
16847           });
16848           continue;
16849         }
16850     
16851         // fences (gfm)
16852         if (cap = this.rules.fences.exec(src)) {
16853           src = src.substring(cap[0].length);
16854           this.tokens.push({
16855             type: 'code',
16856             lang: cap[2],
16857             text: cap[3] || ''
16858           });
16859           continue;
16860         }
16861     
16862         // heading
16863         if (cap = this.rules.heading.exec(src)) {
16864           src = src.substring(cap[0].length);
16865           this.tokens.push({
16866             type: 'heading',
16867             depth: cap[1].length,
16868             text: cap[2]
16869           });
16870           continue;
16871         }
16872     
16873         // table no leading pipe (gfm)
16874         if (top && (cap = this.rules.nptable.exec(src))) {
16875           src = src.substring(cap[0].length);
16876     
16877           item = {
16878             type: 'table',
16879             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16880             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16881             cells: cap[3].replace(/\n$/, '').split('\n')
16882           };
16883     
16884           for (i = 0; i < item.align.length; i++) {
16885             if (/^ *-+: *$/.test(item.align[i])) {
16886               item.align[i] = 'right';
16887             } else if (/^ *:-+: *$/.test(item.align[i])) {
16888               item.align[i] = 'center';
16889             } else if (/^ *:-+ *$/.test(item.align[i])) {
16890               item.align[i] = 'left';
16891             } else {
16892               item.align[i] = null;
16893             }
16894           }
16895     
16896           for (i = 0; i < item.cells.length; i++) {
16897             item.cells[i] = item.cells[i].split(/ *\| */);
16898           }
16899     
16900           this.tokens.push(item);
16901     
16902           continue;
16903         }
16904     
16905         // lheading
16906         if (cap = this.rules.lheading.exec(src)) {
16907           src = src.substring(cap[0].length);
16908           this.tokens.push({
16909             type: 'heading',
16910             depth: cap[2] === '=' ? 1 : 2,
16911             text: cap[1]
16912           });
16913           continue;
16914         }
16915     
16916         // hr
16917         if (cap = this.rules.hr.exec(src)) {
16918           src = src.substring(cap[0].length);
16919           this.tokens.push({
16920             type: 'hr'
16921           });
16922           continue;
16923         }
16924     
16925         // blockquote
16926         if (cap = this.rules.blockquote.exec(src)) {
16927           src = src.substring(cap[0].length);
16928     
16929           this.tokens.push({
16930             type: 'blockquote_start'
16931           });
16932     
16933           cap = cap[0].replace(/^ *> ?/gm, '');
16934     
16935           // Pass `top` to keep the current
16936           // "toplevel" state. This is exactly
16937           // how markdown.pl works.
16938           this.token(cap, top, true);
16939     
16940           this.tokens.push({
16941             type: 'blockquote_end'
16942           });
16943     
16944           continue;
16945         }
16946     
16947         // list
16948         if (cap = this.rules.list.exec(src)) {
16949           src = src.substring(cap[0].length);
16950           bull = cap[2];
16951     
16952           this.tokens.push({
16953             type: 'list_start',
16954             ordered: bull.length > 1
16955           });
16956     
16957           // Get each top-level item.
16958           cap = cap[0].match(this.rules.item);
16959     
16960           next = false;
16961           l = cap.length;
16962           i = 0;
16963     
16964           for (; i < l; i++) {
16965             item = cap[i];
16966     
16967             // Remove the list item's bullet
16968             // so it is seen as the next token.
16969             space = item.length;
16970             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16971     
16972             // Outdent whatever the
16973             // list item contains. Hacky.
16974             if (~item.indexOf('\n ')) {
16975               space -= item.length;
16976               item = !this.options.pedantic
16977                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16978                 : item.replace(/^ {1,4}/gm, '');
16979             }
16980     
16981             // Determine whether the next list item belongs here.
16982             // Backpedal if it does not belong in this list.
16983             if (this.options.smartLists && i !== l - 1) {
16984               b = block.bullet.exec(cap[i + 1])[0];
16985               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16986                 src = cap.slice(i + 1).join('\n') + src;
16987                 i = l - 1;
16988               }
16989             }
16990     
16991             // Determine whether item is loose or not.
16992             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16993             // for discount behavior.
16994             loose = next || /\n\n(?!\s*$)/.test(item);
16995             if (i !== l - 1) {
16996               next = item.charAt(item.length - 1) === '\n';
16997               if (!loose) { loose = next; }
16998             }
16999     
17000             this.tokens.push({
17001               type: loose
17002                 ? 'loose_item_start'
17003                 : 'list_item_start'
17004             });
17005     
17006             // Recurse.
17007             this.token(item, false, bq);
17008     
17009             this.tokens.push({
17010               type: 'list_item_end'
17011             });
17012           }
17013     
17014           this.tokens.push({
17015             type: 'list_end'
17016           });
17017     
17018           continue;
17019         }
17020     
17021         // html
17022         if (cap = this.rules.html.exec(src)) {
17023           src = src.substring(cap[0].length);
17024           this.tokens.push({
17025             type: this.options.sanitize
17026               ? 'paragraph'
17027               : 'html',
17028             pre: !this.options.sanitizer
17029               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17030             text: cap[0]
17031           });
17032           continue;
17033         }
17034     
17035         // def
17036         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17037           src = src.substring(cap[0].length);
17038           this.tokens.links[cap[1].toLowerCase()] = {
17039             href: cap[2],
17040             title: cap[3]
17041           };
17042           continue;
17043         }
17044     
17045         // table (gfm)
17046         if (top && (cap = this.rules.table.exec(src))) {
17047           src = src.substring(cap[0].length);
17048     
17049           item = {
17050             type: 'table',
17051             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17052             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17053             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17054           };
17055     
17056           for (i = 0; i < item.align.length; i++) {
17057             if (/^ *-+: *$/.test(item.align[i])) {
17058               item.align[i] = 'right';
17059             } else if (/^ *:-+: *$/.test(item.align[i])) {
17060               item.align[i] = 'center';
17061             } else if (/^ *:-+ *$/.test(item.align[i])) {
17062               item.align[i] = 'left';
17063             } else {
17064               item.align[i] = null;
17065             }
17066           }
17067     
17068           for (i = 0; i < item.cells.length; i++) {
17069             item.cells[i] = item.cells[i]
17070               .replace(/^ *\| *| *\| *$/g, '')
17071               .split(/ *\| */);
17072           }
17073     
17074           this.tokens.push(item);
17075     
17076           continue;
17077         }
17078     
17079         // top-level paragraph
17080         if (top && (cap = this.rules.paragraph.exec(src))) {
17081           src = src.substring(cap[0].length);
17082           this.tokens.push({
17083             type: 'paragraph',
17084             text: cap[1].charAt(cap[1].length - 1) === '\n'
17085               ? cap[1].slice(0, -1)
17086               : cap[1]
17087           });
17088           continue;
17089         }
17090     
17091         // text
17092         if (cap = this.rules.text.exec(src)) {
17093           // Top-level should never reach here.
17094           src = src.substring(cap[0].length);
17095           this.tokens.push({
17096             type: 'text',
17097             text: cap[0]
17098           });
17099           continue;
17100         }
17101     
17102         if (src) {
17103           throw new
17104             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17105         }
17106       }
17107     
17108       return this.tokens;
17109     };
17110     
17111     /**
17112      * Inline-Level Grammar
17113      */
17114     
17115     var inline = {
17116       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17117       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17118       url: noop,
17119       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17120       link: /^!?\[(inside)\]\(href\)/,
17121       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17122       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17123       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17124       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17125       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17126       br: /^ {2,}\n(?!\s*$)/,
17127       del: noop,
17128       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17129     };
17130     
17131     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17132     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17133     
17134     inline.link = replace(inline.link)
17135       ('inside', inline._inside)
17136       ('href', inline._href)
17137       ();
17138     
17139     inline.reflink = replace(inline.reflink)
17140       ('inside', inline._inside)
17141       ();
17142     
17143     /**
17144      * Normal Inline Grammar
17145      */
17146     
17147     inline.normal = merge({}, inline);
17148     
17149     /**
17150      * Pedantic Inline Grammar
17151      */
17152     
17153     inline.pedantic = merge({}, inline.normal, {
17154       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17155       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17156     });
17157     
17158     /**
17159      * GFM Inline Grammar
17160      */
17161     
17162     inline.gfm = merge({}, inline.normal, {
17163       escape: replace(inline.escape)('])', '~|])')(),
17164       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17165       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17166       text: replace(inline.text)
17167         (']|', '~]|')
17168         ('|', '|https?://|')
17169         ()
17170     });
17171     
17172     /**
17173      * GFM + Line Breaks Inline Grammar
17174      */
17175     
17176     inline.breaks = merge({}, inline.gfm, {
17177       br: replace(inline.br)('{2,}', '*')(),
17178       text: replace(inline.gfm.text)('{2,}', '*')()
17179     });
17180     
17181     /**
17182      * Inline Lexer & Compiler
17183      */
17184     
17185     function InlineLexer(links, options) {
17186       this.options = options || marked.defaults;
17187       this.links = links;
17188       this.rules = inline.normal;
17189       this.renderer = this.options.renderer || new Renderer;
17190       this.renderer.options = this.options;
17191     
17192       if (!this.links) {
17193         throw new
17194           Error('Tokens array requires a `links` property.');
17195       }
17196     
17197       if (this.options.gfm) {
17198         if (this.options.breaks) {
17199           this.rules = inline.breaks;
17200         } else {
17201           this.rules = inline.gfm;
17202         }
17203       } else if (this.options.pedantic) {
17204         this.rules = inline.pedantic;
17205       }
17206     }
17207     
17208     /**
17209      * Expose Inline Rules
17210      */
17211     
17212     InlineLexer.rules = inline;
17213     
17214     /**
17215      * Static Lexing/Compiling Method
17216      */
17217     
17218     InlineLexer.output = function(src, links, options) {
17219       var inline = new InlineLexer(links, options);
17220       return inline.output(src);
17221     };
17222     
17223     /**
17224      * Lexing/Compiling
17225      */
17226     
17227     InlineLexer.prototype.output = function(src) {
17228       var out = ''
17229         , link
17230         , text
17231         , href
17232         , cap;
17233     
17234       while (src) {
17235         // escape
17236         if (cap = this.rules.escape.exec(src)) {
17237           src = src.substring(cap[0].length);
17238           out += cap[1];
17239           continue;
17240         }
17241     
17242         // autolink
17243         if (cap = this.rules.autolink.exec(src)) {
17244           src = src.substring(cap[0].length);
17245           if (cap[2] === '@') {
17246             text = cap[1].charAt(6) === ':'
17247               ? this.mangle(cap[1].substring(7))
17248               : this.mangle(cap[1]);
17249             href = this.mangle('mailto:') + text;
17250           } else {
17251             text = escape(cap[1]);
17252             href = text;
17253           }
17254           out += this.renderer.link(href, null, text);
17255           continue;
17256         }
17257     
17258         // url (gfm)
17259         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17260           src = src.substring(cap[0].length);
17261           text = escape(cap[1]);
17262           href = text;
17263           out += this.renderer.link(href, null, text);
17264           continue;
17265         }
17266     
17267         // tag
17268         if (cap = this.rules.tag.exec(src)) {
17269           if (!this.inLink && /^<a /i.test(cap[0])) {
17270             this.inLink = true;
17271           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17272             this.inLink = false;
17273           }
17274           src = src.substring(cap[0].length);
17275           out += this.options.sanitize
17276             ? this.options.sanitizer
17277               ? this.options.sanitizer(cap[0])
17278               : escape(cap[0])
17279             : cap[0];
17280           continue;
17281         }
17282     
17283         // link
17284         if (cap = this.rules.link.exec(src)) {
17285           src = src.substring(cap[0].length);
17286           this.inLink = true;
17287           out += this.outputLink(cap, {
17288             href: cap[2],
17289             title: cap[3]
17290           });
17291           this.inLink = false;
17292           continue;
17293         }
17294     
17295         // reflink, nolink
17296         if ((cap = this.rules.reflink.exec(src))
17297             || (cap = this.rules.nolink.exec(src))) {
17298           src = src.substring(cap[0].length);
17299           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17300           link = this.links[link.toLowerCase()];
17301           if (!link || !link.href) {
17302             out += cap[0].charAt(0);
17303             src = cap[0].substring(1) + src;
17304             continue;
17305           }
17306           this.inLink = true;
17307           out += this.outputLink(cap, link);
17308           this.inLink = false;
17309           continue;
17310         }
17311     
17312         // strong
17313         if (cap = this.rules.strong.exec(src)) {
17314           src = src.substring(cap[0].length);
17315           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17316           continue;
17317         }
17318     
17319         // em
17320         if (cap = this.rules.em.exec(src)) {
17321           src = src.substring(cap[0].length);
17322           out += this.renderer.em(this.output(cap[2] || cap[1]));
17323           continue;
17324         }
17325     
17326         // code
17327         if (cap = this.rules.code.exec(src)) {
17328           src = src.substring(cap[0].length);
17329           out += this.renderer.codespan(escape(cap[2], true));
17330           continue;
17331         }
17332     
17333         // br
17334         if (cap = this.rules.br.exec(src)) {
17335           src = src.substring(cap[0].length);
17336           out += this.renderer.br();
17337           continue;
17338         }
17339     
17340         // del (gfm)
17341         if (cap = this.rules.del.exec(src)) {
17342           src = src.substring(cap[0].length);
17343           out += this.renderer.del(this.output(cap[1]));
17344           continue;
17345         }
17346     
17347         // text
17348         if (cap = this.rules.text.exec(src)) {
17349           src = src.substring(cap[0].length);
17350           out += this.renderer.text(escape(this.smartypants(cap[0])));
17351           continue;
17352         }
17353     
17354         if (src) {
17355           throw new
17356             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17357         }
17358       }
17359     
17360       return out;
17361     };
17362     
17363     /**
17364      * Compile Link
17365      */
17366     
17367     InlineLexer.prototype.outputLink = function(cap, link) {
17368       var href = escape(link.href)
17369         , title = link.title ? escape(link.title) : null;
17370     
17371       return cap[0].charAt(0) !== '!'
17372         ? this.renderer.link(href, title, this.output(cap[1]))
17373         : this.renderer.image(href, title, escape(cap[1]));
17374     };
17375     
17376     /**
17377      * Smartypants Transformations
17378      */
17379     
17380     InlineLexer.prototype.smartypants = function(text) {
17381       if (!this.options.smartypants)  { return text; }
17382       return text
17383         // em-dashes
17384         .replace(/---/g, '\u2014')
17385         // en-dashes
17386         .replace(/--/g, '\u2013')
17387         // opening singles
17388         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17389         // closing singles & apostrophes
17390         .replace(/'/g, '\u2019')
17391         // opening doubles
17392         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17393         // closing doubles
17394         .replace(/"/g, '\u201d')
17395         // ellipses
17396         .replace(/\.{3}/g, '\u2026');
17397     };
17398     
17399     /**
17400      * Mangle Links
17401      */
17402     
17403     InlineLexer.prototype.mangle = function(text) {
17404       if (!this.options.mangle) { return text; }
17405       var out = ''
17406         , l = text.length
17407         , i = 0
17408         , ch;
17409     
17410       for (; i < l; i++) {
17411         ch = text.charCodeAt(i);
17412         if (Math.random() > 0.5) {
17413           ch = 'x' + ch.toString(16);
17414         }
17415         out += '&#' + ch + ';';
17416       }
17417     
17418       return out;
17419     };
17420     
17421     /**
17422      * Renderer
17423      */
17424     
17425     function Renderer(options) {
17426       this.options = options || {};
17427     }
17428     
17429     Renderer.prototype.code = function(code, lang, escaped) {
17430       if (this.options.highlight) {
17431         var out = this.options.highlight(code, lang);
17432         if (out != null && out !== code) {
17433           escaped = true;
17434           code = out;
17435         }
17436       } else {
17437             // hack!!! - it's already escapeD?
17438             escaped = true;
17439       }
17440     
17441       if (!lang) {
17442         return '<pre><code>'
17443           + (escaped ? code : escape(code, true))
17444           + '\n</code></pre>';
17445       }
17446     
17447       return '<pre><code class="'
17448         + this.options.langPrefix
17449         + escape(lang, true)
17450         + '">'
17451         + (escaped ? code : escape(code, true))
17452         + '\n</code></pre>\n';
17453     };
17454     
17455     Renderer.prototype.blockquote = function(quote) {
17456       return '<blockquote>\n' + quote + '</blockquote>\n';
17457     };
17458     
17459     Renderer.prototype.html = function(html) {
17460       return html;
17461     };
17462     
17463     Renderer.prototype.heading = function(text, level, raw) {
17464       return '<h'
17465         + level
17466         + ' id="'
17467         + this.options.headerPrefix
17468         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17469         + '">'
17470         + text
17471         + '</h'
17472         + level
17473         + '>\n';
17474     };
17475     
17476     Renderer.prototype.hr = function() {
17477       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17478     };
17479     
17480     Renderer.prototype.list = function(body, ordered) {
17481       var type = ordered ? 'ol' : 'ul';
17482       return '<' + type + '>\n' + body + '</' + type + '>\n';
17483     };
17484     
17485     Renderer.prototype.listitem = function(text) {
17486       return '<li>' + text + '</li>\n';
17487     };
17488     
17489     Renderer.prototype.paragraph = function(text) {
17490       return '<p>' + text + '</p>\n';
17491     };
17492     
17493     Renderer.prototype.table = function(header, body) {
17494       return '<table class="table table-striped">\n'
17495         + '<thead>\n'
17496         + header
17497         + '</thead>\n'
17498         + '<tbody>\n'
17499         + body
17500         + '</tbody>\n'
17501         + '</table>\n';
17502     };
17503     
17504     Renderer.prototype.tablerow = function(content) {
17505       return '<tr>\n' + content + '</tr>\n';
17506     };
17507     
17508     Renderer.prototype.tablecell = function(content, flags) {
17509       var type = flags.header ? 'th' : 'td';
17510       var tag = flags.align
17511         ? '<' + type + ' style="text-align:' + flags.align + '">'
17512         : '<' + type + '>';
17513       return tag + content + '</' + type + '>\n';
17514     };
17515     
17516     // span level renderer
17517     Renderer.prototype.strong = function(text) {
17518       return '<strong>' + text + '</strong>';
17519     };
17520     
17521     Renderer.prototype.em = function(text) {
17522       return '<em>' + text + '</em>';
17523     };
17524     
17525     Renderer.prototype.codespan = function(text) {
17526       return '<code>' + text + '</code>';
17527     };
17528     
17529     Renderer.prototype.br = function() {
17530       return this.options.xhtml ? '<br/>' : '<br>';
17531     };
17532     
17533     Renderer.prototype.del = function(text) {
17534       return '<del>' + text + '</del>';
17535     };
17536     
17537     Renderer.prototype.link = function(href, title, text) {
17538       if (this.options.sanitize) {
17539         try {
17540           var prot = decodeURIComponent(unescape(href))
17541             .replace(/[^\w:]/g, '')
17542             .toLowerCase();
17543         } catch (e) {
17544           return '';
17545         }
17546         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17547           return '';
17548         }
17549       }
17550       var out = '<a href="' + href + '"';
17551       if (title) {
17552         out += ' title="' + title + '"';
17553       }
17554       out += '>' + text + '</a>';
17555       return out;
17556     };
17557     
17558     Renderer.prototype.image = function(href, title, text) {
17559       var out = '<img src="' + href + '" alt="' + text + '"';
17560       if (title) {
17561         out += ' title="' + title + '"';
17562       }
17563       out += this.options.xhtml ? '/>' : '>';
17564       return out;
17565     };
17566     
17567     Renderer.prototype.text = function(text) {
17568       return text;
17569     };
17570     
17571     /**
17572      * Parsing & Compiling
17573      */
17574     
17575     function Parser(options) {
17576       this.tokens = [];
17577       this.token = null;
17578       this.options = options || marked.defaults;
17579       this.options.renderer = this.options.renderer || new Renderer;
17580       this.renderer = this.options.renderer;
17581       this.renderer.options = this.options;
17582     }
17583     
17584     /**
17585      * Static Parse Method
17586      */
17587     
17588     Parser.parse = function(src, options, renderer) {
17589       var parser = new Parser(options, renderer);
17590       return parser.parse(src);
17591     };
17592     
17593     /**
17594      * Parse Loop
17595      */
17596     
17597     Parser.prototype.parse = function(src) {
17598       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17599       this.tokens = src.reverse();
17600     
17601       var out = '';
17602       while (this.next()) {
17603         out += this.tok();
17604       }
17605     
17606       return out;
17607     };
17608     
17609     /**
17610      * Next Token
17611      */
17612     
17613     Parser.prototype.next = function() {
17614       return this.token = this.tokens.pop();
17615     };
17616     
17617     /**
17618      * Preview Next Token
17619      */
17620     
17621     Parser.prototype.peek = function() {
17622       return this.tokens[this.tokens.length - 1] || 0;
17623     };
17624     
17625     /**
17626      * Parse Text Tokens
17627      */
17628     
17629     Parser.prototype.parseText = function() {
17630       var body = this.token.text;
17631     
17632       while (this.peek().type === 'text') {
17633         body += '\n' + this.next().text;
17634       }
17635     
17636       return this.inline.output(body);
17637     };
17638     
17639     /**
17640      * Parse Current Token
17641      */
17642     
17643     Parser.prototype.tok = function() {
17644       switch (this.token.type) {
17645         case 'space': {
17646           return '';
17647         }
17648         case 'hr': {
17649           return this.renderer.hr();
17650         }
17651         case 'heading': {
17652           return this.renderer.heading(
17653             this.inline.output(this.token.text),
17654             this.token.depth,
17655             this.token.text);
17656         }
17657         case 'code': {
17658           return this.renderer.code(this.token.text,
17659             this.token.lang,
17660             this.token.escaped);
17661         }
17662         case 'table': {
17663           var header = ''
17664             , body = ''
17665             , i
17666             , row
17667             , cell
17668             , flags
17669             , j;
17670     
17671           // header
17672           cell = '';
17673           for (i = 0; i < this.token.header.length; i++) {
17674             flags = { header: true, align: this.token.align[i] };
17675             cell += this.renderer.tablecell(
17676               this.inline.output(this.token.header[i]),
17677               { header: true, align: this.token.align[i] }
17678             );
17679           }
17680           header += this.renderer.tablerow(cell);
17681     
17682           for (i = 0; i < this.token.cells.length; i++) {
17683             row = this.token.cells[i];
17684     
17685             cell = '';
17686             for (j = 0; j < row.length; j++) {
17687               cell += this.renderer.tablecell(
17688                 this.inline.output(row[j]),
17689                 { header: false, align: this.token.align[j] }
17690               );
17691             }
17692     
17693             body += this.renderer.tablerow(cell);
17694           }
17695           return this.renderer.table(header, body);
17696         }
17697         case 'blockquote_start': {
17698           var body = '';
17699     
17700           while (this.next().type !== 'blockquote_end') {
17701             body += this.tok();
17702           }
17703     
17704           return this.renderer.blockquote(body);
17705         }
17706         case 'list_start': {
17707           var body = ''
17708             , ordered = this.token.ordered;
17709     
17710           while (this.next().type !== 'list_end') {
17711             body += this.tok();
17712           }
17713     
17714           return this.renderer.list(body, ordered);
17715         }
17716         case 'list_item_start': {
17717           var body = '';
17718     
17719           while (this.next().type !== 'list_item_end') {
17720             body += this.token.type === 'text'
17721               ? this.parseText()
17722               : this.tok();
17723           }
17724     
17725           return this.renderer.listitem(body);
17726         }
17727         case 'loose_item_start': {
17728           var body = '';
17729     
17730           while (this.next().type !== 'list_item_end') {
17731             body += this.tok();
17732           }
17733     
17734           return this.renderer.listitem(body);
17735         }
17736         case 'html': {
17737           var html = !this.token.pre && !this.options.pedantic
17738             ? this.inline.output(this.token.text)
17739             : this.token.text;
17740           return this.renderer.html(html);
17741         }
17742         case 'paragraph': {
17743           return this.renderer.paragraph(this.inline.output(this.token.text));
17744         }
17745         case 'text': {
17746           return this.renderer.paragraph(this.parseText());
17747         }
17748       }
17749     };
17750     
17751     /**
17752      * Helpers
17753      */
17754     
17755     function escape(html, encode) {
17756       return html
17757         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17758         .replace(/</g, '&lt;')
17759         .replace(/>/g, '&gt;')
17760         .replace(/"/g, '&quot;')
17761         .replace(/'/g, '&#39;');
17762     }
17763     
17764     function unescape(html) {
17765         // explicitly match decimal, hex, and named HTML entities 
17766       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17767         n = n.toLowerCase();
17768         if (n === 'colon') { return ':'; }
17769         if (n.charAt(0) === '#') {
17770           return n.charAt(1) === 'x'
17771             ? String.fromCharCode(parseInt(n.substring(2), 16))
17772             : String.fromCharCode(+n.substring(1));
17773         }
17774         return '';
17775       });
17776     }
17777     
17778     function replace(regex, opt) {
17779       regex = regex.source;
17780       opt = opt || '';
17781       return function self(name, val) {
17782         if (!name) { return new RegExp(regex, opt); }
17783         val = val.source || val;
17784         val = val.replace(/(^|[^\[])\^/g, '$1');
17785         regex = regex.replace(name, val);
17786         return self;
17787       };
17788     }
17789     
17790     function noop() {}
17791     noop.exec = noop;
17792     
17793     function merge(obj) {
17794       var i = 1
17795         , target
17796         , key;
17797     
17798       for (; i < arguments.length; i++) {
17799         target = arguments[i];
17800         for (key in target) {
17801           if (Object.prototype.hasOwnProperty.call(target, key)) {
17802             obj[key] = target[key];
17803           }
17804         }
17805       }
17806     
17807       return obj;
17808     }
17809     
17810     
17811     /**
17812      * Marked
17813      */
17814     
17815     function marked(src, opt, callback) {
17816       if (callback || typeof opt === 'function') {
17817         if (!callback) {
17818           callback = opt;
17819           opt = null;
17820         }
17821     
17822         opt = merge({}, marked.defaults, opt || {});
17823     
17824         var highlight = opt.highlight
17825           , tokens
17826           , pending
17827           , i = 0;
17828     
17829         try {
17830           tokens = Lexer.lex(src, opt)
17831         } catch (e) {
17832           return callback(e);
17833         }
17834     
17835         pending = tokens.length;
17836     
17837         var done = function(err) {
17838           if (err) {
17839             opt.highlight = highlight;
17840             return callback(err);
17841           }
17842     
17843           var out;
17844     
17845           try {
17846             out = Parser.parse(tokens, opt);
17847           } catch (e) {
17848             err = e;
17849           }
17850     
17851           opt.highlight = highlight;
17852     
17853           return err
17854             ? callback(err)
17855             : callback(null, out);
17856         };
17857     
17858         if (!highlight || highlight.length < 3) {
17859           return done();
17860         }
17861     
17862         delete opt.highlight;
17863     
17864         if (!pending) { return done(); }
17865     
17866         for (; i < tokens.length; i++) {
17867           (function(token) {
17868             if (token.type !== 'code') {
17869               return --pending || done();
17870             }
17871             return highlight(token.text, token.lang, function(err, code) {
17872               if (err) { return done(err); }
17873               if (code == null || code === token.text) {
17874                 return --pending || done();
17875               }
17876               token.text = code;
17877               token.escaped = true;
17878               --pending || done();
17879             });
17880           })(tokens[i]);
17881         }
17882     
17883         return;
17884       }
17885       try {
17886         if (opt) { opt = merge({}, marked.defaults, opt); }
17887         return Parser.parse(Lexer.lex(src, opt), opt);
17888       } catch (e) {
17889         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17890         if ((opt || marked.defaults).silent) {
17891           return '<p>An error occured:</p><pre>'
17892             + escape(e.message + '', true)
17893             + '</pre>';
17894         }
17895         throw e;
17896       }
17897     }
17898     
17899     /**
17900      * Options
17901      */
17902     
17903     marked.options =
17904     marked.setOptions = function(opt) {
17905       merge(marked.defaults, opt);
17906       return marked;
17907     };
17908     
17909     marked.defaults = {
17910       gfm: true,
17911       tables: true,
17912       breaks: false,
17913       pedantic: false,
17914       sanitize: false,
17915       sanitizer: null,
17916       mangle: true,
17917       smartLists: false,
17918       silent: false,
17919       highlight: null,
17920       langPrefix: 'lang-',
17921       smartypants: false,
17922       headerPrefix: '',
17923       renderer: new Renderer,
17924       xhtml: false
17925     };
17926     
17927     /**
17928      * Expose
17929      */
17930     
17931     marked.Parser = Parser;
17932     marked.parser = Parser.parse;
17933     
17934     marked.Renderer = Renderer;
17935     
17936     marked.Lexer = Lexer;
17937     marked.lexer = Lexer.lex;
17938     
17939     marked.InlineLexer = InlineLexer;
17940     marked.inlineLexer = InlineLexer.output;
17941     
17942     marked.parse = marked;
17943     
17944     Roo.Markdown.marked = marked;
17945
17946 })();/*
17947  * Based on:
17948  * Ext JS Library 1.1.1
17949  * Copyright(c) 2006-2007, Ext JS, LLC.
17950  *
17951  * Originally Released Under LGPL - original licence link has changed is not relivant.
17952  *
17953  * Fork - LGPL
17954  * <script type="text/javascript">
17955  */
17956
17957
17958
17959 /*
17960  * These classes are derivatives of the similarly named classes in the YUI Library.
17961  * The original license:
17962  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17963  * Code licensed under the BSD License:
17964  * http://developer.yahoo.net/yui/license.txt
17965  */
17966
17967 (function() {
17968
17969 var Event=Roo.EventManager;
17970 var Dom=Roo.lib.Dom;
17971
17972 /**
17973  * @class Roo.dd.DragDrop
17974  * @extends Roo.util.Observable
17975  * Defines the interface and base operation of items that that can be
17976  * dragged or can be drop targets.  It was designed to be extended, overriding
17977  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17978  * Up to three html elements can be associated with a DragDrop instance:
17979  * <ul>
17980  * <li>linked element: the element that is passed into the constructor.
17981  * This is the element which defines the boundaries for interaction with
17982  * other DragDrop objects.</li>
17983  * <li>handle element(s): The drag operation only occurs if the element that
17984  * was clicked matches a handle element.  By default this is the linked
17985  * element, but there are times that you will want only a portion of the
17986  * linked element to initiate the drag operation, and the setHandleElId()
17987  * method provides a way to define this.</li>
17988  * <li>drag element: this represents the element that would be moved along
17989  * with the cursor during a drag operation.  By default, this is the linked
17990  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17991  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17992  * </li>
17993  * </ul>
17994  * This class should not be instantiated until the onload event to ensure that
17995  * the associated elements are available.
17996  * The following would define a DragDrop obj that would interact with any
17997  * other DragDrop obj in the "group1" group:
17998  * <pre>
17999  *  dd = new Roo.dd.DragDrop("div1", "group1");
18000  * </pre>
18001  * Since none of the event handlers have been implemented, nothing would
18002  * actually happen if you were to run the code above.  Normally you would
18003  * override this class or one of the default implementations, but you can
18004  * also override the methods you want on an instance of the class...
18005  * <pre>
18006  *  dd.onDragDrop = function(e, id) {
18007  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18008  *  }
18009  * </pre>
18010  * @constructor
18011  * @param {String} id of the element that is linked to this instance
18012  * @param {String} sGroup the group of related DragDrop objects
18013  * @param {object} config an object containing configurable attributes
18014  *                Valid properties for DragDrop:
18015  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18016  */
18017 Roo.dd.DragDrop = function(id, sGroup, config) {
18018     if (id) {
18019         this.init(id, sGroup, config);
18020     }
18021     
18022 };
18023
18024 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18025
18026     /**
18027      * The id of the element associated with this object.  This is what we
18028      * refer to as the "linked element" because the size and position of
18029      * this element is used to determine when the drag and drop objects have
18030      * interacted.
18031      * @property id
18032      * @type String
18033      */
18034     id: null,
18035
18036     /**
18037      * Configuration attributes passed into the constructor
18038      * @property config
18039      * @type object
18040      */
18041     config: null,
18042
18043     /**
18044      * The id of the element that will be dragged.  By default this is same
18045      * as the linked element , but could be changed to another element. Ex:
18046      * Roo.dd.DDProxy
18047      * @property dragElId
18048      * @type String
18049      * @private
18050      */
18051     dragElId: null,
18052
18053     /**
18054      * the id of the element that initiates the drag operation.  By default
18055      * this is the linked element, but could be changed to be a child of this
18056      * element.  This lets us do things like only starting the drag when the
18057      * header element within the linked html element is clicked.
18058      * @property handleElId
18059      * @type String
18060      * @private
18061      */
18062     handleElId: null,
18063
18064     /**
18065      * An associative array of HTML tags that will be ignored if clicked.
18066      * @property invalidHandleTypes
18067      * @type {string: string}
18068      */
18069     invalidHandleTypes: null,
18070
18071     /**
18072      * An associative array of ids for elements that will be ignored if clicked
18073      * @property invalidHandleIds
18074      * @type {string: string}
18075      */
18076     invalidHandleIds: null,
18077
18078     /**
18079      * An indexted array of css class names for elements that will be ignored
18080      * if clicked.
18081      * @property invalidHandleClasses
18082      * @type string[]
18083      */
18084     invalidHandleClasses: null,
18085
18086     /**
18087      * The linked element's absolute X position at the time the drag was
18088      * started
18089      * @property startPageX
18090      * @type int
18091      * @private
18092      */
18093     startPageX: 0,
18094
18095     /**
18096      * The linked element's absolute X position at the time the drag was
18097      * started
18098      * @property startPageY
18099      * @type int
18100      * @private
18101      */
18102     startPageY: 0,
18103
18104     /**
18105      * The group defines a logical collection of DragDrop objects that are
18106      * related.  Instances only get events when interacting with other
18107      * DragDrop object in the same group.  This lets us define multiple
18108      * groups using a single DragDrop subclass if we want.
18109      * @property groups
18110      * @type {string: string}
18111      */
18112     groups: null,
18113
18114     /**
18115      * Individual drag/drop instances can be locked.  This will prevent
18116      * onmousedown start drag.
18117      * @property locked
18118      * @type boolean
18119      * @private
18120      */
18121     locked: false,
18122
18123     /**
18124      * Lock this instance
18125      * @method lock
18126      */
18127     lock: function() { this.locked = true; },
18128
18129     /**
18130      * Unlock this instace
18131      * @method unlock
18132      */
18133     unlock: function() { this.locked = false; },
18134
18135     /**
18136      * By default, all insances can be a drop target.  This can be disabled by
18137      * setting isTarget to false.
18138      * @method isTarget
18139      * @type boolean
18140      */
18141     isTarget: true,
18142
18143     /**
18144      * The padding configured for this drag and drop object for calculating
18145      * the drop zone intersection with this object.
18146      * @method padding
18147      * @type int[]
18148      */
18149     padding: null,
18150
18151     /**
18152      * Cached reference to the linked element
18153      * @property _domRef
18154      * @private
18155      */
18156     _domRef: null,
18157
18158     /**
18159      * Internal typeof flag
18160      * @property __ygDragDrop
18161      * @private
18162      */
18163     __ygDragDrop: true,
18164
18165     /**
18166      * Set to true when horizontal contraints are applied
18167      * @property constrainX
18168      * @type boolean
18169      * @private
18170      */
18171     constrainX: false,
18172
18173     /**
18174      * Set to true when vertical contraints are applied
18175      * @property constrainY
18176      * @type boolean
18177      * @private
18178      */
18179     constrainY: false,
18180
18181     /**
18182      * The left constraint
18183      * @property minX
18184      * @type int
18185      * @private
18186      */
18187     minX: 0,
18188
18189     /**
18190      * The right constraint
18191      * @property maxX
18192      * @type int
18193      * @private
18194      */
18195     maxX: 0,
18196
18197     /**
18198      * The up constraint
18199      * @property minY
18200      * @type int
18201      * @type int
18202      * @private
18203      */
18204     minY: 0,
18205
18206     /**
18207      * The down constraint
18208      * @property maxY
18209      * @type int
18210      * @private
18211      */
18212     maxY: 0,
18213
18214     /**
18215      * Maintain offsets when we resetconstraints.  Set to true when you want
18216      * the position of the element relative to its parent to stay the same
18217      * when the page changes
18218      *
18219      * @property maintainOffset
18220      * @type boolean
18221      */
18222     maintainOffset: false,
18223
18224     /**
18225      * Array of pixel locations the element will snap to if we specified a
18226      * horizontal graduation/interval.  This array is generated automatically
18227      * when you define a tick interval.
18228      * @property xTicks
18229      * @type int[]
18230      */
18231     xTicks: null,
18232
18233     /**
18234      * Array of pixel locations the element will snap to if we specified a
18235      * vertical graduation/interval.  This array is generated automatically
18236      * when you define a tick interval.
18237      * @property yTicks
18238      * @type int[]
18239      */
18240     yTicks: null,
18241
18242     /**
18243      * By default the drag and drop instance will only respond to the primary
18244      * button click (left button for a right-handed mouse).  Set to true to
18245      * allow drag and drop to start with any mouse click that is propogated
18246      * by the browser
18247      * @property primaryButtonOnly
18248      * @type boolean
18249      */
18250     primaryButtonOnly: true,
18251
18252     /**
18253      * The availabe property is false until the linked dom element is accessible.
18254      * @property available
18255      * @type boolean
18256      */
18257     available: false,
18258
18259     /**
18260      * By default, drags can only be initiated if the mousedown occurs in the
18261      * region the linked element is.  This is done in part to work around a
18262      * bug in some browsers that mis-report the mousedown if the previous
18263      * mouseup happened outside of the window.  This property is set to true
18264      * if outer handles are defined.
18265      *
18266      * @property hasOuterHandles
18267      * @type boolean
18268      * @default false
18269      */
18270     hasOuterHandles: false,
18271
18272     /**
18273      * Code that executes immediately before the startDrag event
18274      * @method b4StartDrag
18275      * @private
18276      */
18277     b4StartDrag: function(x, y) { },
18278
18279     /**
18280      * Abstract method called after a drag/drop object is clicked
18281      * and the drag or mousedown time thresholds have beeen met.
18282      * @method startDrag
18283      * @param {int} X click location
18284      * @param {int} Y click location
18285      */
18286     startDrag: function(x, y) { /* override this */ },
18287
18288     /**
18289      * Code that executes immediately before the onDrag event
18290      * @method b4Drag
18291      * @private
18292      */
18293     b4Drag: function(e) { },
18294
18295     /**
18296      * Abstract method called during the onMouseMove event while dragging an
18297      * object.
18298      * @method onDrag
18299      * @param {Event} e the mousemove event
18300      */
18301     onDrag: function(e) { /* override this */ },
18302
18303     /**
18304      * Abstract method called when this element fist begins hovering over
18305      * another DragDrop obj
18306      * @method onDragEnter
18307      * @param {Event} e the mousemove event
18308      * @param {String|DragDrop[]} id In POINT mode, the element
18309      * id this is hovering over.  In INTERSECT mode, an array of one or more
18310      * dragdrop items being hovered over.
18311      */
18312     onDragEnter: function(e, id) { /* override this */ },
18313
18314     /**
18315      * Code that executes immediately before the onDragOver event
18316      * @method b4DragOver
18317      * @private
18318      */
18319     b4DragOver: function(e) { },
18320
18321     /**
18322      * Abstract method called when this element is hovering over another
18323      * DragDrop obj
18324      * @method onDragOver
18325      * @param {Event} e the mousemove event
18326      * @param {String|DragDrop[]} id In POINT mode, the element
18327      * id this is hovering over.  In INTERSECT mode, an array of dd items
18328      * being hovered over.
18329      */
18330     onDragOver: function(e, id) { /* override this */ },
18331
18332     /**
18333      * Code that executes immediately before the onDragOut event
18334      * @method b4DragOut
18335      * @private
18336      */
18337     b4DragOut: function(e) { },
18338
18339     /**
18340      * Abstract method called when we are no longer hovering over an element
18341      * @method onDragOut
18342      * @param {Event} e the mousemove event
18343      * @param {String|DragDrop[]} id In POINT mode, the element
18344      * id this was hovering over.  In INTERSECT mode, an array of dd items
18345      * that the mouse is no longer over.
18346      */
18347     onDragOut: function(e, id) { /* override this */ },
18348
18349     /**
18350      * Code that executes immediately before the onDragDrop event
18351      * @method b4DragDrop
18352      * @private
18353      */
18354     b4DragDrop: function(e) { },
18355
18356     /**
18357      * Abstract method called when this item is dropped on another DragDrop
18358      * obj
18359      * @method onDragDrop
18360      * @param {Event} e the mouseup event
18361      * @param {String|DragDrop[]} id In POINT mode, the element
18362      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18363      * was dropped on.
18364      */
18365     onDragDrop: function(e, id) { /* override this */ },
18366
18367     /**
18368      * Abstract method called when this item is dropped on an area with no
18369      * drop target
18370      * @method onInvalidDrop
18371      * @param {Event} e the mouseup event
18372      */
18373     onInvalidDrop: function(e) { /* override this */ },
18374
18375     /**
18376      * Code that executes immediately before the endDrag event
18377      * @method b4EndDrag
18378      * @private
18379      */
18380     b4EndDrag: function(e) { },
18381
18382     /**
18383      * Fired when we are done dragging the object
18384      * @method endDrag
18385      * @param {Event} e the mouseup event
18386      */
18387     endDrag: function(e) { /* override this */ },
18388
18389     /**
18390      * Code executed immediately before the onMouseDown event
18391      * @method b4MouseDown
18392      * @param {Event} e the mousedown event
18393      * @private
18394      */
18395     b4MouseDown: function(e) {  },
18396
18397     /**
18398      * Event handler that fires when a drag/drop obj gets a mousedown
18399      * @method onMouseDown
18400      * @param {Event} e the mousedown event
18401      */
18402     onMouseDown: function(e) { /* override this */ },
18403
18404     /**
18405      * Event handler that fires when a drag/drop obj gets a mouseup
18406      * @method onMouseUp
18407      * @param {Event} e the mouseup event
18408      */
18409     onMouseUp: function(e) { /* override this */ },
18410
18411     /**
18412      * Override the onAvailable method to do what is needed after the initial
18413      * position was determined.
18414      * @method onAvailable
18415      */
18416     onAvailable: function () {
18417     },
18418
18419     /*
18420      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18421      * @type Object
18422      */
18423     defaultPadding : {left:0, right:0, top:0, bottom:0},
18424
18425     /*
18426      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18427  *
18428  * Usage:
18429  <pre><code>
18430  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18431                 { dragElId: "existingProxyDiv" });
18432  dd.startDrag = function(){
18433      this.constrainTo("parent-id");
18434  };
18435  </code></pre>
18436  * Or you can initalize it using the {@link Roo.Element} object:
18437  <pre><code>
18438  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18439      startDrag : function(){
18440          this.constrainTo("parent-id");
18441      }
18442  });
18443  </code></pre>
18444      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18445      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18446      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18447      * an object containing the sides to pad. For example: {right:10, bottom:10}
18448      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18449      */
18450     constrainTo : function(constrainTo, pad, inContent){
18451         if(typeof pad == "number"){
18452             pad = {left: pad, right:pad, top:pad, bottom:pad};
18453         }
18454         pad = pad || this.defaultPadding;
18455         var b = Roo.get(this.getEl()).getBox();
18456         var ce = Roo.get(constrainTo);
18457         var s = ce.getScroll();
18458         var c, cd = ce.dom;
18459         if(cd == document.body){
18460             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18461         }else{
18462             xy = ce.getXY();
18463             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18464         }
18465
18466
18467         var topSpace = b.y - c.y;
18468         var leftSpace = b.x - c.x;
18469
18470         this.resetConstraints();
18471         this.setXConstraint(leftSpace - (pad.left||0), // left
18472                 c.width - leftSpace - b.width - (pad.right||0) //right
18473         );
18474         this.setYConstraint(topSpace - (pad.top||0), //top
18475                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18476         );
18477     },
18478
18479     /**
18480      * Returns a reference to the linked element
18481      * @method getEl
18482      * @return {HTMLElement} the html element
18483      */
18484     getEl: function() {
18485         if (!this._domRef) {
18486             this._domRef = Roo.getDom(this.id);
18487         }
18488
18489         return this._domRef;
18490     },
18491
18492     /**
18493      * Returns a reference to the actual element to drag.  By default this is
18494      * the same as the html element, but it can be assigned to another
18495      * element. An example of this can be found in Roo.dd.DDProxy
18496      * @method getDragEl
18497      * @return {HTMLElement} the html element
18498      */
18499     getDragEl: function() {
18500         return Roo.getDom(this.dragElId);
18501     },
18502
18503     /**
18504      * Sets up the DragDrop object.  Must be called in the constructor of any
18505      * Roo.dd.DragDrop subclass
18506      * @method init
18507      * @param id the id of the linked element
18508      * @param {String} sGroup the group of related items
18509      * @param {object} config configuration attributes
18510      */
18511     init: function(id, sGroup, config) {
18512         this.initTarget(id, sGroup, config);
18513         if (!Roo.isTouch) {
18514             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18515         }
18516         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18517         // Event.on(this.id, "selectstart", Event.preventDefault);
18518     },
18519
18520     /**
18521      * Initializes Targeting functionality only... the object does not
18522      * get a mousedown handler.
18523      * @method initTarget
18524      * @param id the id of the linked element
18525      * @param {String} sGroup the group of related items
18526      * @param {object} config configuration attributes
18527      */
18528     initTarget: function(id, sGroup, config) {
18529
18530         // configuration attributes
18531         this.config = config || {};
18532
18533         // create a local reference to the drag and drop manager
18534         this.DDM = Roo.dd.DDM;
18535         // initialize the groups array
18536         this.groups = {};
18537
18538         // assume that we have an element reference instead of an id if the
18539         // parameter is not a string
18540         if (typeof id !== "string") {
18541             id = Roo.id(id);
18542         }
18543
18544         // set the id
18545         this.id = id;
18546
18547         // add to an interaction group
18548         this.addToGroup((sGroup) ? sGroup : "default");
18549
18550         // We don't want to register this as the handle with the manager
18551         // so we just set the id rather than calling the setter.
18552         this.handleElId = id;
18553
18554         // the linked element is the element that gets dragged by default
18555         this.setDragElId(id);
18556
18557         // by default, clicked anchors will not start drag operations.
18558         this.invalidHandleTypes = { A: "A" };
18559         this.invalidHandleIds = {};
18560         this.invalidHandleClasses = [];
18561
18562         this.applyConfig();
18563
18564         this.handleOnAvailable();
18565     },
18566
18567     /**
18568      * Applies the configuration parameters that were passed into the constructor.
18569      * This is supposed to happen at each level through the inheritance chain.  So
18570      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18571      * DragDrop in order to get all of the parameters that are available in
18572      * each object.
18573      * @method applyConfig
18574      */
18575     applyConfig: function() {
18576
18577         // configurable properties:
18578         //    padding, isTarget, maintainOffset, primaryButtonOnly
18579         this.padding           = this.config.padding || [0, 0, 0, 0];
18580         this.isTarget          = (this.config.isTarget !== false);
18581         this.maintainOffset    = (this.config.maintainOffset);
18582         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18583
18584     },
18585
18586     /**
18587      * Executed when the linked element is available
18588      * @method handleOnAvailable
18589      * @private
18590      */
18591     handleOnAvailable: function() {
18592         this.available = true;
18593         this.resetConstraints();
18594         this.onAvailable();
18595     },
18596
18597      /**
18598      * Configures the padding for the target zone in px.  Effectively expands
18599      * (or reduces) the virtual object size for targeting calculations.
18600      * Supports css-style shorthand; if only one parameter is passed, all sides
18601      * will have that padding, and if only two are passed, the top and bottom
18602      * will have the first param, the left and right the second.
18603      * @method setPadding
18604      * @param {int} iTop    Top pad
18605      * @param {int} iRight  Right pad
18606      * @param {int} iBot    Bot pad
18607      * @param {int} iLeft   Left pad
18608      */
18609     setPadding: function(iTop, iRight, iBot, iLeft) {
18610         // this.padding = [iLeft, iRight, iTop, iBot];
18611         if (!iRight && 0 !== iRight) {
18612             this.padding = [iTop, iTop, iTop, iTop];
18613         } else if (!iBot && 0 !== iBot) {
18614             this.padding = [iTop, iRight, iTop, iRight];
18615         } else {
18616             this.padding = [iTop, iRight, iBot, iLeft];
18617         }
18618     },
18619
18620     /**
18621      * Stores the initial placement of the linked element.
18622      * @method setInitialPosition
18623      * @param {int} diffX   the X offset, default 0
18624      * @param {int} diffY   the Y offset, default 0
18625      */
18626     setInitPosition: function(diffX, diffY) {
18627         var el = this.getEl();
18628
18629         if (!this.DDM.verifyEl(el)) {
18630             return;
18631         }
18632
18633         var dx = diffX || 0;
18634         var dy = diffY || 0;
18635
18636         var p = Dom.getXY( el );
18637
18638         this.initPageX = p[0] - dx;
18639         this.initPageY = p[1] - dy;
18640
18641         this.lastPageX = p[0];
18642         this.lastPageY = p[1];
18643
18644
18645         this.setStartPosition(p);
18646     },
18647
18648     /**
18649      * Sets the start position of the element.  This is set when the obj
18650      * is initialized, the reset when a drag is started.
18651      * @method setStartPosition
18652      * @param pos current position (from previous lookup)
18653      * @private
18654      */
18655     setStartPosition: function(pos) {
18656         var p = pos || Dom.getXY( this.getEl() );
18657         this.deltaSetXY = null;
18658
18659         this.startPageX = p[0];
18660         this.startPageY = p[1];
18661     },
18662
18663     /**
18664      * Add this instance to a group of related drag/drop objects.  All
18665      * instances belong to at least one group, and can belong to as many
18666      * groups as needed.
18667      * @method addToGroup
18668      * @param sGroup {string} the name of the group
18669      */
18670     addToGroup: function(sGroup) {
18671         this.groups[sGroup] = true;
18672         this.DDM.regDragDrop(this, sGroup);
18673     },
18674
18675     /**
18676      * Remove's this instance from the supplied interaction group
18677      * @method removeFromGroup
18678      * @param {string}  sGroup  The group to drop
18679      */
18680     removeFromGroup: function(sGroup) {
18681         if (this.groups[sGroup]) {
18682             delete this.groups[sGroup];
18683         }
18684
18685         this.DDM.removeDDFromGroup(this, sGroup);
18686     },
18687
18688     /**
18689      * Allows you to specify that an element other than the linked element
18690      * will be moved with the cursor during a drag
18691      * @method setDragElId
18692      * @param id {string} the id of the element that will be used to initiate the drag
18693      */
18694     setDragElId: function(id) {
18695         this.dragElId = id;
18696     },
18697
18698     /**
18699      * Allows you to specify a child of the linked element that should be
18700      * used to initiate the drag operation.  An example of this would be if
18701      * you have a content div with text and links.  Clicking anywhere in the
18702      * content area would normally start the drag operation.  Use this method
18703      * to specify that an element inside of the content div is the element
18704      * that starts the drag operation.
18705      * @method setHandleElId
18706      * @param id {string} the id of the element that will be used to
18707      * initiate the drag.
18708      */
18709     setHandleElId: function(id) {
18710         if (typeof id !== "string") {
18711             id = Roo.id(id);
18712         }
18713         this.handleElId = id;
18714         this.DDM.regHandle(this.id, id);
18715     },
18716
18717     /**
18718      * Allows you to set an element outside of the linked element as a drag
18719      * handle
18720      * @method setOuterHandleElId
18721      * @param id the id of the element that will be used to initiate the drag
18722      */
18723     setOuterHandleElId: function(id) {
18724         if (typeof id !== "string") {
18725             id = Roo.id(id);
18726         }
18727         Event.on(id, "mousedown",
18728                 this.handleMouseDown, this);
18729         this.setHandleElId(id);
18730
18731         this.hasOuterHandles = true;
18732     },
18733
18734     /**
18735      * Remove all drag and drop hooks for this element
18736      * @method unreg
18737      */
18738     unreg: function() {
18739         Event.un(this.id, "mousedown",
18740                 this.handleMouseDown);
18741         Event.un(this.id, "touchstart",
18742                 this.handleMouseDown);
18743         this._domRef = null;
18744         this.DDM._remove(this);
18745     },
18746
18747     destroy : function(){
18748         this.unreg();
18749     },
18750
18751     /**
18752      * Returns true if this instance is locked, or the drag drop mgr is locked
18753      * (meaning that all drag/drop is disabled on the page.)
18754      * @method isLocked
18755      * @return {boolean} true if this obj or all drag/drop is locked, else
18756      * false
18757      */
18758     isLocked: function() {
18759         return (this.DDM.isLocked() || this.locked);
18760     },
18761
18762     /**
18763      * Fired when this object is clicked
18764      * @method handleMouseDown
18765      * @param {Event} e
18766      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18767      * @private
18768      */
18769     handleMouseDown: function(e, oDD){
18770      
18771         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18772             //Roo.log('not touch/ button !=0');
18773             return;
18774         }
18775         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18776             return; // double touch..
18777         }
18778         
18779
18780         if (this.isLocked()) {
18781             //Roo.log('locked');
18782             return;
18783         }
18784
18785         this.DDM.refreshCache(this.groups);
18786 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18787         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18788         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18789             //Roo.log('no outer handes or not over target');
18790                 // do nothing.
18791         } else {
18792 //            Roo.log('check validator');
18793             if (this.clickValidator(e)) {
18794 //                Roo.log('validate success');
18795                 // set the initial element position
18796                 this.setStartPosition();
18797
18798
18799                 this.b4MouseDown(e);
18800                 this.onMouseDown(e);
18801
18802                 this.DDM.handleMouseDown(e, this);
18803
18804                 this.DDM.stopEvent(e);
18805             } else {
18806
18807
18808             }
18809         }
18810     },
18811
18812     clickValidator: function(e) {
18813         var target = e.getTarget();
18814         return ( this.isValidHandleChild(target) &&
18815                     (this.id == this.handleElId ||
18816                         this.DDM.handleWasClicked(target, this.id)) );
18817     },
18818
18819     /**
18820      * Allows you to specify a tag name that should not start a drag operation
18821      * when clicked.  This is designed to facilitate embedding links within a
18822      * drag handle that do something other than start the drag.
18823      * @method addInvalidHandleType
18824      * @param {string} tagName the type of element to exclude
18825      */
18826     addInvalidHandleType: function(tagName) {
18827         var type = tagName.toUpperCase();
18828         this.invalidHandleTypes[type] = type;
18829     },
18830
18831     /**
18832      * Lets you to specify an element id for a child of a drag handle
18833      * that should not initiate a drag
18834      * @method addInvalidHandleId
18835      * @param {string} id the element id of the element you wish to ignore
18836      */
18837     addInvalidHandleId: function(id) {
18838         if (typeof id !== "string") {
18839             id = Roo.id(id);
18840         }
18841         this.invalidHandleIds[id] = id;
18842     },
18843
18844     /**
18845      * Lets you specify a css class of elements that will not initiate a drag
18846      * @method addInvalidHandleClass
18847      * @param {string} cssClass the class of the elements you wish to ignore
18848      */
18849     addInvalidHandleClass: function(cssClass) {
18850         this.invalidHandleClasses.push(cssClass);
18851     },
18852
18853     /**
18854      * Unsets an excluded tag name set by addInvalidHandleType
18855      * @method removeInvalidHandleType
18856      * @param {string} tagName the type of element to unexclude
18857      */
18858     removeInvalidHandleType: function(tagName) {
18859         var type = tagName.toUpperCase();
18860         // this.invalidHandleTypes[type] = null;
18861         delete this.invalidHandleTypes[type];
18862     },
18863
18864     /**
18865      * Unsets an invalid handle id
18866      * @method removeInvalidHandleId
18867      * @param {string} id the id of the element to re-enable
18868      */
18869     removeInvalidHandleId: function(id) {
18870         if (typeof id !== "string") {
18871             id = Roo.id(id);
18872         }
18873         delete this.invalidHandleIds[id];
18874     },
18875
18876     /**
18877      * Unsets an invalid css class
18878      * @method removeInvalidHandleClass
18879      * @param {string} cssClass the class of the element(s) you wish to
18880      * re-enable
18881      */
18882     removeInvalidHandleClass: function(cssClass) {
18883         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18884             if (this.invalidHandleClasses[i] == cssClass) {
18885                 delete this.invalidHandleClasses[i];
18886             }
18887         }
18888     },
18889
18890     /**
18891      * Checks the tag exclusion list to see if this click should be ignored
18892      * @method isValidHandleChild
18893      * @param {HTMLElement} node the HTMLElement to evaluate
18894      * @return {boolean} true if this is a valid tag type, false if not
18895      */
18896     isValidHandleChild: function(node) {
18897
18898         var valid = true;
18899         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18900         var nodeName;
18901         try {
18902             nodeName = node.nodeName.toUpperCase();
18903         } catch(e) {
18904             nodeName = node.nodeName;
18905         }
18906         valid = valid && !this.invalidHandleTypes[nodeName];
18907         valid = valid && !this.invalidHandleIds[node.id];
18908
18909         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18910             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18911         }
18912
18913
18914         return valid;
18915
18916     },
18917
18918     /**
18919      * Create the array of horizontal tick marks if an interval was specified
18920      * in setXConstraint().
18921      * @method setXTicks
18922      * @private
18923      */
18924     setXTicks: function(iStartX, iTickSize) {
18925         this.xTicks = [];
18926         this.xTickSize = iTickSize;
18927
18928         var tickMap = {};
18929
18930         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18931             if (!tickMap[i]) {
18932                 this.xTicks[this.xTicks.length] = i;
18933                 tickMap[i] = true;
18934             }
18935         }
18936
18937         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18938             if (!tickMap[i]) {
18939                 this.xTicks[this.xTicks.length] = i;
18940                 tickMap[i] = true;
18941             }
18942         }
18943
18944         this.xTicks.sort(this.DDM.numericSort) ;
18945     },
18946
18947     /**
18948      * Create the array of vertical tick marks if an interval was specified in
18949      * setYConstraint().
18950      * @method setYTicks
18951      * @private
18952      */
18953     setYTicks: function(iStartY, iTickSize) {
18954         this.yTicks = [];
18955         this.yTickSize = iTickSize;
18956
18957         var tickMap = {};
18958
18959         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18960             if (!tickMap[i]) {
18961                 this.yTicks[this.yTicks.length] = i;
18962                 tickMap[i] = true;
18963             }
18964         }
18965
18966         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18967             if (!tickMap[i]) {
18968                 this.yTicks[this.yTicks.length] = i;
18969                 tickMap[i] = true;
18970             }
18971         }
18972
18973         this.yTicks.sort(this.DDM.numericSort) ;
18974     },
18975
18976     /**
18977      * By default, the element can be dragged any place on the screen.  Use
18978      * this method to limit the horizontal travel of the element.  Pass in
18979      * 0,0 for the parameters if you want to lock the drag to the y axis.
18980      * @method setXConstraint
18981      * @param {int} iLeft the number of pixels the element can move to the left
18982      * @param {int} iRight the number of pixels the element can move to the
18983      * right
18984      * @param {int} iTickSize optional parameter for specifying that the
18985      * element
18986      * should move iTickSize pixels at a time.
18987      */
18988     setXConstraint: function(iLeft, iRight, iTickSize) {
18989         this.leftConstraint = iLeft;
18990         this.rightConstraint = iRight;
18991
18992         this.minX = this.initPageX - iLeft;
18993         this.maxX = this.initPageX + iRight;
18994         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18995
18996         this.constrainX = true;
18997     },
18998
18999     /**
19000      * Clears any constraints applied to this instance.  Also clears ticks
19001      * since they can't exist independent of a constraint at this time.
19002      * @method clearConstraints
19003      */
19004     clearConstraints: function() {
19005         this.constrainX = false;
19006         this.constrainY = false;
19007         this.clearTicks();
19008     },
19009
19010     /**
19011      * Clears any tick interval defined for this instance
19012      * @method clearTicks
19013      */
19014     clearTicks: function() {
19015         this.xTicks = null;
19016         this.yTicks = null;
19017         this.xTickSize = 0;
19018         this.yTickSize = 0;
19019     },
19020
19021     /**
19022      * By default, the element can be dragged any place on the screen.  Set
19023      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19024      * parameters if you want to lock the drag to the x axis.
19025      * @method setYConstraint
19026      * @param {int} iUp the number of pixels the element can move up
19027      * @param {int} iDown the number of pixels the element can move down
19028      * @param {int} iTickSize optional parameter for specifying that the
19029      * element should move iTickSize pixels at a time.
19030      */
19031     setYConstraint: function(iUp, iDown, iTickSize) {
19032         this.topConstraint = iUp;
19033         this.bottomConstraint = iDown;
19034
19035         this.minY = this.initPageY - iUp;
19036         this.maxY = this.initPageY + iDown;
19037         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19038
19039         this.constrainY = true;
19040
19041     },
19042
19043     /**
19044      * resetConstraints must be called if you manually reposition a dd element.
19045      * @method resetConstraints
19046      * @param {boolean} maintainOffset
19047      */
19048     resetConstraints: function() {
19049
19050
19051         // Maintain offsets if necessary
19052         if (this.initPageX || this.initPageX === 0) {
19053             // figure out how much this thing has moved
19054             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19055             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19056
19057             this.setInitPosition(dx, dy);
19058
19059         // This is the first time we have detected the element's position
19060         } else {
19061             this.setInitPosition();
19062         }
19063
19064         if (this.constrainX) {
19065             this.setXConstraint( this.leftConstraint,
19066                                  this.rightConstraint,
19067                                  this.xTickSize        );
19068         }
19069
19070         if (this.constrainY) {
19071             this.setYConstraint( this.topConstraint,
19072                                  this.bottomConstraint,
19073                                  this.yTickSize         );
19074         }
19075     },
19076
19077     /**
19078      * Normally the drag element is moved pixel by pixel, but we can specify
19079      * that it move a number of pixels at a time.  This method resolves the
19080      * location when we have it set up like this.
19081      * @method getTick
19082      * @param {int} val where we want to place the object
19083      * @param {int[]} tickArray sorted array of valid points
19084      * @return {int} the closest tick
19085      * @private
19086      */
19087     getTick: function(val, tickArray) {
19088
19089         if (!tickArray) {
19090             // If tick interval is not defined, it is effectively 1 pixel,
19091             // so we return the value passed to us.
19092             return val;
19093         } else if (tickArray[0] >= val) {
19094             // The value is lower than the first tick, so we return the first
19095             // tick.
19096             return tickArray[0];
19097         } else {
19098             for (var i=0, len=tickArray.length; i<len; ++i) {
19099                 var next = i + 1;
19100                 if (tickArray[next] && tickArray[next] >= val) {
19101                     var diff1 = val - tickArray[i];
19102                     var diff2 = tickArray[next] - val;
19103                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19104                 }
19105             }
19106
19107             // The value is larger than the last tick, so we return the last
19108             // tick.
19109             return tickArray[tickArray.length - 1];
19110         }
19111     },
19112
19113     /**
19114      * toString method
19115      * @method toString
19116      * @return {string} string representation of the dd obj
19117      */
19118     toString: function() {
19119         return ("DragDrop " + this.id);
19120     }
19121
19122 });
19123
19124 })();
19125 /*
19126  * Based on:
19127  * Ext JS Library 1.1.1
19128  * Copyright(c) 2006-2007, Ext JS, LLC.
19129  *
19130  * Originally Released Under LGPL - original licence link has changed is not relivant.
19131  *
19132  * Fork - LGPL
19133  * <script type="text/javascript">
19134  */
19135
19136
19137 /**
19138  * The drag and drop utility provides a framework for building drag and drop
19139  * applications.  In addition to enabling drag and drop for specific elements,
19140  * the drag and drop elements are tracked by the manager class, and the
19141  * interactions between the various elements are tracked during the drag and
19142  * the implementing code is notified about these important moments.
19143  */
19144
19145 // Only load the library once.  Rewriting the manager class would orphan
19146 // existing drag and drop instances.
19147 if (!Roo.dd.DragDropMgr) {
19148
19149 /**
19150  * @class Roo.dd.DragDropMgr
19151  * DragDropMgr is a singleton that tracks the element interaction for
19152  * all DragDrop items in the window.  Generally, you will not call
19153  * this class directly, but it does have helper methods that could
19154  * be useful in your DragDrop implementations.
19155  * @singleton
19156  */
19157 Roo.dd.DragDropMgr = function() {
19158
19159     var Event = Roo.EventManager;
19160
19161     return {
19162
19163         /**
19164          * Two dimensional Array of registered DragDrop objects.  The first
19165          * dimension is the DragDrop item group, the second the DragDrop
19166          * object.
19167          * @property ids
19168          * @type {string: string}
19169          * @private
19170          * @static
19171          */
19172         ids: {},
19173
19174         /**
19175          * Array of element ids defined as drag handles.  Used to determine
19176          * if the element that generated the mousedown event is actually the
19177          * handle and not the html element itself.
19178          * @property handleIds
19179          * @type {string: string}
19180          * @private
19181          * @static
19182          */
19183         handleIds: {},
19184
19185         /**
19186          * the DragDrop object that is currently being dragged
19187          * @property dragCurrent
19188          * @type DragDrop
19189          * @private
19190          * @static
19191          **/
19192         dragCurrent: null,
19193
19194         /**
19195          * the DragDrop object(s) that are being hovered over
19196          * @property dragOvers
19197          * @type Array
19198          * @private
19199          * @static
19200          */
19201         dragOvers: {},
19202
19203         /**
19204          * the X distance between the cursor and the object being dragged
19205          * @property deltaX
19206          * @type int
19207          * @private
19208          * @static
19209          */
19210         deltaX: 0,
19211
19212         /**
19213          * the Y distance between the cursor and the object being dragged
19214          * @property deltaY
19215          * @type int
19216          * @private
19217          * @static
19218          */
19219         deltaY: 0,
19220
19221         /**
19222          * Flag to determine if we should prevent the default behavior of the
19223          * events we define. By default this is true, but this can be set to
19224          * false if you need the default behavior (not recommended)
19225          * @property preventDefault
19226          * @type boolean
19227          * @static
19228          */
19229         preventDefault: true,
19230
19231         /**
19232          * Flag to determine if we should stop the propagation of the events
19233          * we generate. This is true by default but you may want to set it to
19234          * false if the html element contains other features that require the
19235          * mouse click.
19236          * @property stopPropagation
19237          * @type boolean
19238          * @static
19239          */
19240         stopPropagation: true,
19241
19242         /**
19243          * Internal flag that is set to true when drag and drop has been
19244          * intialized
19245          * @property initialized
19246          * @private
19247          * @static
19248          */
19249         initalized: false,
19250
19251         /**
19252          * All drag and drop can be disabled.
19253          * @property locked
19254          * @private
19255          * @static
19256          */
19257         locked: false,
19258
19259         /**
19260          * Called the first time an element is registered.
19261          * @method init
19262          * @private
19263          * @static
19264          */
19265         init: function() {
19266             this.initialized = true;
19267         },
19268
19269         /**
19270          * In point mode, drag and drop interaction is defined by the
19271          * location of the cursor during the drag/drop
19272          * @property POINT
19273          * @type int
19274          * @static
19275          */
19276         POINT: 0,
19277
19278         /**
19279          * In intersect mode, drag and drop interactio nis defined by the
19280          * overlap of two or more drag and drop objects.
19281          * @property INTERSECT
19282          * @type int
19283          * @static
19284          */
19285         INTERSECT: 1,
19286
19287         /**
19288          * The current drag and drop mode.  Default: POINT
19289          * @property mode
19290          * @type int
19291          * @static
19292          */
19293         mode: 0,
19294
19295         /**
19296          * Runs method on all drag and drop objects
19297          * @method _execOnAll
19298          * @private
19299          * @static
19300          */
19301         _execOnAll: function(sMethod, args) {
19302             for (var i in this.ids) {
19303                 for (var j in this.ids[i]) {
19304                     var oDD = this.ids[i][j];
19305                     if (! this.isTypeOfDD(oDD)) {
19306                         continue;
19307                     }
19308                     oDD[sMethod].apply(oDD, args);
19309                 }
19310             }
19311         },
19312
19313         /**
19314          * Drag and drop initialization.  Sets up the global event handlers
19315          * @method _onLoad
19316          * @private
19317          * @static
19318          */
19319         _onLoad: function() {
19320
19321             this.init();
19322
19323             if (!Roo.isTouch) {
19324                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19325                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19326             }
19327             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19328             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19329             
19330             Event.on(window,   "unload",    this._onUnload, this, true);
19331             Event.on(window,   "resize",    this._onResize, this, true);
19332             // Event.on(window,   "mouseout",    this._test);
19333
19334         },
19335
19336         /**
19337          * Reset constraints on all drag and drop objs
19338          * @method _onResize
19339          * @private
19340          * @static
19341          */
19342         _onResize: function(e) {
19343             this._execOnAll("resetConstraints", []);
19344         },
19345
19346         /**
19347          * Lock all drag and drop functionality
19348          * @method lock
19349          * @static
19350          */
19351         lock: function() { this.locked = true; },
19352
19353         /**
19354          * Unlock all drag and drop functionality
19355          * @method unlock
19356          * @static
19357          */
19358         unlock: function() { this.locked = false; },
19359
19360         /**
19361          * Is drag and drop locked?
19362          * @method isLocked
19363          * @return {boolean} True if drag and drop is locked, false otherwise.
19364          * @static
19365          */
19366         isLocked: function() { return this.locked; },
19367
19368         /**
19369          * Location cache that is set for all drag drop objects when a drag is
19370          * initiated, cleared when the drag is finished.
19371          * @property locationCache
19372          * @private
19373          * @static
19374          */
19375         locationCache: {},
19376
19377         /**
19378          * Set useCache to false if you want to force object the lookup of each
19379          * drag and drop linked element constantly during a drag.
19380          * @property useCache
19381          * @type boolean
19382          * @static
19383          */
19384         useCache: true,
19385
19386         /**
19387          * The number of pixels that the mouse needs to move after the
19388          * mousedown before the drag is initiated.  Default=3;
19389          * @property clickPixelThresh
19390          * @type int
19391          * @static
19392          */
19393         clickPixelThresh: 3,
19394
19395         /**
19396          * The number of milliseconds after the mousedown event to initiate the
19397          * drag if we don't get a mouseup event. Default=1000
19398          * @property clickTimeThresh
19399          * @type int
19400          * @static
19401          */
19402         clickTimeThresh: 350,
19403
19404         /**
19405          * Flag that indicates that either the drag pixel threshold or the
19406          * mousdown time threshold has been met
19407          * @property dragThreshMet
19408          * @type boolean
19409          * @private
19410          * @static
19411          */
19412         dragThreshMet: false,
19413
19414         /**
19415          * Timeout used for the click time threshold
19416          * @property clickTimeout
19417          * @type Object
19418          * @private
19419          * @static
19420          */
19421         clickTimeout: null,
19422
19423         /**
19424          * The X position of the mousedown event stored for later use when a
19425          * drag threshold is met.
19426          * @property startX
19427          * @type int
19428          * @private
19429          * @static
19430          */
19431         startX: 0,
19432
19433         /**
19434          * The Y position of the mousedown event stored for later use when a
19435          * drag threshold is met.
19436          * @property startY
19437          * @type int
19438          * @private
19439          * @static
19440          */
19441         startY: 0,
19442
19443         /**
19444          * Each DragDrop instance must be registered with the DragDropMgr.
19445          * This is executed in DragDrop.init()
19446          * @method regDragDrop
19447          * @param {DragDrop} oDD the DragDrop object to register
19448          * @param {String} sGroup the name of the group this element belongs to
19449          * @static
19450          */
19451         regDragDrop: function(oDD, sGroup) {
19452             if (!this.initialized) { this.init(); }
19453
19454             if (!this.ids[sGroup]) {
19455                 this.ids[sGroup] = {};
19456             }
19457             this.ids[sGroup][oDD.id] = oDD;
19458         },
19459
19460         /**
19461          * Removes the supplied dd instance from the supplied group. Executed
19462          * by DragDrop.removeFromGroup, so don't call this function directly.
19463          * @method removeDDFromGroup
19464          * @private
19465          * @static
19466          */
19467         removeDDFromGroup: function(oDD, sGroup) {
19468             if (!this.ids[sGroup]) {
19469                 this.ids[sGroup] = {};
19470             }
19471
19472             var obj = this.ids[sGroup];
19473             if (obj && obj[oDD.id]) {
19474                 delete obj[oDD.id];
19475             }
19476         },
19477
19478         /**
19479          * Unregisters a drag and drop item.  This is executed in
19480          * DragDrop.unreg, use that method instead of calling this directly.
19481          * @method _remove
19482          * @private
19483          * @static
19484          */
19485         _remove: function(oDD) {
19486             for (var g in oDD.groups) {
19487                 if (g && this.ids[g][oDD.id]) {
19488                     delete this.ids[g][oDD.id];
19489                 }
19490             }
19491             delete this.handleIds[oDD.id];
19492         },
19493
19494         /**
19495          * Each DragDrop handle element must be registered.  This is done
19496          * automatically when executing DragDrop.setHandleElId()
19497          * @method regHandle
19498          * @param {String} sDDId the DragDrop id this element is a handle for
19499          * @param {String} sHandleId the id of the element that is the drag
19500          * handle
19501          * @static
19502          */
19503         regHandle: function(sDDId, sHandleId) {
19504             if (!this.handleIds[sDDId]) {
19505                 this.handleIds[sDDId] = {};
19506             }
19507             this.handleIds[sDDId][sHandleId] = sHandleId;
19508         },
19509
19510         /**
19511          * Utility function to determine if a given element has been
19512          * registered as a drag drop item.
19513          * @method isDragDrop
19514          * @param {String} id the element id to check
19515          * @return {boolean} true if this element is a DragDrop item,
19516          * false otherwise
19517          * @static
19518          */
19519         isDragDrop: function(id) {
19520             return ( this.getDDById(id) ) ? true : false;
19521         },
19522
19523         /**
19524          * Returns the drag and drop instances that are in all groups the
19525          * passed in instance belongs to.
19526          * @method getRelated
19527          * @param {DragDrop} p_oDD the obj to get related data for
19528          * @param {boolean} bTargetsOnly if true, only return targetable objs
19529          * @return {DragDrop[]} the related instances
19530          * @static
19531          */
19532         getRelated: function(p_oDD, bTargetsOnly) {
19533             var oDDs = [];
19534             for (var i in p_oDD.groups) {
19535                 for (j in this.ids[i]) {
19536                     var dd = this.ids[i][j];
19537                     if (! this.isTypeOfDD(dd)) {
19538                         continue;
19539                     }
19540                     if (!bTargetsOnly || dd.isTarget) {
19541                         oDDs[oDDs.length] = dd;
19542                     }
19543                 }
19544             }
19545
19546             return oDDs;
19547         },
19548
19549         /**
19550          * Returns true if the specified dd target is a legal target for
19551          * the specifice drag obj
19552          * @method isLegalTarget
19553          * @param {DragDrop} the drag obj
19554          * @param {DragDrop} the target
19555          * @return {boolean} true if the target is a legal target for the
19556          * dd obj
19557          * @static
19558          */
19559         isLegalTarget: function (oDD, oTargetDD) {
19560             var targets = this.getRelated(oDD, true);
19561             for (var i=0, len=targets.length;i<len;++i) {
19562                 if (targets[i].id == oTargetDD.id) {
19563                     return true;
19564                 }
19565             }
19566
19567             return false;
19568         },
19569
19570         /**
19571          * My goal is to be able to transparently determine if an object is
19572          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19573          * returns "object", oDD.constructor.toString() always returns
19574          * "DragDrop" and not the name of the subclass.  So for now it just
19575          * evaluates a well-known variable in DragDrop.
19576          * @method isTypeOfDD
19577          * @param {Object} the object to evaluate
19578          * @return {boolean} true if typeof oDD = DragDrop
19579          * @static
19580          */
19581         isTypeOfDD: function (oDD) {
19582             return (oDD && oDD.__ygDragDrop);
19583         },
19584
19585         /**
19586          * Utility function to determine if a given element has been
19587          * registered as a drag drop handle for the given Drag Drop object.
19588          * @method isHandle
19589          * @param {String} id the element id to check
19590          * @return {boolean} true if this element is a DragDrop handle, false
19591          * otherwise
19592          * @static
19593          */
19594         isHandle: function(sDDId, sHandleId) {
19595             return ( this.handleIds[sDDId] &&
19596                             this.handleIds[sDDId][sHandleId] );
19597         },
19598
19599         /**
19600          * Returns the DragDrop instance for a given id
19601          * @method getDDById
19602          * @param {String} id the id of the DragDrop object
19603          * @return {DragDrop} the drag drop object, null if it is not found
19604          * @static
19605          */
19606         getDDById: function(id) {
19607             for (var i in this.ids) {
19608                 if (this.ids[i][id]) {
19609                     return this.ids[i][id];
19610                 }
19611             }
19612             return null;
19613         },
19614
19615         /**
19616          * Fired after a registered DragDrop object gets the mousedown event.
19617          * Sets up the events required to track the object being dragged
19618          * @method handleMouseDown
19619          * @param {Event} e the event
19620          * @param oDD the DragDrop object being dragged
19621          * @private
19622          * @static
19623          */
19624         handleMouseDown: function(e, oDD) {
19625             if(Roo.QuickTips){
19626                 Roo.QuickTips.disable();
19627             }
19628             this.currentTarget = e.getTarget();
19629
19630             this.dragCurrent = oDD;
19631
19632             var el = oDD.getEl();
19633
19634             // track start position
19635             this.startX = e.getPageX();
19636             this.startY = e.getPageY();
19637
19638             this.deltaX = this.startX - el.offsetLeft;
19639             this.deltaY = this.startY - el.offsetTop;
19640
19641             this.dragThreshMet = false;
19642
19643             this.clickTimeout = setTimeout(
19644                     function() {
19645                         var DDM = Roo.dd.DDM;
19646                         DDM.startDrag(DDM.startX, DDM.startY);
19647                     },
19648                     this.clickTimeThresh );
19649         },
19650
19651         /**
19652          * Fired when either the drag pixel threshol or the mousedown hold
19653          * time threshold has been met.
19654          * @method startDrag
19655          * @param x {int} the X position of the original mousedown
19656          * @param y {int} the Y position of the original mousedown
19657          * @static
19658          */
19659         startDrag: function(x, y) {
19660             clearTimeout(this.clickTimeout);
19661             if (this.dragCurrent) {
19662                 this.dragCurrent.b4StartDrag(x, y);
19663                 this.dragCurrent.startDrag(x, y);
19664             }
19665             this.dragThreshMet = true;
19666         },
19667
19668         /**
19669          * Internal function to handle the mouseup event.  Will be invoked
19670          * from the context of the document.
19671          * @method handleMouseUp
19672          * @param {Event} e the event
19673          * @private
19674          * @static
19675          */
19676         handleMouseUp: function(e) {
19677
19678             if(Roo.QuickTips){
19679                 Roo.QuickTips.enable();
19680             }
19681             if (! this.dragCurrent) {
19682                 return;
19683             }
19684
19685             clearTimeout(this.clickTimeout);
19686
19687             if (this.dragThreshMet) {
19688                 this.fireEvents(e, true);
19689             } else {
19690             }
19691
19692             this.stopDrag(e);
19693
19694             this.stopEvent(e);
19695         },
19696
19697         /**
19698          * Utility to stop event propagation and event default, if these
19699          * features are turned on.
19700          * @method stopEvent
19701          * @param {Event} e the event as returned by this.getEvent()
19702          * @static
19703          */
19704         stopEvent: function(e){
19705             if(this.stopPropagation) {
19706                 e.stopPropagation();
19707             }
19708
19709             if (this.preventDefault) {
19710                 e.preventDefault();
19711             }
19712         },
19713
19714         /**
19715          * Internal function to clean up event handlers after the drag
19716          * operation is complete
19717          * @method stopDrag
19718          * @param {Event} e the event
19719          * @private
19720          * @static
19721          */
19722         stopDrag: function(e) {
19723             // Fire the drag end event for the item that was dragged
19724             if (this.dragCurrent) {
19725                 if (this.dragThreshMet) {
19726                     this.dragCurrent.b4EndDrag(e);
19727                     this.dragCurrent.endDrag(e);
19728                 }
19729
19730                 this.dragCurrent.onMouseUp(e);
19731             }
19732
19733             this.dragCurrent = null;
19734             this.dragOvers = {};
19735         },
19736
19737         /**
19738          * Internal function to handle the mousemove event.  Will be invoked
19739          * from the context of the html element.
19740          *
19741          * @TODO figure out what we can do about mouse events lost when the
19742          * user drags objects beyond the window boundary.  Currently we can
19743          * detect this in internet explorer by verifying that the mouse is
19744          * down during the mousemove event.  Firefox doesn't give us the
19745          * button state on the mousemove event.
19746          * @method handleMouseMove
19747          * @param {Event} e the event
19748          * @private
19749          * @static
19750          */
19751         handleMouseMove: function(e) {
19752             if (! this.dragCurrent) {
19753                 return true;
19754             }
19755
19756             // var button = e.which || e.button;
19757
19758             // check for IE mouseup outside of page boundary
19759             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19760                 this.stopEvent(e);
19761                 return this.handleMouseUp(e);
19762             }
19763
19764             if (!this.dragThreshMet) {
19765                 var diffX = Math.abs(this.startX - e.getPageX());
19766                 var diffY = Math.abs(this.startY - e.getPageY());
19767                 if (diffX > this.clickPixelThresh ||
19768                             diffY > this.clickPixelThresh) {
19769                     this.startDrag(this.startX, this.startY);
19770                 }
19771             }
19772
19773             if (this.dragThreshMet) {
19774                 this.dragCurrent.b4Drag(e);
19775                 this.dragCurrent.onDrag(e);
19776                 if(!this.dragCurrent.moveOnly){
19777                     this.fireEvents(e, false);
19778                 }
19779             }
19780
19781             this.stopEvent(e);
19782
19783             return true;
19784         },
19785
19786         /**
19787          * Iterates over all of the DragDrop elements to find ones we are
19788          * hovering over or dropping on
19789          * @method fireEvents
19790          * @param {Event} e the event
19791          * @param {boolean} isDrop is this a drop op or a mouseover op?
19792          * @private
19793          * @static
19794          */
19795         fireEvents: function(e, isDrop) {
19796             var dc = this.dragCurrent;
19797
19798             // If the user did the mouse up outside of the window, we could
19799             // get here even though we have ended the drag.
19800             if (!dc || dc.isLocked()) {
19801                 return;
19802             }
19803
19804             var pt = e.getPoint();
19805
19806             // cache the previous dragOver array
19807             var oldOvers = [];
19808
19809             var outEvts   = [];
19810             var overEvts  = [];
19811             var dropEvts  = [];
19812             var enterEvts = [];
19813
19814             // Check to see if the object(s) we were hovering over is no longer
19815             // being hovered over so we can fire the onDragOut event
19816             for (var i in this.dragOvers) {
19817
19818                 var ddo = this.dragOvers[i];
19819
19820                 if (! this.isTypeOfDD(ddo)) {
19821                     continue;
19822                 }
19823
19824                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19825                     outEvts.push( ddo );
19826                 }
19827
19828                 oldOvers[i] = true;
19829                 delete this.dragOvers[i];
19830             }
19831
19832             for (var sGroup in dc.groups) {
19833
19834                 if ("string" != typeof sGroup) {
19835                     continue;
19836                 }
19837
19838                 for (i in this.ids[sGroup]) {
19839                     var oDD = this.ids[sGroup][i];
19840                     if (! this.isTypeOfDD(oDD)) {
19841                         continue;
19842                     }
19843
19844                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19845                         if (this.isOverTarget(pt, oDD, this.mode)) {
19846                             // look for drop interactions
19847                             if (isDrop) {
19848                                 dropEvts.push( oDD );
19849                             // look for drag enter and drag over interactions
19850                             } else {
19851
19852                                 // initial drag over: dragEnter fires
19853                                 if (!oldOvers[oDD.id]) {
19854                                     enterEvts.push( oDD );
19855                                 // subsequent drag overs: dragOver fires
19856                                 } else {
19857                                     overEvts.push( oDD );
19858                                 }
19859
19860                                 this.dragOvers[oDD.id] = oDD;
19861                             }
19862                         }
19863                     }
19864                 }
19865             }
19866
19867             if (this.mode) {
19868                 if (outEvts.length) {
19869                     dc.b4DragOut(e, outEvts);
19870                     dc.onDragOut(e, outEvts);
19871                 }
19872
19873                 if (enterEvts.length) {
19874                     dc.onDragEnter(e, enterEvts);
19875                 }
19876
19877                 if (overEvts.length) {
19878                     dc.b4DragOver(e, overEvts);
19879                     dc.onDragOver(e, overEvts);
19880                 }
19881
19882                 if (dropEvts.length) {
19883                     dc.b4DragDrop(e, dropEvts);
19884                     dc.onDragDrop(e, dropEvts);
19885                 }
19886
19887             } else {
19888                 // fire dragout events
19889                 var len = 0;
19890                 for (i=0, len=outEvts.length; i<len; ++i) {
19891                     dc.b4DragOut(e, outEvts[i].id);
19892                     dc.onDragOut(e, outEvts[i].id);
19893                 }
19894
19895                 // fire enter events
19896                 for (i=0,len=enterEvts.length; i<len; ++i) {
19897                     // dc.b4DragEnter(e, oDD.id);
19898                     dc.onDragEnter(e, enterEvts[i].id);
19899                 }
19900
19901                 // fire over events
19902                 for (i=0,len=overEvts.length; i<len; ++i) {
19903                     dc.b4DragOver(e, overEvts[i].id);
19904                     dc.onDragOver(e, overEvts[i].id);
19905                 }
19906
19907                 // fire drop events
19908                 for (i=0, len=dropEvts.length; i<len; ++i) {
19909                     dc.b4DragDrop(e, dropEvts[i].id);
19910                     dc.onDragDrop(e, dropEvts[i].id);
19911                 }
19912
19913             }
19914
19915             // notify about a drop that did not find a target
19916             if (isDrop && !dropEvts.length) {
19917                 dc.onInvalidDrop(e);
19918             }
19919
19920         },
19921
19922         /**
19923          * Helper function for getting the best match from the list of drag
19924          * and drop objects returned by the drag and drop events when we are
19925          * in INTERSECT mode.  It returns either the first object that the
19926          * cursor is over, or the object that has the greatest overlap with
19927          * the dragged element.
19928          * @method getBestMatch
19929          * @param  {DragDrop[]} dds The array of drag and drop objects
19930          * targeted
19931          * @return {DragDrop}       The best single match
19932          * @static
19933          */
19934         getBestMatch: function(dds) {
19935             var winner = null;
19936             // Return null if the input is not what we expect
19937             //if (!dds || !dds.length || dds.length == 0) {
19938                // winner = null;
19939             // If there is only one item, it wins
19940             //} else if (dds.length == 1) {
19941
19942             var len = dds.length;
19943
19944             if (len == 1) {
19945                 winner = dds[0];
19946             } else {
19947                 // Loop through the targeted items
19948                 for (var i=0; i<len; ++i) {
19949                     var dd = dds[i];
19950                     // If the cursor is over the object, it wins.  If the
19951                     // cursor is over multiple matches, the first one we come
19952                     // to wins.
19953                     if (dd.cursorIsOver) {
19954                         winner = dd;
19955                         break;
19956                     // Otherwise the object with the most overlap wins
19957                     } else {
19958                         if (!winner ||
19959                             winner.overlap.getArea() < dd.overlap.getArea()) {
19960                             winner = dd;
19961                         }
19962                     }
19963                 }
19964             }
19965
19966             return winner;
19967         },
19968
19969         /**
19970          * Refreshes the cache of the top-left and bottom-right points of the
19971          * drag and drop objects in the specified group(s).  This is in the
19972          * format that is stored in the drag and drop instance, so typical
19973          * usage is:
19974          * <code>
19975          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19976          * </code>
19977          * Alternatively:
19978          * <code>
19979          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19980          * </code>
19981          * @TODO this really should be an indexed array.  Alternatively this
19982          * method could accept both.
19983          * @method refreshCache
19984          * @param {Object} groups an associative array of groups to refresh
19985          * @static
19986          */
19987         refreshCache: function(groups) {
19988             for (var sGroup in groups) {
19989                 if ("string" != typeof sGroup) {
19990                     continue;
19991                 }
19992                 for (var i in this.ids[sGroup]) {
19993                     var oDD = this.ids[sGroup][i];
19994
19995                     if (this.isTypeOfDD(oDD)) {
19996                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19997                         var loc = this.getLocation(oDD);
19998                         if (loc) {
19999                             this.locationCache[oDD.id] = loc;
20000                         } else {
20001                             delete this.locationCache[oDD.id];
20002                             // this will unregister the drag and drop object if
20003                             // the element is not in a usable state
20004                             // oDD.unreg();
20005                         }
20006                     }
20007                 }
20008             }
20009         },
20010
20011         /**
20012          * This checks to make sure an element exists and is in the DOM.  The
20013          * main purpose is to handle cases where innerHTML is used to remove
20014          * drag and drop objects from the DOM.  IE provides an 'unspecified
20015          * error' when trying to access the offsetParent of such an element
20016          * @method verifyEl
20017          * @param {HTMLElement} el the element to check
20018          * @return {boolean} true if the element looks usable
20019          * @static
20020          */
20021         verifyEl: function(el) {
20022             if (el) {
20023                 var parent;
20024                 if(Roo.isIE){
20025                     try{
20026                         parent = el.offsetParent;
20027                     }catch(e){}
20028                 }else{
20029                     parent = el.offsetParent;
20030                 }
20031                 if (parent) {
20032                     return true;
20033                 }
20034             }
20035
20036             return false;
20037         },
20038
20039         /**
20040          * Returns a Region object containing the drag and drop element's position
20041          * and size, including the padding configured for it
20042          * @method getLocation
20043          * @param {DragDrop} oDD the drag and drop object to get the
20044          *                       location for
20045          * @return {Roo.lib.Region} a Region object representing the total area
20046          *                             the element occupies, including any padding
20047          *                             the instance is configured for.
20048          * @static
20049          */
20050         getLocation: function(oDD) {
20051             if (! this.isTypeOfDD(oDD)) {
20052                 return null;
20053             }
20054
20055             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20056
20057             try {
20058                 pos= Roo.lib.Dom.getXY(el);
20059             } catch (e) { }
20060
20061             if (!pos) {
20062                 return null;
20063             }
20064
20065             x1 = pos[0];
20066             x2 = x1 + el.offsetWidth;
20067             y1 = pos[1];
20068             y2 = y1 + el.offsetHeight;
20069
20070             t = y1 - oDD.padding[0];
20071             r = x2 + oDD.padding[1];
20072             b = y2 + oDD.padding[2];
20073             l = x1 - oDD.padding[3];
20074
20075             return new Roo.lib.Region( t, r, b, l );
20076         },
20077
20078         /**
20079          * Checks the cursor location to see if it over the target
20080          * @method isOverTarget
20081          * @param {Roo.lib.Point} pt The point to evaluate
20082          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20083          * @return {boolean} true if the mouse is over the target
20084          * @private
20085          * @static
20086          */
20087         isOverTarget: function(pt, oTarget, intersect) {
20088             // use cache if available
20089             var loc = this.locationCache[oTarget.id];
20090             if (!loc || !this.useCache) {
20091                 loc = this.getLocation(oTarget);
20092                 this.locationCache[oTarget.id] = loc;
20093
20094             }
20095
20096             if (!loc) {
20097                 return false;
20098             }
20099
20100             oTarget.cursorIsOver = loc.contains( pt );
20101
20102             // DragDrop is using this as a sanity check for the initial mousedown
20103             // in this case we are done.  In POINT mode, if the drag obj has no
20104             // contraints, we are also done. Otherwise we need to evaluate the
20105             // location of the target as related to the actual location of the
20106             // dragged element.
20107             var dc = this.dragCurrent;
20108             if (!dc || !dc.getTargetCoord ||
20109                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20110                 return oTarget.cursorIsOver;
20111             }
20112
20113             oTarget.overlap = null;
20114
20115             // Get the current location of the drag element, this is the
20116             // location of the mouse event less the delta that represents
20117             // where the original mousedown happened on the element.  We
20118             // need to consider constraints and ticks as well.
20119             var pos = dc.getTargetCoord(pt.x, pt.y);
20120
20121             var el = dc.getDragEl();
20122             var curRegion = new Roo.lib.Region( pos.y,
20123                                                    pos.x + el.offsetWidth,
20124                                                    pos.y + el.offsetHeight,
20125                                                    pos.x );
20126
20127             var overlap = curRegion.intersect(loc);
20128
20129             if (overlap) {
20130                 oTarget.overlap = overlap;
20131                 return (intersect) ? true : oTarget.cursorIsOver;
20132             } else {
20133                 return false;
20134             }
20135         },
20136
20137         /**
20138          * unload event handler
20139          * @method _onUnload
20140          * @private
20141          * @static
20142          */
20143         _onUnload: function(e, me) {
20144             Roo.dd.DragDropMgr.unregAll();
20145         },
20146
20147         /**
20148          * Cleans up the drag and drop events and objects.
20149          * @method unregAll
20150          * @private
20151          * @static
20152          */
20153         unregAll: function() {
20154
20155             if (this.dragCurrent) {
20156                 this.stopDrag();
20157                 this.dragCurrent = null;
20158             }
20159
20160             this._execOnAll("unreg", []);
20161
20162             for (i in this.elementCache) {
20163                 delete this.elementCache[i];
20164             }
20165
20166             this.elementCache = {};
20167             this.ids = {};
20168         },
20169
20170         /**
20171          * A cache of DOM elements
20172          * @property elementCache
20173          * @private
20174          * @static
20175          */
20176         elementCache: {},
20177
20178         /**
20179          * Get the wrapper for the DOM element specified
20180          * @method getElWrapper
20181          * @param {String} id the id of the element to get
20182          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20183          * @private
20184          * @deprecated This wrapper isn't that useful
20185          * @static
20186          */
20187         getElWrapper: function(id) {
20188             var oWrapper = this.elementCache[id];
20189             if (!oWrapper || !oWrapper.el) {
20190                 oWrapper = this.elementCache[id] =
20191                     new this.ElementWrapper(Roo.getDom(id));
20192             }
20193             return oWrapper;
20194         },
20195
20196         /**
20197          * Returns the actual DOM element
20198          * @method getElement
20199          * @param {String} id the id of the elment to get
20200          * @return {Object} The element
20201          * @deprecated use Roo.getDom instead
20202          * @static
20203          */
20204         getElement: function(id) {
20205             return Roo.getDom(id);
20206         },
20207
20208         /**
20209          * Returns the style property for the DOM element (i.e.,
20210          * document.getElById(id).style)
20211          * @method getCss
20212          * @param {String} id the id of the elment to get
20213          * @return {Object} The style property of the element
20214          * @deprecated use Roo.getDom instead
20215          * @static
20216          */
20217         getCss: function(id) {
20218             var el = Roo.getDom(id);
20219             return (el) ? el.style : null;
20220         },
20221
20222         /**
20223          * Inner class for cached elements
20224          * @class DragDropMgr.ElementWrapper
20225          * @for DragDropMgr
20226          * @private
20227          * @deprecated
20228          */
20229         ElementWrapper: function(el) {
20230                 /**
20231                  * The element
20232                  * @property el
20233                  */
20234                 this.el = el || null;
20235                 /**
20236                  * The element id
20237                  * @property id
20238                  */
20239                 this.id = this.el && el.id;
20240                 /**
20241                  * A reference to the style property
20242                  * @property css
20243                  */
20244                 this.css = this.el && el.style;
20245             },
20246
20247         /**
20248          * Returns the X position of an html element
20249          * @method getPosX
20250          * @param el the element for which to get the position
20251          * @return {int} the X coordinate
20252          * @for DragDropMgr
20253          * @deprecated use Roo.lib.Dom.getX instead
20254          * @static
20255          */
20256         getPosX: function(el) {
20257             return Roo.lib.Dom.getX(el);
20258         },
20259
20260         /**
20261          * Returns the Y position of an html element
20262          * @method getPosY
20263          * @param el the element for which to get the position
20264          * @return {int} the Y coordinate
20265          * @deprecated use Roo.lib.Dom.getY instead
20266          * @static
20267          */
20268         getPosY: function(el) {
20269             return Roo.lib.Dom.getY(el);
20270         },
20271
20272         /**
20273          * Swap two nodes.  In IE, we use the native method, for others we
20274          * emulate the IE behavior
20275          * @method swapNode
20276          * @param n1 the first node to swap
20277          * @param n2 the other node to swap
20278          * @static
20279          */
20280         swapNode: function(n1, n2) {
20281             if (n1.swapNode) {
20282                 n1.swapNode(n2);
20283             } else {
20284                 var p = n2.parentNode;
20285                 var s = n2.nextSibling;
20286
20287                 if (s == n1) {
20288                     p.insertBefore(n1, n2);
20289                 } else if (n2 == n1.nextSibling) {
20290                     p.insertBefore(n2, n1);
20291                 } else {
20292                     n1.parentNode.replaceChild(n2, n1);
20293                     p.insertBefore(n1, s);
20294                 }
20295             }
20296         },
20297
20298         /**
20299          * Returns the current scroll position
20300          * @method getScroll
20301          * @private
20302          * @static
20303          */
20304         getScroll: function () {
20305             var t, l, dde=document.documentElement, db=document.body;
20306             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20307                 t = dde.scrollTop;
20308                 l = dde.scrollLeft;
20309             } else if (db) {
20310                 t = db.scrollTop;
20311                 l = db.scrollLeft;
20312             } else {
20313
20314             }
20315             return { top: t, left: l };
20316         },
20317
20318         /**
20319          * Returns the specified element style property
20320          * @method getStyle
20321          * @param {HTMLElement} el          the element
20322          * @param {string}      styleProp   the style property
20323          * @return {string} The value of the style property
20324          * @deprecated use Roo.lib.Dom.getStyle
20325          * @static
20326          */
20327         getStyle: function(el, styleProp) {
20328             return Roo.fly(el).getStyle(styleProp);
20329         },
20330
20331         /**
20332          * Gets the scrollTop
20333          * @method getScrollTop
20334          * @return {int} the document's scrollTop
20335          * @static
20336          */
20337         getScrollTop: function () { return this.getScroll().top; },
20338
20339         /**
20340          * Gets the scrollLeft
20341          * @method getScrollLeft
20342          * @return {int} the document's scrollTop
20343          * @static
20344          */
20345         getScrollLeft: function () { return this.getScroll().left; },
20346
20347         /**
20348          * Sets the x/y position of an element to the location of the
20349          * target element.
20350          * @method moveToEl
20351          * @param {HTMLElement} moveEl      The element to move
20352          * @param {HTMLElement} targetEl    The position reference element
20353          * @static
20354          */
20355         moveToEl: function (moveEl, targetEl) {
20356             var aCoord = Roo.lib.Dom.getXY(targetEl);
20357             Roo.lib.Dom.setXY(moveEl, aCoord);
20358         },
20359
20360         /**
20361          * Numeric array sort function
20362          * @method numericSort
20363          * @static
20364          */
20365         numericSort: function(a, b) { return (a - b); },
20366
20367         /**
20368          * Internal counter
20369          * @property _timeoutCount
20370          * @private
20371          * @static
20372          */
20373         _timeoutCount: 0,
20374
20375         /**
20376          * Trying to make the load order less important.  Without this we get
20377          * an error if this file is loaded before the Event Utility.
20378          * @method _addListeners
20379          * @private
20380          * @static
20381          */
20382         _addListeners: function() {
20383             var DDM = Roo.dd.DDM;
20384             if ( Roo.lib.Event && document ) {
20385                 DDM._onLoad();
20386             } else {
20387                 if (DDM._timeoutCount > 2000) {
20388                 } else {
20389                     setTimeout(DDM._addListeners, 10);
20390                     if (document && document.body) {
20391                         DDM._timeoutCount += 1;
20392                     }
20393                 }
20394             }
20395         },
20396
20397         /**
20398          * Recursively searches the immediate parent and all child nodes for
20399          * the handle element in order to determine wheter or not it was
20400          * clicked.
20401          * @method handleWasClicked
20402          * @param node the html element to inspect
20403          * @static
20404          */
20405         handleWasClicked: function(node, id) {
20406             if (this.isHandle(id, node.id)) {
20407                 return true;
20408             } else {
20409                 // check to see if this is a text node child of the one we want
20410                 var p = node.parentNode;
20411
20412                 while (p) {
20413                     if (this.isHandle(id, p.id)) {
20414                         return true;
20415                     } else {
20416                         p = p.parentNode;
20417                     }
20418                 }
20419             }
20420
20421             return false;
20422         }
20423
20424     };
20425
20426 }();
20427
20428 // shorter alias, save a few bytes
20429 Roo.dd.DDM = Roo.dd.DragDropMgr;
20430 Roo.dd.DDM._addListeners();
20431
20432 }/*
20433  * Based on:
20434  * Ext JS Library 1.1.1
20435  * Copyright(c) 2006-2007, Ext JS, LLC.
20436  *
20437  * Originally Released Under LGPL - original licence link has changed is not relivant.
20438  *
20439  * Fork - LGPL
20440  * <script type="text/javascript">
20441  */
20442
20443 /**
20444  * @class Roo.dd.DD
20445  * A DragDrop implementation where the linked element follows the
20446  * mouse cursor during a drag.
20447  * @extends Roo.dd.DragDrop
20448  * @constructor
20449  * @param {String} id the id of the linked element
20450  * @param {String} sGroup the group of related DragDrop items
20451  * @param {object} config an object containing configurable attributes
20452  *                Valid properties for DD:
20453  *                    scroll
20454  */
20455 Roo.dd.DD = function(id, sGroup, config) {
20456     if (id) {
20457         this.init(id, sGroup, config);
20458     }
20459 };
20460
20461 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20462
20463     /**
20464      * When set to true, the utility automatically tries to scroll the browser
20465      * window wehn a drag and drop element is dragged near the viewport boundary.
20466      * Defaults to true.
20467      * @property scroll
20468      * @type boolean
20469      */
20470     scroll: true,
20471
20472     /**
20473      * Sets the pointer offset to the distance between the linked element's top
20474      * left corner and the location the element was clicked
20475      * @method autoOffset
20476      * @param {int} iPageX the X coordinate of the click
20477      * @param {int} iPageY the Y coordinate of the click
20478      */
20479     autoOffset: function(iPageX, iPageY) {
20480         var x = iPageX - this.startPageX;
20481         var y = iPageY - this.startPageY;
20482         this.setDelta(x, y);
20483     },
20484
20485     /**
20486      * Sets the pointer offset.  You can call this directly to force the
20487      * offset to be in a particular location (e.g., pass in 0,0 to set it
20488      * to the center of the object)
20489      * @method setDelta
20490      * @param {int} iDeltaX the distance from the left
20491      * @param {int} iDeltaY the distance from the top
20492      */
20493     setDelta: function(iDeltaX, iDeltaY) {
20494         this.deltaX = iDeltaX;
20495         this.deltaY = iDeltaY;
20496     },
20497
20498     /**
20499      * Sets the drag element to the location of the mousedown or click event,
20500      * maintaining the cursor location relative to the location on the element
20501      * that was clicked.  Override this if you want to place the element in a
20502      * location other than where the cursor is.
20503      * @method setDragElPos
20504      * @param {int} iPageX the X coordinate of the mousedown or drag event
20505      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20506      */
20507     setDragElPos: function(iPageX, iPageY) {
20508         // the first time we do this, we are going to check to make sure
20509         // the element has css positioning
20510
20511         var el = this.getDragEl();
20512         this.alignElWithMouse(el, iPageX, iPageY);
20513     },
20514
20515     /**
20516      * Sets the element to the location of the mousedown or click event,
20517      * maintaining the cursor location relative to the location on the element
20518      * that was clicked.  Override this if you want to place the element in a
20519      * location other than where the cursor is.
20520      * @method alignElWithMouse
20521      * @param {HTMLElement} el the element to move
20522      * @param {int} iPageX the X coordinate of the mousedown or drag event
20523      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20524      */
20525     alignElWithMouse: function(el, iPageX, iPageY) {
20526         var oCoord = this.getTargetCoord(iPageX, iPageY);
20527         var fly = el.dom ? el : Roo.fly(el);
20528         if (!this.deltaSetXY) {
20529             var aCoord = [oCoord.x, oCoord.y];
20530             fly.setXY(aCoord);
20531             var newLeft = fly.getLeft(true);
20532             var newTop  = fly.getTop(true);
20533             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20534         } else {
20535             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20536         }
20537
20538         this.cachePosition(oCoord.x, oCoord.y);
20539         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20540         return oCoord;
20541     },
20542
20543     /**
20544      * Saves the most recent position so that we can reset the constraints and
20545      * tick marks on-demand.  We need to know this so that we can calculate the
20546      * number of pixels the element is offset from its original position.
20547      * @method cachePosition
20548      * @param iPageX the current x position (optional, this just makes it so we
20549      * don't have to look it up again)
20550      * @param iPageY the current y position (optional, this just makes it so we
20551      * don't have to look it up again)
20552      */
20553     cachePosition: function(iPageX, iPageY) {
20554         if (iPageX) {
20555             this.lastPageX = iPageX;
20556             this.lastPageY = iPageY;
20557         } else {
20558             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20559             this.lastPageX = aCoord[0];
20560             this.lastPageY = aCoord[1];
20561         }
20562     },
20563
20564     /**
20565      * Auto-scroll the window if the dragged object has been moved beyond the
20566      * visible window boundary.
20567      * @method autoScroll
20568      * @param {int} x the drag element's x position
20569      * @param {int} y the drag element's y position
20570      * @param {int} h the height of the drag element
20571      * @param {int} w the width of the drag element
20572      * @private
20573      */
20574     autoScroll: function(x, y, h, w) {
20575
20576         if (this.scroll) {
20577             // The client height
20578             var clientH = Roo.lib.Dom.getViewWidth();
20579
20580             // The client width
20581             var clientW = Roo.lib.Dom.getViewHeight();
20582
20583             // The amt scrolled down
20584             var st = this.DDM.getScrollTop();
20585
20586             // The amt scrolled right
20587             var sl = this.DDM.getScrollLeft();
20588
20589             // Location of the bottom of the element
20590             var bot = h + y;
20591
20592             // Location of the right of the element
20593             var right = w + x;
20594
20595             // The distance from the cursor to the bottom of the visible area,
20596             // adjusted so that we don't scroll if the cursor is beyond the
20597             // element drag constraints
20598             var toBot = (clientH + st - y - this.deltaY);
20599
20600             // The distance from the cursor to the right of the visible area
20601             var toRight = (clientW + sl - x - this.deltaX);
20602
20603
20604             // How close to the edge the cursor must be before we scroll
20605             // var thresh = (document.all) ? 100 : 40;
20606             var thresh = 40;
20607
20608             // How many pixels to scroll per autoscroll op.  This helps to reduce
20609             // clunky scrolling. IE is more sensitive about this ... it needs this
20610             // value to be higher.
20611             var scrAmt = (document.all) ? 80 : 30;
20612
20613             // Scroll down if we are near the bottom of the visible page and the
20614             // obj extends below the crease
20615             if ( bot > clientH && toBot < thresh ) {
20616                 window.scrollTo(sl, st + scrAmt);
20617             }
20618
20619             // Scroll up if the window is scrolled down and the top of the object
20620             // goes above the top border
20621             if ( y < st && st > 0 && y - st < thresh ) {
20622                 window.scrollTo(sl, st - scrAmt);
20623             }
20624
20625             // Scroll right if the obj is beyond the right border and the cursor is
20626             // near the border.
20627             if ( right > clientW && toRight < thresh ) {
20628                 window.scrollTo(sl + scrAmt, st);
20629             }
20630
20631             // Scroll left if the window has been scrolled to the right and the obj
20632             // extends past the left border
20633             if ( x < sl && sl > 0 && x - sl < thresh ) {
20634                 window.scrollTo(sl - scrAmt, st);
20635             }
20636         }
20637     },
20638
20639     /**
20640      * Finds the location the element should be placed if we want to move
20641      * it to where the mouse location less the click offset would place us.
20642      * @method getTargetCoord
20643      * @param {int} iPageX the X coordinate of the click
20644      * @param {int} iPageY the Y coordinate of the click
20645      * @return an object that contains the coordinates (Object.x and Object.y)
20646      * @private
20647      */
20648     getTargetCoord: function(iPageX, iPageY) {
20649
20650
20651         var x = iPageX - this.deltaX;
20652         var y = iPageY - this.deltaY;
20653
20654         if (this.constrainX) {
20655             if (x < this.minX) { x = this.minX; }
20656             if (x > this.maxX) { x = this.maxX; }
20657         }
20658
20659         if (this.constrainY) {
20660             if (y < this.minY) { y = this.minY; }
20661             if (y > this.maxY) { y = this.maxY; }
20662         }
20663
20664         x = this.getTick(x, this.xTicks);
20665         y = this.getTick(y, this.yTicks);
20666
20667
20668         return {x:x, y:y};
20669     },
20670
20671     /*
20672      * Sets up config options specific to this class. Overrides
20673      * Roo.dd.DragDrop, but all versions of this method through the
20674      * inheritance chain are called
20675      */
20676     applyConfig: function() {
20677         Roo.dd.DD.superclass.applyConfig.call(this);
20678         this.scroll = (this.config.scroll !== false);
20679     },
20680
20681     /*
20682      * Event that fires prior to the onMouseDown event.  Overrides
20683      * Roo.dd.DragDrop.
20684      */
20685     b4MouseDown: function(e) {
20686         // this.resetConstraints();
20687         this.autoOffset(e.getPageX(),
20688                             e.getPageY());
20689     },
20690
20691     /*
20692      * Event that fires prior to the onDrag event.  Overrides
20693      * Roo.dd.DragDrop.
20694      */
20695     b4Drag: function(e) {
20696         this.setDragElPos(e.getPageX(),
20697                             e.getPageY());
20698     },
20699
20700     toString: function() {
20701         return ("DD " + this.id);
20702     }
20703
20704     //////////////////////////////////////////////////////////////////////////
20705     // Debugging ygDragDrop events that can be overridden
20706     //////////////////////////////////////////////////////////////////////////
20707     /*
20708     startDrag: function(x, y) {
20709     },
20710
20711     onDrag: function(e) {
20712     },
20713
20714     onDragEnter: function(e, id) {
20715     },
20716
20717     onDragOver: function(e, id) {
20718     },
20719
20720     onDragOut: function(e, id) {
20721     },
20722
20723     onDragDrop: function(e, id) {
20724     },
20725
20726     endDrag: function(e) {
20727     }
20728
20729     */
20730
20731 });/*
20732  * Based on:
20733  * Ext JS Library 1.1.1
20734  * Copyright(c) 2006-2007, Ext JS, LLC.
20735  *
20736  * Originally Released Under LGPL - original licence link has changed is not relivant.
20737  *
20738  * Fork - LGPL
20739  * <script type="text/javascript">
20740  */
20741
20742 /**
20743  * @class Roo.dd.DDProxy
20744  * A DragDrop implementation that inserts an empty, bordered div into
20745  * the document that follows the cursor during drag operations.  At the time of
20746  * the click, the frame div is resized to the dimensions of the linked html
20747  * element, and moved to the exact location of the linked element.
20748  *
20749  * References to the "frame" element refer to the single proxy element that
20750  * was created to be dragged in place of all DDProxy elements on the
20751  * page.
20752  *
20753  * @extends Roo.dd.DD
20754  * @constructor
20755  * @param {String} id the id of the linked html element
20756  * @param {String} sGroup the group of related DragDrop objects
20757  * @param {object} config an object containing configurable attributes
20758  *                Valid properties for DDProxy in addition to those in DragDrop:
20759  *                   resizeFrame, centerFrame, dragElId
20760  */
20761 Roo.dd.DDProxy = function(id, sGroup, config) {
20762     if (id) {
20763         this.init(id, sGroup, config);
20764         this.initFrame();
20765     }
20766 };
20767
20768 /**
20769  * The default drag frame div id
20770  * @property Roo.dd.DDProxy.dragElId
20771  * @type String
20772  * @static
20773  */
20774 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20775
20776 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20777
20778     /**
20779      * By default we resize the drag frame to be the same size as the element
20780      * we want to drag (this is to get the frame effect).  We can turn it off
20781      * if we want a different behavior.
20782      * @property resizeFrame
20783      * @type boolean
20784      */
20785     resizeFrame: true,
20786
20787     /**
20788      * By default the frame is positioned exactly where the drag element is, so
20789      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20790      * you do not have constraints on the obj is to have the drag frame centered
20791      * around the cursor.  Set centerFrame to true for this effect.
20792      * @property centerFrame
20793      * @type boolean
20794      */
20795     centerFrame: false,
20796
20797     /**
20798      * Creates the proxy element if it does not yet exist
20799      * @method createFrame
20800      */
20801     createFrame: function() {
20802         var self = this;
20803         var body = document.body;
20804
20805         if (!body || !body.firstChild) {
20806             setTimeout( function() { self.createFrame(); }, 50 );
20807             return;
20808         }
20809
20810         var div = this.getDragEl();
20811
20812         if (!div) {
20813             div    = document.createElement("div");
20814             div.id = this.dragElId;
20815             var s  = div.style;
20816
20817             s.position   = "absolute";
20818             s.visibility = "hidden";
20819             s.cursor     = "move";
20820             s.border     = "2px solid #aaa";
20821             s.zIndex     = 999;
20822
20823             // appendChild can blow up IE if invoked prior to the window load event
20824             // while rendering a table.  It is possible there are other scenarios
20825             // that would cause this to happen as well.
20826             body.insertBefore(div, body.firstChild);
20827         }
20828     },
20829
20830     /**
20831      * Initialization for the drag frame element.  Must be called in the
20832      * constructor of all subclasses
20833      * @method initFrame
20834      */
20835     initFrame: function() {
20836         this.createFrame();
20837     },
20838
20839     applyConfig: function() {
20840         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20841
20842         this.resizeFrame = (this.config.resizeFrame !== false);
20843         this.centerFrame = (this.config.centerFrame);
20844         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20845     },
20846
20847     /**
20848      * Resizes the drag frame to the dimensions of the clicked object, positions
20849      * it over the object, and finally displays it
20850      * @method showFrame
20851      * @param {int} iPageX X click position
20852      * @param {int} iPageY Y click position
20853      * @private
20854      */
20855     showFrame: function(iPageX, iPageY) {
20856         var el = this.getEl();
20857         var dragEl = this.getDragEl();
20858         var s = dragEl.style;
20859
20860         this._resizeProxy();
20861
20862         if (this.centerFrame) {
20863             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20864                            Math.round(parseInt(s.height, 10)/2) );
20865         }
20866
20867         this.setDragElPos(iPageX, iPageY);
20868
20869         Roo.fly(dragEl).show();
20870     },
20871
20872     /**
20873      * The proxy is automatically resized to the dimensions of the linked
20874      * element when a drag is initiated, unless resizeFrame is set to false
20875      * @method _resizeProxy
20876      * @private
20877      */
20878     _resizeProxy: function() {
20879         if (this.resizeFrame) {
20880             var el = this.getEl();
20881             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20882         }
20883     },
20884
20885     // overrides Roo.dd.DragDrop
20886     b4MouseDown: function(e) {
20887         var x = e.getPageX();
20888         var y = e.getPageY();
20889         this.autoOffset(x, y);
20890         this.setDragElPos(x, y);
20891     },
20892
20893     // overrides Roo.dd.DragDrop
20894     b4StartDrag: function(x, y) {
20895         // show the drag frame
20896         this.showFrame(x, y);
20897     },
20898
20899     // overrides Roo.dd.DragDrop
20900     b4EndDrag: function(e) {
20901         Roo.fly(this.getDragEl()).hide();
20902     },
20903
20904     // overrides Roo.dd.DragDrop
20905     // By default we try to move the element to the last location of the frame.
20906     // This is so that the default behavior mirrors that of Roo.dd.DD.
20907     endDrag: function(e) {
20908
20909         var lel = this.getEl();
20910         var del = this.getDragEl();
20911
20912         // Show the drag frame briefly so we can get its position
20913         del.style.visibility = "";
20914
20915         this.beforeMove();
20916         // Hide the linked element before the move to get around a Safari
20917         // rendering bug.
20918         lel.style.visibility = "hidden";
20919         Roo.dd.DDM.moveToEl(lel, del);
20920         del.style.visibility = "hidden";
20921         lel.style.visibility = "";
20922
20923         this.afterDrag();
20924     },
20925
20926     beforeMove : function(){
20927
20928     },
20929
20930     afterDrag : function(){
20931
20932     },
20933
20934     toString: function() {
20935         return ("DDProxy " + this.id);
20936     }
20937
20938 });
20939 /*
20940  * Based on:
20941  * Ext JS Library 1.1.1
20942  * Copyright(c) 2006-2007, Ext JS, LLC.
20943  *
20944  * Originally Released Under LGPL - original licence link has changed is not relivant.
20945  *
20946  * Fork - LGPL
20947  * <script type="text/javascript">
20948  */
20949
20950  /**
20951  * @class Roo.dd.DDTarget
20952  * A DragDrop implementation that does not move, but can be a drop
20953  * target.  You would get the same result by simply omitting implementation
20954  * for the event callbacks, but this way we reduce the processing cost of the
20955  * event listener and the callbacks.
20956  * @extends Roo.dd.DragDrop
20957  * @constructor
20958  * @param {String} id the id of the element that is a drop target
20959  * @param {String} sGroup the group of related DragDrop objects
20960  * @param {object} config an object containing configurable attributes
20961  *                 Valid properties for DDTarget in addition to those in
20962  *                 DragDrop:
20963  *                    none
20964  */
20965 Roo.dd.DDTarget = function(id, sGroup, config) {
20966     if (id) {
20967         this.initTarget(id, sGroup, config);
20968     }
20969     if (config.listeners || config.events) { 
20970        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20971             listeners : config.listeners || {}, 
20972             events : config.events || {} 
20973         });    
20974     }
20975 };
20976
20977 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20978 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20979     toString: function() {
20980         return ("DDTarget " + this.id);
20981     }
20982 });
20983 /*
20984  * Based on:
20985  * Ext JS Library 1.1.1
20986  * Copyright(c) 2006-2007, Ext JS, LLC.
20987  *
20988  * Originally Released Under LGPL - original licence link has changed is not relivant.
20989  *
20990  * Fork - LGPL
20991  * <script type="text/javascript">
20992  */
20993  
20994
20995 /**
20996  * @class Roo.dd.ScrollManager
20997  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20998  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20999  * @singleton
21000  */
21001 Roo.dd.ScrollManager = function(){
21002     var ddm = Roo.dd.DragDropMgr;
21003     var els = {};
21004     var dragEl = null;
21005     var proc = {};
21006     
21007     
21008     
21009     var onStop = function(e){
21010         dragEl = null;
21011         clearProc();
21012     };
21013     
21014     var triggerRefresh = function(){
21015         if(ddm.dragCurrent){
21016              ddm.refreshCache(ddm.dragCurrent.groups);
21017         }
21018     };
21019     
21020     var doScroll = function(){
21021         if(ddm.dragCurrent){
21022             var dds = Roo.dd.ScrollManager;
21023             if(!dds.animate){
21024                 if(proc.el.scroll(proc.dir, dds.increment)){
21025                     triggerRefresh();
21026                 }
21027             }else{
21028                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21029             }
21030         }
21031     };
21032     
21033     var clearProc = function(){
21034         if(proc.id){
21035             clearInterval(proc.id);
21036         }
21037         proc.id = 0;
21038         proc.el = null;
21039         proc.dir = "";
21040     };
21041     
21042     var startProc = function(el, dir){
21043          Roo.log('scroll startproc');
21044         clearProc();
21045         proc.el = el;
21046         proc.dir = dir;
21047         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21048     };
21049     
21050     var onFire = function(e, isDrop){
21051        
21052         if(isDrop || !ddm.dragCurrent){ return; }
21053         var dds = Roo.dd.ScrollManager;
21054         if(!dragEl || dragEl != ddm.dragCurrent){
21055             dragEl = ddm.dragCurrent;
21056             // refresh regions on drag start
21057             dds.refreshCache();
21058         }
21059         
21060         var xy = Roo.lib.Event.getXY(e);
21061         var pt = new Roo.lib.Point(xy[0], xy[1]);
21062         for(var id in els){
21063             var el = els[id], r = el._region;
21064             if(r && r.contains(pt) && el.isScrollable()){
21065                 if(r.bottom - pt.y <= dds.thresh){
21066                     if(proc.el != el){
21067                         startProc(el, "down");
21068                     }
21069                     return;
21070                 }else if(r.right - pt.x <= dds.thresh){
21071                     if(proc.el != el){
21072                         startProc(el, "left");
21073                     }
21074                     return;
21075                 }else if(pt.y - r.top <= dds.thresh){
21076                     if(proc.el != el){
21077                         startProc(el, "up");
21078                     }
21079                     return;
21080                 }else if(pt.x - r.left <= dds.thresh){
21081                     if(proc.el != el){
21082                         startProc(el, "right");
21083                     }
21084                     return;
21085                 }
21086             }
21087         }
21088         clearProc();
21089     };
21090     
21091     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21092     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21093     
21094     return {
21095         /**
21096          * Registers new overflow element(s) to auto scroll
21097          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21098          */
21099         register : function(el){
21100             if(el instanceof Array){
21101                 for(var i = 0, len = el.length; i < len; i++) {
21102                         this.register(el[i]);
21103                 }
21104             }else{
21105                 el = Roo.get(el);
21106                 els[el.id] = el;
21107             }
21108             Roo.dd.ScrollManager.els = els;
21109         },
21110         
21111         /**
21112          * Unregisters overflow element(s) so they are no longer scrolled
21113          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21114          */
21115         unregister : function(el){
21116             if(el instanceof Array){
21117                 for(var i = 0, len = el.length; i < len; i++) {
21118                         this.unregister(el[i]);
21119                 }
21120             }else{
21121                 el = Roo.get(el);
21122                 delete els[el.id];
21123             }
21124         },
21125         
21126         /**
21127          * The number of pixels from the edge of a container the pointer needs to be to 
21128          * trigger scrolling (defaults to 25)
21129          * @type Number
21130          */
21131         thresh : 25,
21132         
21133         /**
21134          * The number of pixels to scroll in each scroll increment (defaults to 50)
21135          * @type Number
21136          */
21137         increment : 100,
21138         
21139         /**
21140          * The frequency of scrolls in milliseconds (defaults to 500)
21141          * @type Number
21142          */
21143         frequency : 500,
21144         
21145         /**
21146          * True to animate the scroll (defaults to true)
21147          * @type Boolean
21148          */
21149         animate: true,
21150         
21151         /**
21152          * The animation duration in seconds - 
21153          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21154          * @type Number
21155          */
21156         animDuration: .4,
21157         
21158         /**
21159          * Manually trigger a cache refresh.
21160          */
21161         refreshCache : function(){
21162             for(var id in els){
21163                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21164                     els[id]._region = els[id].getRegion();
21165                 }
21166             }
21167         }
21168     };
21169 }();/*
21170  * Based on:
21171  * Ext JS Library 1.1.1
21172  * Copyright(c) 2006-2007, Ext JS, LLC.
21173  *
21174  * Originally Released Under LGPL - original licence link has changed is not relivant.
21175  *
21176  * Fork - LGPL
21177  * <script type="text/javascript">
21178  */
21179  
21180
21181 /**
21182  * @class Roo.dd.Registry
21183  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21184  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21185  * @singleton
21186  */
21187 Roo.dd.Registry = function(){
21188     var elements = {}; 
21189     var handles = {}; 
21190     var autoIdSeed = 0;
21191
21192     var getId = function(el, autogen){
21193         if(typeof el == "string"){
21194             return el;
21195         }
21196         var id = el.id;
21197         if(!id && autogen !== false){
21198             id = "roodd-" + (++autoIdSeed);
21199             el.id = id;
21200         }
21201         return id;
21202     };
21203     
21204     return {
21205     /**
21206      * Register a drag drop element
21207      * @param {String|HTMLElement} element The id or DOM node to register
21208      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21209      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21210      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21211      * populated in the data object (if applicable):
21212      * <pre>
21213 Value      Description<br />
21214 ---------  ------------------------------------------<br />
21215 handles    Array of DOM nodes that trigger dragging<br />
21216            for the element being registered<br />
21217 isHandle   True if the element passed in triggers<br />
21218            dragging itself, else false
21219 </pre>
21220      */
21221         register : function(el, data){
21222             data = data || {};
21223             if(typeof el == "string"){
21224                 el = document.getElementById(el);
21225             }
21226             data.ddel = el;
21227             elements[getId(el)] = data;
21228             if(data.isHandle !== false){
21229                 handles[data.ddel.id] = data;
21230             }
21231             if(data.handles){
21232                 var hs = data.handles;
21233                 for(var i = 0, len = hs.length; i < len; i++){
21234                         handles[getId(hs[i])] = data;
21235                 }
21236             }
21237         },
21238
21239     /**
21240      * Unregister a drag drop element
21241      * @param {String|HTMLElement}  element The id or DOM node to unregister
21242      */
21243         unregister : function(el){
21244             var id = getId(el, false);
21245             var data = elements[id];
21246             if(data){
21247                 delete elements[id];
21248                 if(data.handles){
21249                     var hs = data.handles;
21250                     for(var i = 0, len = hs.length; i < len; i++){
21251                         delete handles[getId(hs[i], false)];
21252                     }
21253                 }
21254             }
21255         },
21256
21257     /**
21258      * Returns the handle registered for a DOM Node by id
21259      * @param {String|HTMLElement} id The DOM node or id to look up
21260      * @return {Object} handle The custom handle data
21261      */
21262         getHandle : function(id){
21263             if(typeof id != "string"){ // must be element?
21264                 id = id.id;
21265             }
21266             return handles[id];
21267         },
21268
21269     /**
21270      * Returns the handle that is registered for the DOM node that is the target of the event
21271      * @param {Event} e The event
21272      * @return {Object} handle The custom handle data
21273      */
21274         getHandleFromEvent : function(e){
21275             var t = Roo.lib.Event.getTarget(e);
21276             return t ? handles[t.id] : null;
21277         },
21278
21279     /**
21280      * Returns a custom data object that is registered for a DOM node by id
21281      * @param {String|HTMLElement} id The DOM node or id to look up
21282      * @return {Object} data The custom data
21283      */
21284         getTarget : function(id){
21285             if(typeof id != "string"){ // must be element?
21286                 id = id.id;
21287             }
21288             return elements[id];
21289         },
21290
21291     /**
21292      * Returns a custom data object that is registered for the DOM node that is the target of the event
21293      * @param {Event} e The event
21294      * @return {Object} data The custom data
21295      */
21296         getTargetFromEvent : function(e){
21297             var t = Roo.lib.Event.getTarget(e);
21298             return t ? elements[t.id] || handles[t.id] : null;
21299         }
21300     };
21301 }();/*
21302  * Based on:
21303  * Ext JS Library 1.1.1
21304  * Copyright(c) 2006-2007, Ext JS, LLC.
21305  *
21306  * Originally Released Under LGPL - original licence link has changed is not relivant.
21307  *
21308  * Fork - LGPL
21309  * <script type="text/javascript">
21310  */
21311  
21312
21313 /**
21314  * @class Roo.dd.StatusProxy
21315  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21316  * default drag proxy used by all Roo.dd components.
21317  * @constructor
21318  * @param {Object} config
21319  */
21320 Roo.dd.StatusProxy = function(config){
21321     Roo.apply(this, config);
21322     this.id = this.id || Roo.id();
21323     this.el = new Roo.Layer({
21324         dh: {
21325             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21326                 {tag: "div", cls: "x-dd-drop-icon"},
21327                 {tag: "div", cls: "x-dd-drag-ghost"}
21328             ]
21329         }, 
21330         shadow: !config || config.shadow !== false
21331     });
21332     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21333     this.dropStatus = this.dropNotAllowed;
21334 };
21335
21336 Roo.dd.StatusProxy.prototype = {
21337     /**
21338      * @cfg {String} dropAllowed
21339      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21340      */
21341     dropAllowed : "x-dd-drop-ok",
21342     /**
21343      * @cfg {String} dropNotAllowed
21344      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21345      */
21346     dropNotAllowed : "x-dd-drop-nodrop",
21347
21348     /**
21349      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21350      * over the current target element.
21351      * @param {String} cssClass The css class for the new drop status indicator image
21352      */
21353     setStatus : function(cssClass){
21354         cssClass = cssClass || this.dropNotAllowed;
21355         if(this.dropStatus != cssClass){
21356             this.el.replaceClass(this.dropStatus, cssClass);
21357             this.dropStatus = cssClass;
21358         }
21359     },
21360
21361     /**
21362      * Resets the status indicator to the default dropNotAllowed value
21363      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21364      */
21365     reset : function(clearGhost){
21366         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21367         this.dropStatus = this.dropNotAllowed;
21368         if(clearGhost){
21369             this.ghost.update("");
21370         }
21371     },
21372
21373     /**
21374      * Updates the contents of the ghost element
21375      * @param {String} html The html that will replace the current innerHTML of the ghost element
21376      */
21377     update : function(html){
21378         if(typeof html == "string"){
21379             this.ghost.update(html);
21380         }else{
21381             this.ghost.update("");
21382             html.style.margin = "0";
21383             this.ghost.dom.appendChild(html);
21384         }
21385         // ensure float = none set?? cant remember why though.
21386         var el = this.ghost.dom.firstChild;
21387                 if(el){
21388                         Roo.fly(el).setStyle('float', 'none');
21389                 }
21390     },
21391     
21392     /**
21393      * Returns the underlying proxy {@link Roo.Layer}
21394      * @return {Roo.Layer} el
21395     */
21396     getEl : function(){
21397         return this.el;
21398     },
21399
21400     /**
21401      * Returns the ghost element
21402      * @return {Roo.Element} el
21403      */
21404     getGhost : function(){
21405         return this.ghost;
21406     },
21407
21408     /**
21409      * Hides the proxy
21410      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21411      */
21412     hide : function(clear){
21413         this.el.hide();
21414         if(clear){
21415             this.reset(true);
21416         }
21417     },
21418
21419     /**
21420      * Stops the repair animation if it's currently running
21421      */
21422     stop : function(){
21423         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21424             this.anim.stop();
21425         }
21426     },
21427
21428     /**
21429      * Displays this proxy
21430      */
21431     show : function(){
21432         this.el.show();
21433     },
21434
21435     /**
21436      * Force the Layer to sync its shadow and shim positions to the element
21437      */
21438     sync : function(){
21439         this.el.sync();
21440     },
21441
21442     /**
21443      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21444      * invalid drop operation by the item being dragged.
21445      * @param {Array} xy The XY position of the element ([x, y])
21446      * @param {Function} callback The function to call after the repair is complete
21447      * @param {Object} scope The scope in which to execute the callback
21448      */
21449     repair : function(xy, callback, scope){
21450         this.callback = callback;
21451         this.scope = scope;
21452         if(xy && this.animRepair !== false){
21453             this.el.addClass("x-dd-drag-repair");
21454             this.el.hideUnders(true);
21455             this.anim = this.el.shift({
21456                 duration: this.repairDuration || .5,
21457                 easing: 'easeOut',
21458                 xy: xy,
21459                 stopFx: true,
21460                 callback: this.afterRepair,
21461                 scope: this
21462             });
21463         }else{
21464             this.afterRepair();
21465         }
21466     },
21467
21468     // private
21469     afterRepair : function(){
21470         this.hide(true);
21471         if(typeof this.callback == "function"){
21472             this.callback.call(this.scope || this);
21473         }
21474         this.callback = null;
21475         this.scope = null;
21476     }
21477 };/*
21478  * Based on:
21479  * Ext JS Library 1.1.1
21480  * Copyright(c) 2006-2007, Ext JS, LLC.
21481  *
21482  * Originally Released Under LGPL - original licence link has changed is not relivant.
21483  *
21484  * Fork - LGPL
21485  * <script type="text/javascript">
21486  */
21487
21488 /**
21489  * @class Roo.dd.DragSource
21490  * @extends Roo.dd.DDProxy
21491  * A simple class that provides the basic implementation needed to make any element draggable.
21492  * @constructor
21493  * @param {String/HTMLElement/Element} el The container element
21494  * @param {Object} config
21495  */
21496 Roo.dd.DragSource = function(el, config){
21497     this.el = Roo.get(el);
21498     this.dragData = {};
21499     
21500     Roo.apply(this, config);
21501     
21502     if(!this.proxy){
21503         this.proxy = new Roo.dd.StatusProxy();
21504     }
21505
21506     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21507           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21508     
21509     this.dragging = false;
21510 };
21511
21512 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21513     /**
21514      * @cfg {String} dropAllowed
21515      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21516      */
21517     dropAllowed : "x-dd-drop-ok",
21518     /**
21519      * @cfg {String} dropNotAllowed
21520      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21521      */
21522     dropNotAllowed : "x-dd-drop-nodrop",
21523
21524     /**
21525      * Returns the data object associated with this drag source
21526      * @return {Object} data An object containing arbitrary data
21527      */
21528     getDragData : function(e){
21529         return this.dragData;
21530     },
21531
21532     // private
21533     onDragEnter : function(e, id){
21534         var target = Roo.dd.DragDropMgr.getDDById(id);
21535         this.cachedTarget = target;
21536         if(this.beforeDragEnter(target, e, id) !== false){
21537             if(target.isNotifyTarget){
21538                 var status = target.notifyEnter(this, e, this.dragData);
21539                 this.proxy.setStatus(status);
21540             }else{
21541                 this.proxy.setStatus(this.dropAllowed);
21542             }
21543             
21544             if(this.afterDragEnter){
21545                 /**
21546                  * An empty function by default, but provided so that you can perform a custom action
21547                  * when the dragged item enters the drop target by providing an implementation.
21548                  * @param {Roo.dd.DragDrop} target The drop target
21549                  * @param {Event} e The event object
21550                  * @param {String} id The id of the dragged element
21551                  * @method afterDragEnter
21552                  */
21553                 this.afterDragEnter(target, e, id);
21554             }
21555         }
21556     },
21557
21558     /**
21559      * An empty function by default, but provided so that you can perform a custom action
21560      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21561      * @param {Roo.dd.DragDrop} target The drop target
21562      * @param {Event} e The event object
21563      * @param {String} id The id of the dragged element
21564      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21565      */
21566     beforeDragEnter : function(target, e, id){
21567         return true;
21568     },
21569
21570     // private
21571     alignElWithMouse: function() {
21572         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21573         this.proxy.sync();
21574     },
21575
21576     // private
21577     onDragOver : function(e, id){
21578         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21579         if(this.beforeDragOver(target, e, id) !== false){
21580             if(target.isNotifyTarget){
21581                 var status = target.notifyOver(this, e, this.dragData);
21582                 this.proxy.setStatus(status);
21583             }
21584
21585             if(this.afterDragOver){
21586                 /**
21587                  * An empty function by default, but provided so that you can perform a custom action
21588                  * while the dragged item is over the drop target by providing an implementation.
21589                  * @param {Roo.dd.DragDrop} target The drop target
21590                  * @param {Event} e The event object
21591                  * @param {String} id The id of the dragged element
21592                  * @method afterDragOver
21593                  */
21594                 this.afterDragOver(target, e, id);
21595             }
21596         }
21597     },
21598
21599     /**
21600      * An empty function by default, but provided so that you can perform a custom action
21601      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21602      * @param {Roo.dd.DragDrop} target The drop target
21603      * @param {Event} e The event object
21604      * @param {String} id The id of the dragged element
21605      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21606      */
21607     beforeDragOver : function(target, e, id){
21608         return true;
21609     },
21610
21611     // private
21612     onDragOut : function(e, id){
21613         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21614         if(this.beforeDragOut(target, e, id) !== false){
21615             if(target.isNotifyTarget){
21616                 target.notifyOut(this, e, this.dragData);
21617             }
21618             this.proxy.reset();
21619             if(this.afterDragOut){
21620                 /**
21621                  * An empty function by default, but provided so that you can perform a custom action
21622                  * after the dragged item is dragged out of the target without dropping.
21623                  * @param {Roo.dd.DragDrop} target The drop target
21624                  * @param {Event} e The event object
21625                  * @param {String} id The id of the dragged element
21626                  * @method afterDragOut
21627                  */
21628                 this.afterDragOut(target, e, id);
21629             }
21630         }
21631         this.cachedTarget = null;
21632     },
21633
21634     /**
21635      * An empty function by default, but provided so that you can perform a custom action before the dragged
21636      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21637      * @param {Roo.dd.DragDrop} target The drop target
21638      * @param {Event} e The event object
21639      * @param {String} id The id of the dragged element
21640      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21641      */
21642     beforeDragOut : function(target, e, id){
21643         return true;
21644     },
21645     
21646     // private
21647     onDragDrop : function(e, id){
21648         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21649         if(this.beforeDragDrop(target, e, id) !== false){
21650             if(target.isNotifyTarget){
21651                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21652                     this.onValidDrop(target, e, id);
21653                 }else{
21654                     this.onInvalidDrop(target, e, id);
21655                 }
21656             }else{
21657                 this.onValidDrop(target, e, id);
21658             }
21659             
21660             if(this.afterDragDrop){
21661                 /**
21662                  * An empty function by default, but provided so that you can perform a custom action
21663                  * after a valid drag drop has occurred by providing an implementation.
21664                  * @param {Roo.dd.DragDrop} target The drop target
21665                  * @param {Event} e The event object
21666                  * @param {String} id The id of the dropped element
21667                  * @method afterDragDrop
21668                  */
21669                 this.afterDragDrop(target, e, id);
21670             }
21671         }
21672         delete this.cachedTarget;
21673     },
21674
21675     /**
21676      * An empty function by default, but provided so that you can perform a custom action before the dragged
21677      * item is dropped onto the target and optionally cancel the onDragDrop.
21678      * @param {Roo.dd.DragDrop} target The drop target
21679      * @param {Event} e The event object
21680      * @param {String} id The id of the dragged element
21681      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21682      */
21683     beforeDragDrop : function(target, e, id){
21684         return true;
21685     },
21686
21687     // private
21688     onValidDrop : function(target, e, id){
21689         this.hideProxy();
21690         if(this.afterValidDrop){
21691             /**
21692              * An empty function by default, but provided so that you can perform a custom action
21693              * after a valid drop has occurred by providing an implementation.
21694              * @param {Object} target The target DD 
21695              * @param {Event} e The event object
21696              * @param {String} id The id of the dropped element
21697              * @method afterInvalidDrop
21698              */
21699             this.afterValidDrop(target, e, id);
21700         }
21701     },
21702
21703     // private
21704     getRepairXY : function(e, data){
21705         return this.el.getXY();  
21706     },
21707
21708     // private
21709     onInvalidDrop : function(target, e, id){
21710         this.beforeInvalidDrop(target, e, id);
21711         if(this.cachedTarget){
21712             if(this.cachedTarget.isNotifyTarget){
21713                 this.cachedTarget.notifyOut(this, e, this.dragData);
21714             }
21715             this.cacheTarget = null;
21716         }
21717         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21718
21719         if(this.afterInvalidDrop){
21720             /**
21721              * An empty function by default, but provided so that you can perform a custom action
21722              * after an invalid drop has occurred by providing an implementation.
21723              * @param {Event} e The event object
21724              * @param {String} id The id of the dropped element
21725              * @method afterInvalidDrop
21726              */
21727             this.afterInvalidDrop(e, id);
21728         }
21729     },
21730
21731     // private
21732     afterRepair : function(){
21733         if(Roo.enableFx){
21734             this.el.highlight(this.hlColor || "c3daf9");
21735         }
21736         this.dragging = false;
21737     },
21738
21739     /**
21740      * An empty function by default, but provided so that you can perform a custom action after an invalid
21741      * drop has occurred.
21742      * @param {Roo.dd.DragDrop} target The drop target
21743      * @param {Event} e The event object
21744      * @param {String} id The id of the dragged element
21745      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21746      */
21747     beforeInvalidDrop : function(target, e, id){
21748         return true;
21749     },
21750
21751     // private
21752     handleMouseDown : function(e){
21753         if(this.dragging) {
21754             return;
21755         }
21756         var data = this.getDragData(e);
21757         if(data && this.onBeforeDrag(data, e) !== false){
21758             this.dragData = data;
21759             this.proxy.stop();
21760             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21761         } 
21762     },
21763
21764     /**
21765      * An empty function by default, but provided so that you can perform a custom action before the initial
21766      * drag event begins and optionally cancel it.
21767      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21768      * @param {Event} e The event object
21769      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21770      */
21771     onBeforeDrag : function(data, e){
21772         return true;
21773     },
21774
21775     /**
21776      * An empty function by default, but provided so that you can perform a custom action once the initial
21777      * drag event has begun.  The drag cannot be canceled from this function.
21778      * @param {Number} x The x position of the click on the dragged object
21779      * @param {Number} y The y position of the click on the dragged object
21780      */
21781     onStartDrag : Roo.emptyFn,
21782
21783     // private - YUI override
21784     startDrag : function(x, y){
21785         this.proxy.reset();
21786         this.dragging = true;
21787         this.proxy.update("");
21788         this.onInitDrag(x, y);
21789         this.proxy.show();
21790     },
21791
21792     // private
21793     onInitDrag : function(x, y){
21794         var clone = this.el.dom.cloneNode(true);
21795         clone.id = Roo.id(); // prevent duplicate ids
21796         this.proxy.update(clone);
21797         this.onStartDrag(x, y);
21798         return true;
21799     },
21800
21801     /**
21802      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21803      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21804      */
21805     getProxy : function(){
21806         return this.proxy;  
21807     },
21808
21809     /**
21810      * Hides the drag source's {@link Roo.dd.StatusProxy}
21811      */
21812     hideProxy : function(){
21813         this.proxy.hide();  
21814         this.proxy.reset(true);
21815         this.dragging = false;
21816     },
21817
21818     // private
21819     triggerCacheRefresh : function(){
21820         Roo.dd.DDM.refreshCache(this.groups);
21821     },
21822
21823     // private - override to prevent hiding
21824     b4EndDrag: function(e) {
21825     },
21826
21827     // private - override to prevent moving
21828     endDrag : function(e){
21829         this.onEndDrag(this.dragData, e);
21830     },
21831
21832     // private
21833     onEndDrag : function(data, e){
21834     },
21835     
21836     // private - pin to cursor
21837     autoOffset : function(x, y) {
21838         this.setDelta(-12, -20);
21839     }    
21840 });/*
21841  * Based on:
21842  * Ext JS Library 1.1.1
21843  * Copyright(c) 2006-2007, Ext JS, LLC.
21844  *
21845  * Originally Released Under LGPL - original licence link has changed is not relivant.
21846  *
21847  * Fork - LGPL
21848  * <script type="text/javascript">
21849  */
21850
21851
21852 /**
21853  * @class Roo.dd.DropTarget
21854  * @extends Roo.dd.DDTarget
21855  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21856  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21857  * @constructor
21858  * @param {String/HTMLElement/Element} el The container element
21859  * @param {Object} config
21860  */
21861 Roo.dd.DropTarget = function(el, config){
21862     this.el = Roo.get(el);
21863     
21864     var listeners = false; ;
21865     if (config && config.listeners) {
21866         listeners= config.listeners;
21867         delete config.listeners;
21868     }
21869     Roo.apply(this, config);
21870     
21871     if(this.containerScroll){
21872         Roo.dd.ScrollManager.register(this.el);
21873     }
21874     this.addEvents( {
21875          /**
21876          * @scope Roo.dd.DropTarget
21877          */
21878          
21879          /**
21880          * @event enter
21881          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21882          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21883          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21884          * 
21885          * IMPORTANT : it should set this.overClass and this.dropAllowed
21886          * 
21887          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21888          * @param {Event} e The event
21889          * @param {Object} data An object containing arbitrary data supplied by the drag source
21890          */
21891         "enter" : true,
21892         
21893          /**
21894          * @event over
21895          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21896          * This method will be called on every mouse movement while the drag source is over the drop target.
21897          * This default implementation simply returns the dropAllowed config value.
21898          * 
21899          * IMPORTANT : it should set this.dropAllowed
21900          * 
21901          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21902          * @param {Event} e The event
21903          * @param {Object} data An object containing arbitrary data supplied by the drag source
21904          
21905          */
21906         "over" : true,
21907         /**
21908          * @event out
21909          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21910          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21911          * overClass (if any) from the drop element.
21912          * 
21913          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21914          * @param {Event} e The event
21915          * @param {Object} data An object containing arbitrary data supplied by the drag source
21916          */
21917          "out" : true,
21918          
21919         /**
21920          * @event drop
21921          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21922          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21923          * implementation that does something to process the drop event and returns true so that the drag source's
21924          * repair action does not run.
21925          * 
21926          * IMPORTANT : it should set this.success
21927          * 
21928          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21929          * @param {Event} e The event
21930          * @param {Object} data An object containing arbitrary data supplied by the drag source
21931         */
21932          "drop" : true
21933     });
21934             
21935      
21936     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21937         this.el.dom, 
21938         this.ddGroup || this.group,
21939         {
21940             isTarget: true,
21941             listeners : listeners || {} 
21942            
21943         
21944         }
21945     );
21946
21947 };
21948
21949 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21950     /**
21951      * @cfg {String} overClass
21952      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21953      */
21954      /**
21955      * @cfg {String} ddGroup
21956      * The drag drop group to handle drop events for
21957      */
21958      
21959     /**
21960      * @cfg {String} dropAllowed
21961      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21962      */
21963     dropAllowed : "x-dd-drop-ok",
21964     /**
21965      * @cfg {String} dropNotAllowed
21966      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21967      */
21968     dropNotAllowed : "x-dd-drop-nodrop",
21969     /**
21970      * @cfg {boolean} success
21971      * set this after drop listener.. 
21972      */
21973     success : false,
21974     /**
21975      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21976      * if the drop point is valid for over/enter..
21977      */
21978     valid : false,
21979     // private
21980     isTarget : true,
21981
21982     // private
21983     isNotifyTarget : true,
21984     
21985     /**
21986      * @hide
21987      */
21988     notifyEnter : function(dd, e, data)
21989     {
21990         this.valid = true;
21991         this.fireEvent('enter', dd, e, data);
21992         if(this.overClass){
21993             this.el.addClass(this.overClass);
21994         }
21995         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21996             this.valid ? this.dropAllowed : this.dropNotAllowed
21997         );
21998     },
21999
22000     /**
22001      * @hide
22002      */
22003     notifyOver : function(dd, e, data)
22004     {
22005         this.valid = true;
22006         this.fireEvent('over', dd, e, data);
22007         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22008             this.valid ? this.dropAllowed : this.dropNotAllowed
22009         );
22010     },
22011
22012     /**
22013      * @hide
22014      */
22015     notifyOut : function(dd, e, data)
22016     {
22017         this.fireEvent('out', dd, e, data);
22018         if(this.overClass){
22019             this.el.removeClass(this.overClass);
22020         }
22021     },
22022
22023     /**
22024      * @hide
22025      */
22026     notifyDrop : function(dd, e, data)
22027     {
22028         this.success = false;
22029         this.fireEvent('drop', dd, e, data);
22030         return this.success;
22031     }
22032 });/*
22033  * Based on:
22034  * Ext JS Library 1.1.1
22035  * Copyright(c) 2006-2007, Ext JS, LLC.
22036  *
22037  * Originally Released Under LGPL - original licence link has changed is not relivant.
22038  *
22039  * Fork - LGPL
22040  * <script type="text/javascript">
22041  */
22042
22043
22044 /**
22045  * @class Roo.dd.DragZone
22046  * @extends Roo.dd.DragSource
22047  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22048  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22049  * @constructor
22050  * @param {String/HTMLElement/Element} el The container element
22051  * @param {Object} config
22052  */
22053 Roo.dd.DragZone = function(el, config){
22054     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22055     if(this.containerScroll){
22056         Roo.dd.ScrollManager.register(this.el);
22057     }
22058 };
22059
22060 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22061     /**
22062      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22063      * for auto scrolling during drag operations.
22064      */
22065     /**
22066      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22067      * method after a failed drop (defaults to "c3daf9" - light blue)
22068      */
22069
22070     /**
22071      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22072      * for a valid target to drag based on the mouse down. Override this method
22073      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22074      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22075      * @param {EventObject} e The mouse down event
22076      * @return {Object} The dragData
22077      */
22078     getDragData : function(e){
22079         return Roo.dd.Registry.getHandleFromEvent(e);
22080     },
22081     
22082     /**
22083      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22084      * this.dragData.ddel
22085      * @param {Number} x The x position of the click on the dragged object
22086      * @param {Number} y The y position of the click on the dragged object
22087      * @return {Boolean} true to continue the drag, false to cancel
22088      */
22089     onInitDrag : function(x, y){
22090         this.proxy.update(this.dragData.ddel.cloneNode(true));
22091         this.onStartDrag(x, y);
22092         return true;
22093     },
22094     
22095     /**
22096      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22097      */
22098     afterRepair : function(){
22099         if(Roo.enableFx){
22100             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22101         }
22102         this.dragging = false;
22103     },
22104
22105     /**
22106      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22107      * the XY of this.dragData.ddel
22108      * @param {EventObject} e The mouse up event
22109      * @return {Array} The xy location (e.g. [100, 200])
22110      */
22111     getRepairXY : function(e){
22112         return Roo.Element.fly(this.dragData.ddel).getXY();  
22113     }
22114 });/*
22115  * Based on:
22116  * Ext JS Library 1.1.1
22117  * Copyright(c) 2006-2007, Ext JS, LLC.
22118  *
22119  * Originally Released Under LGPL - original licence link has changed is not relivant.
22120  *
22121  * Fork - LGPL
22122  * <script type="text/javascript">
22123  */
22124 /**
22125  * @class Roo.dd.DropZone
22126  * @extends Roo.dd.DropTarget
22127  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22128  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22129  * @constructor
22130  * @param {String/HTMLElement/Element} el The container element
22131  * @param {Object} config
22132  */
22133 Roo.dd.DropZone = function(el, config){
22134     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22135 };
22136
22137 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22138     /**
22139      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22140      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22141      * provide your own custom lookup.
22142      * @param {Event} e The event
22143      * @return {Object} data The custom data
22144      */
22145     getTargetFromEvent : function(e){
22146         return Roo.dd.Registry.getTargetFromEvent(e);
22147     },
22148
22149     /**
22150      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22151      * that it has registered.  This method has no default implementation and should be overridden to provide
22152      * node-specific processing if necessary.
22153      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22154      * {@link #getTargetFromEvent} for this node)
22155      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22156      * @param {Event} e The event
22157      * @param {Object} data An object containing arbitrary data supplied by the drag source
22158      */
22159     onNodeEnter : function(n, dd, e, data){
22160         
22161     },
22162
22163     /**
22164      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22165      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22166      * overridden to provide the proper feedback.
22167      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22168      * {@link #getTargetFromEvent} for this node)
22169      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22170      * @param {Event} e The event
22171      * @param {Object} data An object containing arbitrary data supplied by the drag source
22172      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22173      * underlying {@link Roo.dd.StatusProxy} can be updated
22174      */
22175     onNodeOver : function(n, dd, e, data){
22176         return this.dropAllowed;
22177     },
22178
22179     /**
22180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22181      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22182      * node-specific processing if necessary.
22183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22184      * {@link #getTargetFromEvent} for this node)
22185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22186      * @param {Event} e The event
22187      * @param {Object} data An object containing arbitrary data supplied by the drag source
22188      */
22189     onNodeOut : function(n, dd, e, data){
22190         
22191     },
22192
22193     /**
22194      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22195      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22196      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22198      * {@link #getTargetFromEvent} for this node)
22199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22200      * @param {Event} e The event
22201      * @param {Object} data An object containing arbitrary data supplied by the drag source
22202      * @return {Boolean} True if the drop was valid, else false
22203      */
22204     onNodeDrop : function(n, dd, e, data){
22205         return false;
22206     },
22207
22208     /**
22209      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22210      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22211      * it should be overridden to provide the proper feedback if necessary.
22212      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22213      * @param {Event} e The event
22214      * @param {Object} data An object containing arbitrary data supplied by the drag source
22215      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22216      * underlying {@link Roo.dd.StatusProxy} can be updated
22217      */
22218     onContainerOver : function(dd, e, data){
22219         return this.dropNotAllowed;
22220     },
22221
22222     /**
22223      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22224      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22225      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22226      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22227      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22228      * @param {Event} e The event
22229      * @param {Object} data An object containing arbitrary data supplied by the drag source
22230      * @return {Boolean} True if the drop was valid, else false
22231      */
22232     onContainerDrop : function(dd, e, data){
22233         return false;
22234     },
22235
22236     /**
22237      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22238      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22239      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22240      * you should override this method and provide a custom implementation.
22241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22242      * @param {Event} e The event
22243      * @param {Object} data An object containing arbitrary data supplied by the drag source
22244      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22245      * underlying {@link Roo.dd.StatusProxy} can be updated
22246      */
22247     notifyEnter : function(dd, e, data){
22248         return this.dropNotAllowed;
22249     },
22250
22251     /**
22252      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22253      * This method will be called on every mouse movement while the drag source is over the drop zone.
22254      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22255      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22256      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22257      * registered node, it will call {@link #onContainerOver}.
22258      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22259      * @param {Event} e The event
22260      * @param {Object} data An object containing arbitrary data supplied by the drag source
22261      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22262      * underlying {@link Roo.dd.StatusProxy} can be updated
22263      */
22264     notifyOver : function(dd, e, data){
22265         var n = this.getTargetFromEvent(e);
22266         if(!n){ // not over valid drop target
22267             if(this.lastOverNode){
22268                 this.onNodeOut(this.lastOverNode, dd, e, data);
22269                 this.lastOverNode = null;
22270             }
22271             return this.onContainerOver(dd, e, data);
22272         }
22273         if(this.lastOverNode != n){
22274             if(this.lastOverNode){
22275                 this.onNodeOut(this.lastOverNode, dd, e, data);
22276             }
22277             this.onNodeEnter(n, dd, e, data);
22278             this.lastOverNode = n;
22279         }
22280         return this.onNodeOver(n, dd, e, data);
22281     },
22282
22283     /**
22284      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22285      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22286      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22287      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22288      * @param {Event} e The event
22289      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22290      */
22291     notifyOut : function(dd, e, data){
22292         if(this.lastOverNode){
22293             this.onNodeOut(this.lastOverNode, dd, e, data);
22294             this.lastOverNode = null;
22295         }
22296     },
22297
22298     /**
22299      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22300      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22301      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22302      * otherwise it will call {@link #onContainerDrop}.
22303      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22304      * @param {Event} e The event
22305      * @param {Object} data An object containing arbitrary data supplied by the drag source
22306      * @return {Boolean} True if the drop was valid, else false
22307      */
22308     notifyDrop : function(dd, e, data){
22309         if(this.lastOverNode){
22310             this.onNodeOut(this.lastOverNode, dd, e, data);
22311             this.lastOverNode = null;
22312         }
22313         var n = this.getTargetFromEvent(e);
22314         return n ?
22315             this.onNodeDrop(n, dd, e, data) :
22316             this.onContainerDrop(dd, e, data);
22317     },
22318
22319     // private
22320     triggerCacheRefresh : function(){
22321         Roo.dd.DDM.refreshCache(this.groups);
22322     }  
22323 });/*
22324  * Based on:
22325  * Ext JS Library 1.1.1
22326  * Copyright(c) 2006-2007, Ext JS, LLC.
22327  *
22328  * Originally Released Under LGPL - original licence link has changed is not relivant.
22329  *
22330  * Fork - LGPL
22331  * <script type="text/javascript">
22332  */
22333
22334
22335 /**
22336  * @class Roo.data.SortTypes
22337  * @singleton
22338  * Defines the default sorting (casting?) comparison functions used when sorting data.
22339  */
22340 Roo.data.SortTypes = {
22341     /**
22342      * Default sort that does nothing
22343      * @param {Mixed} s The value being converted
22344      * @return {Mixed} The comparison value
22345      */
22346     none : function(s){
22347         return s;
22348     },
22349     
22350     /**
22351      * The regular expression used to strip tags
22352      * @type {RegExp}
22353      * @property
22354      */
22355     stripTagsRE : /<\/?[^>]+>/gi,
22356     
22357     /**
22358      * Strips all HTML tags to sort on text only
22359      * @param {Mixed} s The value being converted
22360      * @return {String} The comparison value
22361      */
22362     asText : function(s){
22363         return String(s).replace(this.stripTagsRE, "");
22364     },
22365     
22366     /**
22367      * Strips all HTML tags to sort on text only - Case insensitive
22368      * @param {Mixed} s The value being converted
22369      * @return {String} The comparison value
22370      */
22371     asUCText : function(s){
22372         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22373     },
22374     
22375     /**
22376      * Case insensitive string
22377      * @param {Mixed} s The value being converted
22378      * @return {String} The comparison value
22379      */
22380     asUCString : function(s) {
22381         return String(s).toUpperCase();
22382     },
22383     
22384     /**
22385      * Date sorting
22386      * @param {Mixed} s The value being converted
22387      * @return {Number} The comparison value
22388      */
22389     asDate : function(s) {
22390         if(!s){
22391             return 0;
22392         }
22393         if(s instanceof Date){
22394             return s.getTime();
22395         }
22396         return Date.parse(String(s));
22397     },
22398     
22399     /**
22400      * Float sorting
22401      * @param {Mixed} s The value being converted
22402      * @return {Float} The comparison value
22403      */
22404     asFloat : function(s) {
22405         var val = parseFloat(String(s).replace(/,/g, ""));
22406         if(isNaN(val)) {
22407             val = 0;
22408         }
22409         return val;
22410     },
22411     
22412     /**
22413      * Integer sorting
22414      * @param {Mixed} s The value being converted
22415      * @return {Number} The comparison value
22416      */
22417     asInt : function(s) {
22418         var val = parseInt(String(s).replace(/,/g, ""));
22419         if(isNaN(val)) {
22420             val = 0;
22421         }
22422         return val;
22423     }
22424 };/*
22425  * Based on:
22426  * Ext JS Library 1.1.1
22427  * Copyright(c) 2006-2007, Ext JS, LLC.
22428  *
22429  * Originally Released Under LGPL - original licence link has changed is not relivant.
22430  *
22431  * Fork - LGPL
22432  * <script type="text/javascript">
22433  */
22434
22435 /**
22436 * @class Roo.data.Record
22437  * Instances of this class encapsulate both record <em>definition</em> information, and record
22438  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22439  * to access Records cached in an {@link Roo.data.Store} object.<br>
22440  * <p>
22441  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22442  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22443  * objects.<br>
22444  * <p>
22445  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22446  * @constructor
22447  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22448  * {@link #create}. The parameters are the same.
22449  * @param {Array} data An associative Array of data values keyed by the field name.
22450  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22451  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22452  * not specified an integer id is generated.
22453  */
22454 Roo.data.Record = function(data, id){
22455     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22456     this.data = data;
22457 };
22458
22459 /**
22460  * Generate a constructor for a specific record layout.
22461  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22462  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22463  * Each field definition object may contain the following properties: <ul>
22464  * <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,
22465  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22466  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22467  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22468  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22469  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22470  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22471  * this may be omitted.</p></li>
22472  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22473  * <ul><li>auto (Default, implies no conversion)</li>
22474  * <li>string</li>
22475  * <li>int</li>
22476  * <li>float</li>
22477  * <li>boolean</li>
22478  * <li>date</li></ul></p></li>
22479  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22480  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22481  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22482  * by the Reader into an object that will be stored in the Record. It is passed the
22483  * following parameters:<ul>
22484  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22485  * </ul></p></li>
22486  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22487  * </ul>
22488  * <br>usage:<br><pre><code>
22489 var TopicRecord = Roo.data.Record.create(
22490     {name: 'title', mapping: 'topic_title'},
22491     {name: 'author', mapping: 'username'},
22492     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22493     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22494     {name: 'lastPoster', mapping: 'user2'},
22495     {name: 'excerpt', mapping: 'post_text'}
22496 );
22497
22498 var myNewRecord = new TopicRecord({
22499     title: 'Do my job please',
22500     author: 'noobie',
22501     totalPosts: 1,
22502     lastPost: new Date(),
22503     lastPoster: 'Animal',
22504     excerpt: 'No way dude!'
22505 });
22506 myStore.add(myNewRecord);
22507 </code></pre>
22508  * @method create
22509  * @static
22510  */
22511 Roo.data.Record.create = function(o){
22512     var f = function(){
22513         f.superclass.constructor.apply(this, arguments);
22514     };
22515     Roo.extend(f, Roo.data.Record);
22516     var p = f.prototype;
22517     p.fields = new Roo.util.MixedCollection(false, function(field){
22518         return field.name;
22519     });
22520     for(var i = 0, len = o.length; i < len; i++){
22521         p.fields.add(new Roo.data.Field(o[i]));
22522     }
22523     f.getField = function(name){
22524         return p.fields.get(name);  
22525     };
22526     return f;
22527 };
22528
22529 Roo.data.Record.AUTO_ID = 1000;
22530 Roo.data.Record.EDIT = 'edit';
22531 Roo.data.Record.REJECT = 'reject';
22532 Roo.data.Record.COMMIT = 'commit';
22533
22534 Roo.data.Record.prototype = {
22535     /**
22536      * Readonly flag - true if this record has been modified.
22537      * @type Boolean
22538      */
22539     dirty : false,
22540     editing : false,
22541     error: null,
22542     modified: null,
22543
22544     // private
22545     join : function(store){
22546         this.store = store;
22547     },
22548
22549     /**
22550      * Set the named field to the specified value.
22551      * @param {String} name The name of the field to set.
22552      * @param {Object} value The value to set the field to.
22553      */
22554     set : function(name, value){
22555         if(this.data[name] == value){
22556             return;
22557         }
22558         this.dirty = true;
22559         if(!this.modified){
22560             this.modified = {};
22561         }
22562         if(typeof this.modified[name] == 'undefined'){
22563             this.modified[name] = this.data[name];
22564         }
22565         this.data[name] = value;
22566         if(!this.editing && this.store){
22567             this.store.afterEdit(this);
22568         }       
22569     },
22570
22571     /**
22572      * Get the value of the named field.
22573      * @param {String} name The name of the field to get the value of.
22574      * @return {Object} The value of the field.
22575      */
22576     get : function(name){
22577         return this.data[name]; 
22578     },
22579
22580     // private
22581     beginEdit : function(){
22582         this.editing = true;
22583         this.modified = {}; 
22584     },
22585
22586     // private
22587     cancelEdit : function(){
22588         this.editing = false;
22589         delete this.modified;
22590     },
22591
22592     // private
22593     endEdit : function(){
22594         this.editing = false;
22595         if(this.dirty && this.store){
22596             this.store.afterEdit(this);
22597         }
22598     },
22599
22600     /**
22601      * Usually called by the {@link Roo.data.Store} which owns the Record.
22602      * Rejects all changes made to the Record since either creation, or the last commit operation.
22603      * Modified fields are reverted to their original values.
22604      * <p>
22605      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22606      * of reject operations.
22607      */
22608     reject : function(){
22609         var m = this.modified;
22610         for(var n in m){
22611             if(typeof m[n] != "function"){
22612                 this.data[n] = m[n];
22613             }
22614         }
22615         this.dirty = false;
22616         delete this.modified;
22617         this.editing = false;
22618         if(this.store){
22619             this.store.afterReject(this);
22620         }
22621     },
22622
22623     /**
22624      * Usually called by the {@link Roo.data.Store} which owns the Record.
22625      * Commits all changes made to the Record since either creation, or the last commit operation.
22626      * <p>
22627      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22628      * of commit operations.
22629      */
22630     commit : function(){
22631         this.dirty = false;
22632         delete this.modified;
22633         this.editing = false;
22634         if(this.store){
22635             this.store.afterCommit(this);
22636         }
22637     },
22638
22639     // private
22640     hasError : function(){
22641         return this.error != null;
22642     },
22643
22644     // private
22645     clearError : function(){
22646         this.error = null;
22647     },
22648
22649     /**
22650      * Creates a copy of this record.
22651      * @param {String} id (optional) A new record id if you don't want to use this record's id
22652      * @return {Record}
22653      */
22654     copy : function(newId) {
22655         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22656     }
22657 };/*
22658  * Based on:
22659  * Ext JS Library 1.1.1
22660  * Copyright(c) 2006-2007, Ext JS, LLC.
22661  *
22662  * Originally Released Under LGPL - original licence link has changed is not relivant.
22663  *
22664  * Fork - LGPL
22665  * <script type="text/javascript">
22666  */
22667
22668
22669
22670 /**
22671  * @class Roo.data.Store
22672  * @extends Roo.util.Observable
22673  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22674  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22675  * <p>
22676  * 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
22677  * has no knowledge of the format of the data returned by the Proxy.<br>
22678  * <p>
22679  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22680  * instances from the data object. These records are cached and made available through accessor functions.
22681  * @constructor
22682  * Creates a new Store.
22683  * @param {Object} config A config object containing the objects needed for the Store to access data,
22684  * and read the data into Records.
22685  */
22686 Roo.data.Store = function(config){
22687     this.data = new Roo.util.MixedCollection(false);
22688     this.data.getKey = function(o){
22689         return o.id;
22690     };
22691     this.baseParams = {};
22692     // private
22693     this.paramNames = {
22694         "start" : "start",
22695         "limit" : "limit",
22696         "sort" : "sort",
22697         "dir" : "dir",
22698         "multisort" : "_multisort"
22699     };
22700
22701     if(config && config.data){
22702         this.inlineData = config.data;
22703         delete config.data;
22704     }
22705
22706     Roo.apply(this, config);
22707     
22708     if(this.reader){ // reader passed
22709         this.reader = Roo.factory(this.reader, Roo.data);
22710         this.reader.xmodule = this.xmodule || false;
22711         if(!this.recordType){
22712             this.recordType = this.reader.recordType;
22713         }
22714         if(this.reader.onMetaChange){
22715             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22716         }
22717     }
22718
22719     if(this.recordType){
22720         this.fields = this.recordType.prototype.fields;
22721     }
22722     this.modified = [];
22723
22724     this.addEvents({
22725         /**
22726          * @event datachanged
22727          * Fires when the data cache has changed, and a widget which is using this Store
22728          * as a Record cache should refresh its view.
22729          * @param {Store} this
22730          */
22731         datachanged : true,
22732         /**
22733          * @event metachange
22734          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22735          * @param {Store} this
22736          * @param {Object} meta The JSON metadata
22737          */
22738         metachange : true,
22739         /**
22740          * @event add
22741          * Fires when Records have been added to the Store
22742          * @param {Store} this
22743          * @param {Roo.data.Record[]} records The array of Records added
22744          * @param {Number} index The index at which the record(s) were added
22745          */
22746         add : true,
22747         /**
22748          * @event remove
22749          * Fires when a Record has been removed from the Store
22750          * @param {Store} this
22751          * @param {Roo.data.Record} record The Record that was removed
22752          * @param {Number} index The index at which the record was removed
22753          */
22754         remove : true,
22755         /**
22756          * @event update
22757          * Fires when a Record has been updated
22758          * @param {Store} this
22759          * @param {Roo.data.Record} record The Record that was updated
22760          * @param {String} operation The update operation being performed.  Value may be one of:
22761          * <pre><code>
22762  Roo.data.Record.EDIT
22763  Roo.data.Record.REJECT
22764  Roo.data.Record.COMMIT
22765          * </code></pre>
22766          */
22767         update : true,
22768         /**
22769          * @event clear
22770          * Fires when the data cache has been cleared.
22771          * @param {Store} this
22772          */
22773         clear : true,
22774         /**
22775          * @event beforeload
22776          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22777          * the load action will be canceled.
22778          * @param {Store} this
22779          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22780          */
22781         beforeload : true,
22782         /**
22783          * @event beforeloadadd
22784          * Fires after a new set of Records has been loaded.
22785          * @param {Store} this
22786          * @param {Roo.data.Record[]} records The Records that were loaded
22787          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22788          */
22789         beforeloadadd : true,
22790         /**
22791          * @event load
22792          * Fires after a new set of Records has been loaded, before they are added to the store.
22793          * @param {Store} this
22794          * @param {Roo.data.Record[]} records The Records that were loaded
22795          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22796          * @params {Object} return from reader
22797          */
22798         load : true,
22799         /**
22800          * @event loadexception
22801          * Fires if an exception occurs in the Proxy during loading.
22802          * Called with the signature of the Proxy's "loadexception" event.
22803          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22804          * 
22805          * @param {Proxy} 
22806          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22807          * @param {Object} load options 
22808          * @param {Object} jsonData from your request (normally this contains the Exception)
22809          */
22810         loadexception : true
22811     });
22812     
22813     if(this.proxy){
22814         this.proxy = Roo.factory(this.proxy, Roo.data);
22815         this.proxy.xmodule = this.xmodule || false;
22816         this.relayEvents(this.proxy,  ["loadexception"]);
22817     }
22818     this.sortToggle = {};
22819     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22820
22821     Roo.data.Store.superclass.constructor.call(this);
22822
22823     if(this.inlineData){
22824         this.loadData(this.inlineData);
22825         delete this.inlineData;
22826     }
22827 };
22828
22829 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22830      /**
22831     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22832     * without a remote query - used by combo/forms at present.
22833     */
22834     
22835     /**
22836     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22837     */
22838     /**
22839     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22840     */
22841     /**
22842     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22843     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22844     */
22845     /**
22846     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22847     * on any HTTP request
22848     */
22849     /**
22850     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22851     */
22852     /**
22853     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22854     */
22855     multiSort: false,
22856     /**
22857     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22858     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22859     */
22860     remoteSort : false,
22861
22862     /**
22863     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22864      * loaded or when a record is removed. (defaults to false).
22865     */
22866     pruneModifiedRecords : false,
22867
22868     // private
22869     lastOptions : null,
22870
22871     /**
22872      * Add Records to the Store and fires the add event.
22873      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22874      */
22875     add : function(records){
22876         records = [].concat(records);
22877         for(var i = 0, len = records.length; i < len; i++){
22878             records[i].join(this);
22879         }
22880         var index = this.data.length;
22881         this.data.addAll(records);
22882         this.fireEvent("add", this, records, index);
22883     },
22884
22885     /**
22886      * Remove a Record from the Store and fires the remove event.
22887      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22888      */
22889     remove : function(record){
22890         var index = this.data.indexOf(record);
22891         this.data.removeAt(index);
22892         if(this.pruneModifiedRecords){
22893             this.modified.remove(record);
22894         }
22895         this.fireEvent("remove", this, record, index);
22896     },
22897
22898     /**
22899      * Remove all Records from the Store and fires the clear event.
22900      */
22901     removeAll : function(){
22902         this.data.clear();
22903         if(this.pruneModifiedRecords){
22904             this.modified = [];
22905         }
22906         this.fireEvent("clear", this);
22907     },
22908
22909     /**
22910      * Inserts Records to the Store at the given index and fires the add event.
22911      * @param {Number} index The start index at which to insert the passed Records.
22912      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22913      */
22914     insert : function(index, records){
22915         records = [].concat(records);
22916         for(var i = 0, len = records.length; i < len; i++){
22917             this.data.insert(index, records[i]);
22918             records[i].join(this);
22919         }
22920         this.fireEvent("add", this, records, index);
22921     },
22922
22923     /**
22924      * Get the index within the cache of the passed Record.
22925      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22926      * @return {Number} The index of the passed Record. Returns -1 if not found.
22927      */
22928     indexOf : function(record){
22929         return this.data.indexOf(record);
22930     },
22931
22932     /**
22933      * Get the index within the cache of the Record with the passed id.
22934      * @param {String} id The id of the Record to find.
22935      * @return {Number} The index of the Record. Returns -1 if not found.
22936      */
22937     indexOfId : function(id){
22938         return this.data.indexOfKey(id);
22939     },
22940
22941     /**
22942      * Get the Record with the specified id.
22943      * @param {String} id The id of the Record to find.
22944      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22945      */
22946     getById : function(id){
22947         return this.data.key(id);
22948     },
22949
22950     /**
22951      * Get the Record at the specified index.
22952      * @param {Number} index The index of the Record to find.
22953      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22954      */
22955     getAt : function(index){
22956         return this.data.itemAt(index);
22957     },
22958
22959     /**
22960      * Returns a range of Records between specified indices.
22961      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22962      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22963      * @return {Roo.data.Record[]} An array of Records
22964      */
22965     getRange : function(start, end){
22966         return this.data.getRange(start, end);
22967     },
22968
22969     // private
22970     storeOptions : function(o){
22971         o = Roo.apply({}, o);
22972         delete o.callback;
22973         delete o.scope;
22974         this.lastOptions = o;
22975     },
22976
22977     /**
22978      * Loads the Record cache from the configured Proxy using the configured Reader.
22979      * <p>
22980      * If using remote paging, then the first load call must specify the <em>start</em>
22981      * and <em>limit</em> properties in the options.params property to establish the initial
22982      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22983      * <p>
22984      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22985      * and this call will return before the new data has been loaded. Perform any post-processing
22986      * in a callback function, or in a "load" event handler.</strong>
22987      * <p>
22988      * @param {Object} options An object containing properties which control loading options:<ul>
22989      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22990      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22991      * passed the following arguments:<ul>
22992      * <li>r : Roo.data.Record[]</li>
22993      * <li>options: Options object from the load call</li>
22994      * <li>success: Boolean success indicator</li></ul></li>
22995      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22996      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22997      * </ul>
22998      */
22999     load : function(options){
23000         options = options || {};
23001         if(this.fireEvent("beforeload", this, options) !== false){
23002             this.storeOptions(options);
23003             var p = Roo.apply(options.params || {}, this.baseParams);
23004             // if meta was not loaded from remote source.. try requesting it.
23005             if (!this.reader.metaFromRemote) {
23006                 p._requestMeta = 1;
23007             }
23008             if(this.sortInfo && this.remoteSort){
23009                 var pn = this.paramNames;
23010                 p[pn["sort"]] = this.sortInfo.field;
23011                 p[pn["dir"]] = this.sortInfo.direction;
23012             }
23013             if (this.multiSort) {
23014                 var pn = this.paramNames;
23015                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23016             }
23017             
23018             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23019         }
23020     },
23021
23022     /**
23023      * Reloads the Record cache from the configured Proxy using the configured Reader and
23024      * the options from the last load operation performed.
23025      * @param {Object} options (optional) An object containing properties which may override the options
23026      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23027      * the most recently used options are reused).
23028      */
23029     reload : function(options){
23030         this.load(Roo.applyIf(options||{}, this.lastOptions));
23031     },
23032
23033     // private
23034     // Called as a callback by the Reader during a load operation.
23035     loadRecords : function(o, options, success){
23036         if(!o || success === false){
23037             if(success !== false){
23038                 this.fireEvent("load", this, [], options, o);
23039             }
23040             if(options.callback){
23041                 options.callback.call(options.scope || this, [], options, false);
23042             }
23043             return;
23044         }
23045         // if data returned failure - throw an exception.
23046         if (o.success === false) {
23047             // show a message if no listener is registered.
23048             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23049                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23050             }
23051             // loadmask wil be hooked into this..
23052             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23053             return;
23054         }
23055         var r = o.records, t = o.totalRecords || r.length;
23056         
23057         this.fireEvent("beforeloadadd", this, r, options, o);
23058         
23059         if(!options || options.add !== true){
23060             if(this.pruneModifiedRecords){
23061                 this.modified = [];
23062             }
23063             for(var i = 0, len = r.length; i < len; i++){
23064                 r[i].join(this);
23065             }
23066             if(this.snapshot){
23067                 this.data = this.snapshot;
23068                 delete this.snapshot;
23069             }
23070             this.data.clear();
23071             this.data.addAll(r);
23072             this.totalLength = t;
23073             this.applySort();
23074             this.fireEvent("datachanged", this);
23075         }else{
23076             this.totalLength = Math.max(t, this.data.length+r.length);
23077             this.add(r);
23078         }
23079         
23080         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23081                 
23082             var e = new Roo.data.Record({});
23083
23084             e.set(this.parent.displayField, this.parent.emptyTitle);
23085             e.set(this.parent.valueField, '');
23086
23087             this.insert(0, e);
23088         }
23089             
23090         this.fireEvent("load", this, r, options, o);
23091         if(options.callback){
23092             options.callback.call(options.scope || this, r, options, true);
23093         }
23094     },
23095
23096
23097     /**
23098      * Loads data from a passed data block. A Reader which understands the format of the data
23099      * must have been configured in the constructor.
23100      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23101      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23102      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23103      */
23104     loadData : function(o, append){
23105         var r = this.reader.readRecords(o);
23106         this.loadRecords(r, {add: append}, true);
23107     },
23108
23109     /**
23110      * Gets the number of cached records.
23111      * <p>
23112      * <em>If using paging, this may not be the total size of the dataset. If the data object
23113      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23114      * the data set size</em>
23115      */
23116     getCount : function(){
23117         return this.data.length || 0;
23118     },
23119
23120     /**
23121      * Gets the total number of records in the dataset as returned by the server.
23122      * <p>
23123      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23124      * the dataset size</em>
23125      */
23126     getTotalCount : function(){
23127         return this.totalLength || 0;
23128     },
23129
23130     /**
23131      * Returns the sort state of the Store as an object with two properties:
23132      * <pre><code>
23133  field {String} The name of the field by which the Records are sorted
23134  direction {String} The sort order, "ASC" or "DESC"
23135      * </code></pre>
23136      */
23137     getSortState : function(){
23138         return this.sortInfo;
23139     },
23140
23141     // private
23142     applySort : function(){
23143         if(this.sortInfo && !this.remoteSort){
23144             var s = this.sortInfo, f = s.field;
23145             var st = this.fields.get(f).sortType;
23146             var fn = function(r1, r2){
23147                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23148                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23149             };
23150             this.data.sort(s.direction, fn);
23151             if(this.snapshot && this.snapshot != this.data){
23152                 this.snapshot.sort(s.direction, fn);
23153             }
23154         }
23155     },
23156
23157     /**
23158      * Sets the default sort column and order to be used by the next load operation.
23159      * @param {String} fieldName The name of the field to sort by.
23160      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23161      */
23162     setDefaultSort : function(field, dir){
23163         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23164     },
23165
23166     /**
23167      * Sort the Records.
23168      * If remote sorting is used, the sort is performed on the server, and the cache is
23169      * reloaded. If local sorting is used, the cache is sorted internally.
23170      * @param {String} fieldName The name of the field to sort by.
23171      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23172      */
23173     sort : function(fieldName, dir){
23174         var f = this.fields.get(fieldName);
23175         if(!dir){
23176             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23177             
23178             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23179                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23180             }else{
23181                 dir = f.sortDir;
23182             }
23183         }
23184         this.sortToggle[f.name] = dir;
23185         this.sortInfo = {field: f.name, direction: dir};
23186         if(!this.remoteSort){
23187             this.applySort();
23188             this.fireEvent("datachanged", this);
23189         }else{
23190             this.load(this.lastOptions);
23191         }
23192     },
23193
23194     /**
23195      * Calls the specified function for each of the Records in the cache.
23196      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23197      * Returning <em>false</em> aborts and exits the iteration.
23198      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23199      */
23200     each : function(fn, scope){
23201         this.data.each(fn, scope);
23202     },
23203
23204     /**
23205      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23206      * (e.g., during paging).
23207      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23208      */
23209     getModifiedRecords : function(){
23210         return this.modified;
23211     },
23212
23213     // private
23214     createFilterFn : function(property, value, anyMatch){
23215         if(!value.exec){ // not a regex
23216             value = String(value);
23217             if(value.length == 0){
23218                 return false;
23219             }
23220             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23221         }
23222         return function(r){
23223             return value.test(r.data[property]);
23224         };
23225     },
23226
23227     /**
23228      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23229      * @param {String} property A field on your records
23230      * @param {Number} start The record index to start at (defaults to 0)
23231      * @param {Number} end The last record index to include (defaults to length - 1)
23232      * @return {Number} The sum
23233      */
23234     sum : function(property, start, end){
23235         var rs = this.data.items, v = 0;
23236         start = start || 0;
23237         end = (end || end === 0) ? end : rs.length-1;
23238
23239         for(var i = start; i <= end; i++){
23240             v += (rs[i].data[property] || 0);
23241         }
23242         return v;
23243     },
23244
23245     /**
23246      * Filter the records by a specified property.
23247      * @param {String} field A field on your records
23248      * @param {String/RegExp} value Either a string that the field
23249      * should start with or a RegExp to test against the field
23250      * @param {Boolean} anyMatch True to match any part not just the beginning
23251      */
23252     filter : function(property, value, anyMatch){
23253         var fn = this.createFilterFn(property, value, anyMatch);
23254         return fn ? this.filterBy(fn) : this.clearFilter();
23255     },
23256
23257     /**
23258      * Filter by a function. The specified function will be called with each
23259      * record in this data source. If the function returns true the record is included,
23260      * otherwise it is filtered.
23261      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23262      * @param {Object} scope (optional) The scope of the function (defaults to this)
23263      */
23264     filterBy : function(fn, scope){
23265         this.snapshot = this.snapshot || this.data;
23266         this.data = this.queryBy(fn, scope||this);
23267         this.fireEvent("datachanged", this);
23268     },
23269
23270     /**
23271      * Query the records by a specified property.
23272      * @param {String} field A field on your records
23273      * @param {String/RegExp} value Either a string that the field
23274      * should start with or a RegExp to test against the field
23275      * @param {Boolean} anyMatch True to match any part not just the beginning
23276      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23277      */
23278     query : function(property, value, anyMatch){
23279         var fn = this.createFilterFn(property, value, anyMatch);
23280         return fn ? this.queryBy(fn) : this.data.clone();
23281     },
23282
23283     /**
23284      * Query by a function. The specified function will be called with each
23285      * record in this data source. If the function returns true the record is included
23286      * in the results.
23287      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23288      * @param {Object} scope (optional) The scope of the function (defaults to this)
23289       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23290      **/
23291     queryBy : function(fn, scope){
23292         var data = this.snapshot || this.data;
23293         return data.filterBy(fn, scope||this);
23294     },
23295
23296     /**
23297      * Collects unique values for a particular dataIndex from this store.
23298      * @param {String} dataIndex The property to collect
23299      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23300      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23301      * @return {Array} An array of the unique values
23302      **/
23303     collect : function(dataIndex, allowNull, bypassFilter){
23304         var d = (bypassFilter === true && this.snapshot) ?
23305                 this.snapshot.items : this.data.items;
23306         var v, sv, r = [], l = {};
23307         for(var i = 0, len = d.length; i < len; i++){
23308             v = d[i].data[dataIndex];
23309             sv = String(v);
23310             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23311                 l[sv] = true;
23312                 r[r.length] = v;
23313             }
23314         }
23315         return r;
23316     },
23317
23318     /**
23319      * Revert to a view of the Record cache with no filtering applied.
23320      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23321      */
23322     clearFilter : function(suppressEvent){
23323         if(this.snapshot && this.snapshot != this.data){
23324             this.data = this.snapshot;
23325             delete this.snapshot;
23326             if(suppressEvent !== true){
23327                 this.fireEvent("datachanged", this);
23328             }
23329         }
23330     },
23331
23332     // private
23333     afterEdit : function(record){
23334         if(this.modified.indexOf(record) == -1){
23335             this.modified.push(record);
23336         }
23337         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23338     },
23339     
23340     // private
23341     afterReject : function(record){
23342         this.modified.remove(record);
23343         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23344     },
23345
23346     // private
23347     afterCommit : function(record){
23348         this.modified.remove(record);
23349         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23350     },
23351
23352     /**
23353      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23354      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23355      */
23356     commitChanges : function(){
23357         var m = this.modified.slice(0);
23358         this.modified = [];
23359         for(var i = 0, len = m.length; i < len; i++){
23360             m[i].commit();
23361         }
23362     },
23363
23364     /**
23365      * Cancel outstanding changes on all changed records.
23366      */
23367     rejectChanges : function(){
23368         var m = this.modified.slice(0);
23369         this.modified = [];
23370         for(var i = 0, len = m.length; i < len; i++){
23371             m[i].reject();
23372         }
23373     },
23374
23375     onMetaChange : function(meta, rtype, o){
23376         this.recordType = rtype;
23377         this.fields = rtype.prototype.fields;
23378         delete this.snapshot;
23379         this.sortInfo = meta.sortInfo || this.sortInfo;
23380         this.modified = [];
23381         this.fireEvent('metachange', this, this.reader.meta);
23382     },
23383     
23384     moveIndex : function(data, type)
23385     {
23386         var index = this.indexOf(data);
23387         
23388         var newIndex = index + type;
23389         
23390         this.remove(data);
23391         
23392         this.insert(newIndex, data);
23393         
23394     }
23395 });/*
23396  * Based on:
23397  * Ext JS Library 1.1.1
23398  * Copyright(c) 2006-2007, Ext JS, LLC.
23399  *
23400  * Originally Released Under LGPL - original licence link has changed is not relivant.
23401  *
23402  * Fork - LGPL
23403  * <script type="text/javascript">
23404  */
23405
23406 /**
23407  * @class Roo.data.SimpleStore
23408  * @extends Roo.data.Store
23409  * Small helper class to make creating Stores from Array data easier.
23410  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23411  * @cfg {Array} fields An array of field definition objects, or field name strings.
23412  * @cfg {Array} data The multi-dimensional array of data
23413  * @constructor
23414  * @param {Object} config
23415  */
23416 Roo.data.SimpleStore = function(config){
23417     Roo.data.SimpleStore.superclass.constructor.call(this, {
23418         isLocal : true,
23419         reader: new Roo.data.ArrayReader({
23420                 id: config.id
23421             },
23422             Roo.data.Record.create(config.fields)
23423         ),
23424         proxy : new Roo.data.MemoryProxy(config.data)
23425     });
23426     this.load();
23427 };
23428 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23429  * Based on:
23430  * Ext JS Library 1.1.1
23431  * Copyright(c) 2006-2007, Ext JS, LLC.
23432  *
23433  * Originally Released Under LGPL - original licence link has changed is not relivant.
23434  *
23435  * Fork - LGPL
23436  * <script type="text/javascript">
23437  */
23438
23439 /**
23440 /**
23441  * @extends Roo.data.Store
23442  * @class Roo.data.JsonStore
23443  * Small helper class to make creating Stores for JSON data easier. <br/>
23444 <pre><code>
23445 var store = new Roo.data.JsonStore({
23446     url: 'get-images.php',
23447     root: 'images',
23448     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23449 });
23450 </code></pre>
23451  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23452  * JsonReader and HttpProxy (unless inline data is provided).</b>
23453  * @cfg {Array} fields An array of field definition objects, or field name strings.
23454  * @constructor
23455  * @param {Object} config
23456  */
23457 Roo.data.JsonStore = function(c){
23458     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23459         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23460         reader: new Roo.data.JsonReader(c, c.fields)
23461     }));
23462 };
23463 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23464  * Based on:
23465  * Ext JS Library 1.1.1
23466  * Copyright(c) 2006-2007, Ext JS, LLC.
23467  *
23468  * Originally Released Under LGPL - original licence link has changed is not relivant.
23469  *
23470  * Fork - LGPL
23471  * <script type="text/javascript">
23472  */
23473
23474  
23475 Roo.data.Field = function(config){
23476     if(typeof config == "string"){
23477         config = {name: config};
23478     }
23479     Roo.apply(this, config);
23480     
23481     if(!this.type){
23482         this.type = "auto";
23483     }
23484     
23485     var st = Roo.data.SortTypes;
23486     // named sortTypes are supported, here we look them up
23487     if(typeof this.sortType == "string"){
23488         this.sortType = st[this.sortType];
23489     }
23490     
23491     // set default sortType for strings and dates
23492     if(!this.sortType){
23493         switch(this.type){
23494             case "string":
23495                 this.sortType = st.asUCString;
23496                 break;
23497             case "date":
23498                 this.sortType = st.asDate;
23499                 break;
23500             default:
23501                 this.sortType = st.none;
23502         }
23503     }
23504
23505     // define once
23506     var stripRe = /[\$,%]/g;
23507
23508     // prebuilt conversion function for this field, instead of
23509     // switching every time we're reading a value
23510     if(!this.convert){
23511         var cv, dateFormat = this.dateFormat;
23512         switch(this.type){
23513             case "":
23514             case "auto":
23515             case undefined:
23516                 cv = function(v){ return v; };
23517                 break;
23518             case "string":
23519                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23520                 break;
23521             case "int":
23522                 cv = function(v){
23523                     return v !== undefined && v !== null && v !== '' ?
23524                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23525                     };
23526                 break;
23527             case "float":
23528                 cv = function(v){
23529                     return v !== undefined && v !== null && v !== '' ?
23530                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23531                     };
23532                 break;
23533             case "bool":
23534             case "boolean":
23535                 cv = function(v){ return v === true || v === "true" || v == 1; };
23536                 break;
23537             case "date":
23538                 cv = function(v){
23539                     if(!v){
23540                         return '';
23541                     }
23542                     if(v instanceof Date){
23543                         return v;
23544                     }
23545                     if(dateFormat){
23546                         if(dateFormat == "timestamp"){
23547                             return new Date(v*1000);
23548                         }
23549                         return Date.parseDate(v, dateFormat);
23550                     }
23551                     var parsed = Date.parse(v);
23552                     return parsed ? new Date(parsed) : null;
23553                 };
23554              break;
23555             
23556         }
23557         this.convert = cv;
23558     }
23559 };
23560
23561 Roo.data.Field.prototype = {
23562     dateFormat: null,
23563     defaultValue: "",
23564     mapping: null,
23565     sortType : null,
23566     sortDir : "ASC"
23567 };/*
23568  * Based on:
23569  * Ext JS Library 1.1.1
23570  * Copyright(c) 2006-2007, Ext JS, LLC.
23571  *
23572  * Originally Released Under LGPL - original licence link has changed is not relivant.
23573  *
23574  * Fork - LGPL
23575  * <script type="text/javascript">
23576  */
23577  
23578 // Base class for reading structured data from a data source.  This class is intended to be
23579 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23580
23581 /**
23582  * @class Roo.data.DataReader
23583  * Base class for reading structured data from a data source.  This class is intended to be
23584  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23585  */
23586
23587 Roo.data.DataReader = function(meta, recordType){
23588     
23589     this.meta = meta;
23590     
23591     this.recordType = recordType instanceof Array ? 
23592         Roo.data.Record.create(recordType) : recordType;
23593 };
23594
23595 Roo.data.DataReader.prototype = {
23596      /**
23597      * Create an empty record
23598      * @param {Object} data (optional) - overlay some values
23599      * @return {Roo.data.Record} record created.
23600      */
23601     newRow :  function(d) {
23602         var da =  {};
23603         this.recordType.prototype.fields.each(function(c) {
23604             switch( c.type) {
23605                 case 'int' : da[c.name] = 0; break;
23606                 case 'date' : da[c.name] = new Date(); break;
23607                 case 'float' : da[c.name] = 0.0; break;
23608                 case 'boolean' : da[c.name] = false; break;
23609                 default : da[c.name] = ""; break;
23610             }
23611             
23612         });
23613         return new this.recordType(Roo.apply(da, d));
23614     }
23615     
23616 };/*
23617  * Based on:
23618  * Ext JS Library 1.1.1
23619  * Copyright(c) 2006-2007, Ext JS, LLC.
23620  *
23621  * Originally Released Under LGPL - original licence link has changed is not relivant.
23622  *
23623  * Fork - LGPL
23624  * <script type="text/javascript">
23625  */
23626
23627 /**
23628  * @class Roo.data.DataProxy
23629  * @extends Roo.data.Observable
23630  * This class is an abstract base class for implementations which provide retrieval of
23631  * unformatted data objects.<br>
23632  * <p>
23633  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23634  * (of the appropriate type which knows how to parse the data object) to provide a block of
23635  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23636  * <p>
23637  * Custom implementations must implement the load method as described in
23638  * {@link Roo.data.HttpProxy#load}.
23639  */
23640 Roo.data.DataProxy = function(){
23641     this.addEvents({
23642         /**
23643          * @event beforeload
23644          * Fires before a network request is made to retrieve a data object.
23645          * @param {Object} This DataProxy object.
23646          * @param {Object} params The params parameter to the load function.
23647          */
23648         beforeload : true,
23649         /**
23650          * @event load
23651          * Fires before the load method's callback is called.
23652          * @param {Object} This DataProxy object.
23653          * @param {Object} o The data object.
23654          * @param {Object} arg The callback argument object passed to the load function.
23655          */
23656         load : true,
23657         /**
23658          * @event loadexception
23659          * Fires if an Exception occurs during data retrieval.
23660          * @param {Object} This DataProxy object.
23661          * @param {Object} o The data object.
23662          * @param {Object} arg The callback argument object passed to the load function.
23663          * @param {Object} e The Exception.
23664          */
23665         loadexception : true
23666     });
23667     Roo.data.DataProxy.superclass.constructor.call(this);
23668 };
23669
23670 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23671
23672     /**
23673      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23674      */
23675 /*
23676  * Based on:
23677  * Ext JS Library 1.1.1
23678  * Copyright(c) 2006-2007, Ext JS, LLC.
23679  *
23680  * Originally Released Under LGPL - original licence link has changed is not relivant.
23681  *
23682  * Fork - LGPL
23683  * <script type="text/javascript">
23684  */
23685 /**
23686  * @class Roo.data.MemoryProxy
23687  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23688  * to the Reader when its load method is called.
23689  * @constructor
23690  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23691  */
23692 Roo.data.MemoryProxy = function(data){
23693     if (data.data) {
23694         data = data.data;
23695     }
23696     Roo.data.MemoryProxy.superclass.constructor.call(this);
23697     this.data = data;
23698 };
23699
23700 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23701     
23702     /**
23703      * Load data from the requested source (in this case an in-memory
23704      * data object passed to the constructor), read the data object into
23705      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23706      * process that block using the passed callback.
23707      * @param {Object} params This parameter is not used by the MemoryProxy class.
23708      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23709      * object into a block of Roo.data.Records.
23710      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23711      * The function must be passed <ul>
23712      * <li>The Record block object</li>
23713      * <li>The "arg" argument from the load function</li>
23714      * <li>A boolean success indicator</li>
23715      * </ul>
23716      * @param {Object} scope The scope in which to call the callback
23717      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23718      */
23719     load : function(params, reader, callback, scope, arg){
23720         params = params || {};
23721         var result;
23722         try {
23723             result = reader.readRecords(this.data);
23724         }catch(e){
23725             this.fireEvent("loadexception", this, arg, null, e);
23726             callback.call(scope, null, arg, false);
23727             return;
23728         }
23729         callback.call(scope, result, arg, true);
23730     },
23731     
23732     // private
23733     update : function(params, records){
23734         
23735     }
23736 });/*
23737  * Based on:
23738  * Ext JS Library 1.1.1
23739  * Copyright(c) 2006-2007, Ext JS, LLC.
23740  *
23741  * Originally Released Under LGPL - original licence link has changed is not relivant.
23742  *
23743  * Fork - LGPL
23744  * <script type="text/javascript">
23745  */
23746 /**
23747  * @class Roo.data.HttpProxy
23748  * @extends Roo.data.DataProxy
23749  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23750  * configured to reference a certain URL.<br><br>
23751  * <p>
23752  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23753  * from which the running page was served.<br><br>
23754  * <p>
23755  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23756  * <p>
23757  * Be aware that to enable the browser to parse an XML document, the server must set
23758  * the Content-Type header in the HTTP response to "text/xml".
23759  * @constructor
23760  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23761  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23762  * will be used to make the request.
23763  */
23764 Roo.data.HttpProxy = function(conn){
23765     Roo.data.HttpProxy.superclass.constructor.call(this);
23766     // is conn a conn config or a real conn?
23767     this.conn = conn;
23768     this.useAjax = !conn || !conn.events;
23769   
23770 };
23771
23772 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23773     // thse are take from connection...
23774     
23775     /**
23776      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23777      */
23778     /**
23779      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23780      * extra parameters to each request made by this object. (defaults to undefined)
23781      */
23782     /**
23783      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23784      *  to each request made by this object. (defaults to undefined)
23785      */
23786     /**
23787      * @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)
23788      */
23789     /**
23790      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23791      */
23792      /**
23793      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23794      * @type Boolean
23795      */
23796   
23797
23798     /**
23799      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23800      * @type Boolean
23801      */
23802     /**
23803      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23804      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23805      * a finer-grained basis than the DataProxy events.
23806      */
23807     getConnection : function(){
23808         return this.useAjax ? Roo.Ajax : this.conn;
23809     },
23810
23811     /**
23812      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23813      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23814      * process that block using the passed callback.
23815      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23816      * for the request to the remote server.
23817      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23818      * object into a block of Roo.data.Records.
23819      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23820      * The function must be passed <ul>
23821      * <li>The Record block object</li>
23822      * <li>The "arg" argument from the load function</li>
23823      * <li>A boolean success indicator</li>
23824      * </ul>
23825      * @param {Object} scope The scope in which to call the callback
23826      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23827      */
23828     load : function(params, reader, callback, scope, arg){
23829         if(this.fireEvent("beforeload", this, params) !== false){
23830             var  o = {
23831                 params : params || {},
23832                 request: {
23833                     callback : callback,
23834                     scope : scope,
23835                     arg : arg
23836                 },
23837                 reader: reader,
23838                 callback : this.loadResponse,
23839                 scope: this
23840             };
23841             if(this.useAjax){
23842                 Roo.applyIf(o, this.conn);
23843                 if(this.activeRequest){
23844                     Roo.Ajax.abort(this.activeRequest);
23845                 }
23846                 this.activeRequest = Roo.Ajax.request(o);
23847             }else{
23848                 this.conn.request(o);
23849             }
23850         }else{
23851             callback.call(scope||this, null, arg, false);
23852         }
23853     },
23854
23855     // private
23856     loadResponse : function(o, success, response){
23857         delete this.activeRequest;
23858         if(!success){
23859             this.fireEvent("loadexception", this, o, response);
23860             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23861             return;
23862         }
23863         var result;
23864         try {
23865             result = o.reader.read(response);
23866         }catch(e){
23867             this.fireEvent("loadexception", this, o, response, e);
23868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23869             return;
23870         }
23871         
23872         this.fireEvent("load", this, o, o.request.arg);
23873         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23874     },
23875
23876     // private
23877     update : function(dataSet){
23878
23879     },
23880
23881     // private
23882     updateResponse : function(dataSet){
23883
23884     }
23885 });/*
23886  * Based on:
23887  * Ext JS Library 1.1.1
23888  * Copyright(c) 2006-2007, Ext JS, LLC.
23889  *
23890  * Originally Released Under LGPL - original licence link has changed is not relivant.
23891  *
23892  * Fork - LGPL
23893  * <script type="text/javascript">
23894  */
23895
23896 /**
23897  * @class Roo.data.ScriptTagProxy
23898  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23899  * other than the originating domain of the running page.<br><br>
23900  * <p>
23901  * <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
23902  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23903  * <p>
23904  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23905  * source code that is used as the source inside a &lt;script> tag.<br><br>
23906  * <p>
23907  * In order for the browser to process the returned data, the server must wrap the data object
23908  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23909  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23910  * depending on whether the callback name was passed:
23911  * <p>
23912  * <pre><code>
23913 boolean scriptTag = false;
23914 String cb = request.getParameter("callback");
23915 if (cb != null) {
23916     scriptTag = true;
23917     response.setContentType("text/javascript");
23918 } else {
23919     response.setContentType("application/x-json");
23920 }
23921 Writer out = response.getWriter();
23922 if (scriptTag) {
23923     out.write(cb + "(");
23924 }
23925 out.print(dataBlock.toJsonString());
23926 if (scriptTag) {
23927     out.write(");");
23928 }
23929 </pre></code>
23930  *
23931  * @constructor
23932  * @param {Object} config A configuration object.
23933  */
23934 Roo.data.ScriptTagProxy = function(config){
23935     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23936     Roo.apply(this, config);
23937     this.head = document.getElementsByTagName("head")[0];
23938 };
23939
23940 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23941
23942 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23943     /**
23944      * @cfg {String} url The URL from which to request the data object.
23945      */
23946     /**
23947      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23948      */
23949     timeout : 30000,
23950     /**
23951      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23952      * the server the name of the callback function set up by the load call to process the returned data object.
23953      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23954      * javascript output which calls this named function passing the data object as its only parameter.
23955      */
23956     callbackParam : "callback",
23957     /**
23958      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23959      * name to the request.
23960      */
23961     nocache : true,
23962
23963     /**
23964      * Load data from the configured URL, read the data object into
23965      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23966      * process that block using the passed callback.
23967      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23968      * for the request to the remote server.
23969      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23970      * object into a block of Roo.data.Records.
23971      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23972      * The function must be passed <ul>
23973      * <li>The Record block object</li>
23974      * <li>The "arg" argument from the load function</li>
23975      * <li>A boolean success indicator</li>
23976      * </ul>
23977      * @param {Object} scope The scope in which to call the callback
23978      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23979      */
23980     load : function(params, reader, callback, scope, arg){
23981         if(this.fireEvent("beforeload", this, params) !== false){
23982
23983             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23984
23985             var url = this.url;
23986             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23987             if(this.nocache){
23988                 url += "&_dc=" + (new Date().getTime());
23989             }
23990             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23991             var trans = {
23992                 id : transId,
23993                 cb : "stcCallback"+transId,
23994                 scriptId : "stcScript"+transId,
23995                 params : params,
23996                 arg : arg,
23997                 url : url,
23998                 callback : callback,
23999                 scope : scope,
24000                 reader : reader
24001             };
24002             var conn = this;
24003
24004             window[trans.cb] = function(o){
24005                 conn.handleResponse(o, trans);
24006             };
24007
24008             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24009
24010             if(this.autoAbort !== false){
24011                 this.abort();
24012             }
24013
24014             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24015
24016             var script = document.createElement("script");
24017             script.setAttribute("src", url);
24018             script.setAttribute("type", "text/javascript");
24019             script.setAttribute("id", trans.scriptId);
24020             this.head.appendChild(script);
24021
24022             this.trans = trans;
24023         }else{
24024             callback.call(scope||this, null, arg, false);
24025         }
24026     },
24027
24028     // private
24029     isLoading : function(){
24030         return this.trans ? true : false;
24031     },
24032
24033     /**
24034      * Abort the current server request.
24035      */
24036     abort : function(){
24037         if(this.isLoading()){
24038             this.destroyTrans(this.trans);
24039         }
24040     },
24041
24042     // private
24043     destroyTrans : function(trans, isLoaded){
24044         this.head.removeChild(document.getElementById(trans.scriptId));
24045         clearTimeout(trans.timeoutId);
24046         if(isLoaded){
24047             window[trans.cb] = undefined;
24048             try{
24049                 delete window[trans.cb];
24050             }catch(e){}
24051         }else{
24052             // if hasn't been loaded, wait for load to remove it to prevent script error
24053             window[trans.cb] = function(){
24054                 window[trans.cb] = undefined;
24055                 try{
24056                     delete window[trans.cb];
24057                 }catch(e){}
24058             };
24059         }
24060     },
24061
24062     // private
24063     handleResponse : function(o, trans){
24064         this.trans = false;
24065         this.destroyTrans(trans, true);
24066         var result;
24067         try {
24068             result = trans.reader.readRecords(o);
24069         }catch(e){
24070             this.fireEvent("loadexception", this, o, trans.arg, e);
24071             trans.callback.call(trans.scope||window, null, trans.arg, false);
24072             return;
24073         }
24074         this.fireEvent("load", this, o, trans.arg);
24075         trans.callback.call(trans.scope||window, result, trans.arg, true);
24076     },
24077
24078     // private
24079     handleFailure : function(trans){
24080         this.trans = false;
24081         this.destroyTrans(trans, false);
24082         this.fireEvent("loadexception", this, null, trans.arg);
24083         trans.callback.call(trans.scope||window, null, trans.arg, false);
24084     }
24085 });/*
24086  * Based on:
24087  * Ext JS Library 1.1.1
24088  * Copyright(c) 2006-2007, Ext JS, LLC.
24089  *
24090  * Originally Released Under LGPL - original licence link has changed is not relivant.
24091  *
24092  * Fork - LGPL
24093  * <script type="text/javascript">
24094  */
24095
24096 /**
24097  * @class Roo.data.JsonReader
24098  * @extends Roo.data.DataReader
24099  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24100  * based on mappings in a provided Roo.data.Record constructor.
24101  * 
24102  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24103  * in the reply previously. 
24104  * 
24105  * <p>
24106  * Example code:
24107  * <pre><code>
24108 var RecordDef = Roo.data.Record.create([
24109     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24110     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24111 ]);
24112 var myReader = new Roo.data.JsonReader({
24113     totalProperty: "results",    // The property which contains the total dataset size (optional)
24114     root: "rows",                // The property which contains an Array of row objects
24115     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24116 }, RecordDef);
24117 </code></pre>
24118  * <p>
24119  * This would consume a JSON file like this:
24120  * <pre><code>
24121 { 'results': 2, 'rows': [
24122     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24123     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24124 }
24125 </code></pre>
24126  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24127  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24128  * paged from the remote server.
24129  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24130  * @cfg {String} root name of the property which contains the Array of row objects.
24131  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24132  * @cfg {Array} fields Array of field definition objects
24133  * @constructor
24134  * Create a new JsonReader
24135  * @param {Object} meta Metadata configuration options
24136  * @param {Object} recordType Either an Array of field definition objects,
24137  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24138  */
24139 Roo.data.JsonReader = function(meta, recordType){
24140     
24141     meta = meta || {};
24142     // set some defaults:
24143     Roo.applyIf(meta, {
24144         totalProperty: 'total',
24145         successProperty : 'success',
24146         root : 'data',
24147         id : 'id'
24148     });
24149     
24150     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24151 };
24152 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24153     
24154     /**
24155      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24156      * Used by Store query builder to append _requestMeta to params.
24157      * 
24158      */
24159     metaFromRemote : false,
24160     /**
24161      * This method is only used by a DataProxy which has retrieved data from a remote server.
24162      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24163      * @return {Object} data A data block which is used by an Roo.data.Store object as
24164      * a cache of Roo.data.Records.
24165      */
24166     read : function(response){
24167         var json = response.responseText;
24168        
24169         var o = /* eval:var:o */ eval("("+json+")");
24170         if(!o) {
24171             throw {message: "JsonReader.read: Json object not found"};
24172         }
24173         
24174         if(o.metaData){
24175             
24176             delete this.ef;
24177             this.metaFromRemote = true;
24178             this.meta = o.metaData;
24179             this.recordType = Roo.data.Record.create(o.metaData.fields);
24180             this.onMetaChange(this.meta, this.recordType, o);
24181         }
24182         return this.readRecords(o);
24183     },
24184
24185     // private function a store will implement
24186     onMetaChange : function(meta, recordType, o){
24187
24188     },
24189
24190     /**
24191          * @ignore
24192          */
24193     simpleAccess: function(obj, subsc) {
24194         return obj[subsc];
24195     },
24196
24197         /**
24198          * @ignore
24199          */
24200     getJsonAccessor: function(){
24201         var re = /[\[\.]/;
24202         return function(expr) {
24203             try {
24204                 return(re.test(expr))
24205                     ? new Function("obj", "return obj." + expr)
24206                     : function(obj){
24207                         return obj[expr];
24208                     };
24209             } catch(e){}
24210             return Roo.emptyFn;
24211         };
24212     }(),
24213
24214     /**
24215      * Create a data block containing Roo.data.Records from an XML document.
24216      * @param {Object} o An object which contains an Array of row objects in the property specified
24217      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24218      * which contains the total size of the dataset.
24219      * @return {Object} data A data block which is used by an Roo.data.Store object as
24220      * a cache of Roo.data.Records.
24221      */
24222     readRecords : function(o){
24223         /**
24224          * After any data loads, the raw JSON data is available for further custom processing.
24225          * @type Object
24226          */
24227         this.o = o;
24228         var s = this.meta, Record = this.recordType,
24229             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24230
24231 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24232         if (!this.ef) {
24233             if(s.totalProperty) {
24234                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24235                 }
24236                 if(s.successProperty) {
24237                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24238                 }
24239                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24240                 if (s.id) {
24241                         var g = this.getJsonAccessor(s.id);
24242                         this.getId = function(rec) {
24243                                 var r = g(rec);  
24244                                 return (r === undefined || r === "") ? null : r;
24245                         };
24246                 } else {
24247                         this.getId = function(){return null;};
24248                 }
24249             this.ef = [];
24250             for(var jj = 0; jj < fl; jj++){
24251                 f = fi[jj];
24252                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24253                 this.ef[jj] = this.getJsonAccessor(map);
24254             }
24255         }
24256
24257         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24258         if(s.totalProperty){
24259             var vt = parseInt(this.getTotal(o), 10);
24260             if(!isNaN(vt)){
24261                 totalRecords = vt;
24262             }
24263         }
24264         if(s.successProperty){
24265             var vs = this.getSuccess(o);
24266             if(vs === false || vs === 'false'){
24267                 success = false;
24268             }
24269         }
24270         var records = [];
24271         for(var i = 0; i < c; i++){
24272                 var n = root[i];
24273             var values = {};
24274             var id = this.getId(n);
24275             for(var j = 0; j < fl; j++){
24276                 f = fi[j];
24277             var v = this.ef[j](n);
24278             if (!f.convert) {
24279                 Roo.log('missing convert for ' + f.name);
24280                 Roo.log(f);
24281                 continue;
24282             }
24283             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24284             }
24285             var record = new Record(values, id);
24286             record.json = n;
24287             records[i] = record;
24288         }
24289         return {
24290             raw : o,
24291             success : success,
24292             records : records,
24293             totalRecords : totalRecords
24294         };
24295     }
24296 });/*
24297  * Based on:
24298  * Ext JS Library 1.1.1
24299  * Copyright(c) 2006-2007, Ext JS, LLC.
24300  *
24301  * Originally Released Under LGPL - original licence link has changed is not relivant.
24302  *
24303  * Fork - LGPL
24304  * <script type="text/javascript">
24305  */
24306
24307 /**
24308  * @class Roo.data.XmlReader
24309  * @extends Roo.data.DataReader
24310  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24311  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24312  * <p>
24313  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24314  * header in the HTTP response must be set to "text/xml".</em>
24315  * <p>
24316  * Example code:
24317  * <pre><code>
24318 var RecordDef = Roo.data.Record.create([
24319    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24320    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24321 ]);
24322 var myReader = new Roo.data.XmlReader({
24323    totalRecords: "results", // The element which contains the total dataset size (optional)
24324    record: "row",           // The repeated element which contains row information
24325    id: "id"                 // The element within the row that provides an ID for the record (optional)
24326 }, RecordDef);
24327 </code></pre>
24328  * <p>
24329  * This would consume an XML file like this:
24330  * <pre><code>
24331 &lt;?xml?>
24332 &lt;dataset>
24333  &lt;results>2&lt;/results>
24334  &lt;row>
24335    &lt;id>1&lt;/id>
24336    &lt;name>Bill&lt;/name>
24337    &lt;occupation>Gardener&lt;/occupation>
24338  &lt;/row>
24339  &lt;row>
24340    &lt;id>2&lt;/id>
24341    &lt;name>Ben&lt;/name>
24342    &lt;occupation>Horticulturalist&lt;/occupation>
24343  &lt;/row>
24344 &lt;/dataset>
24345 </code></pre>
24346  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24347  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24348  * paged from the remote server.
24349  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24350  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24351  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24352  * a record identifier value.
24353  * @constructor
24354  * Create a new XmlReader
24355  * @param {Object} meta Metadata configuration options
24356  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24357  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24358  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24359  */
24360 Roo.data.XmlReader = function(meta, recordType){
24361     meta = meta || {};
24362     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24363 };
24364 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24365     /**
24366      * This method is only used by a DataProxy which has retrieved data from a remote server.
24367          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24368          * to contain a method called 'responseXML' that returns an XML document object.
24369      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24370      * a cache of Roo.data.Records.
24371      */
24372     read : function(response){
24373         var doc = response.responseXML;
24374         if(!doc) {
24375             throw {message: "XmlReader.read: XML Document not available"};
24376         }
24377         return this.readRecords(doc);
24378     },
24379
24380     /**
24381      * Create a data block containing Roo.data.Records from an XML document.
24382          * @param {Object} doc A parsed XML document.
24383      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24384      * a cache of Roo.data.Records.
24385      */
24386     readRecords : function(doc){
24387         /**
24388          * After any data loads/reads, the raw XML Document is available for further custom processing.
24389          * @type XMLDocument
24390          */
24391         this.xmlData = doc;
24392         var root = doc.documentElement || doc;
24393         var q = Roo.DomQuery;
24394         var recordType = this.recordType, fields = recordType.prototype.fields;
24395         var sid = this.meta.id;
24396         var totalRecords = 0, success = true;
24397         if(this.meta.totalRecords){
24398             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24399         }
24400         
24401         if(this.meta.success){
24402             var sv = q.selectValue(this.meta.success, root, true);
24403             success = sv !== false && sv !== 'false';
24404         }
24405         var records = [];
24406         var ns = q.select(this.meta.record, root);
24407         for(var i = 0, len = ns.length; i < len; i++) {
24408                 var n = ns[i];
24409                 var values = {};
24410                 var id = sid ? q.selectValue(sid, n) : undefined;
24411                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24412                     var f = fields.items[j];
24413                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24414                     v = f.convert(v);
24415                     values[f.name] = v;
24416                 }
24417                 var record = new recordType(values, id);
24418                 record.node = n;
24419                 records[records.length] = record;
24420             }
24421
24422             return {
24423                 success : success,
24424                 records : records,
24425                 totalRecords : totalRecords || records.length
24426             };
24427     }
24428 });/*
24429  * Based on:
24430  * Ext JS Library 1.1.1
24431  * Copyright(c) 2006-2007, Ext JS, LLC.
24432  *
24433  * Originally Released Under LGPL - original licence link has changed is not relivant.
24434  *
24435  * Fork - LGPL
24436  * <script type="text/javascript">
24437  */
24438
24439 /**
24440  * @class Roo.data.ArrayReader
24441  * @extends Roo.data.DataReader
24442  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24443  * Each element of that Array represents a row of data fields. The
24444  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24445  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24446  * <p>
24447  * Example code:.
24448  * <pre><code>
24449 var RecordDef = Roo.data.Record.create([
24450     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24451     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24452 ]);
24453 var myReader = new Roo.data.ArrayReader({
24454     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24455 }, RecordDef);
24456 </code></pre>
24457  * <p>
24458  * This would consume an Array like this:
24459  * <pre><code>
24460 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24461   </code></pre>
24462  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24463  * @constructor
24464  * Create a new JsonReader
24465  * @param {Object} meta Metadata configuration options.
24466  * @param {Object} recordType Either an Array of field definition objects
24467  * as specified to {@link Roo.data.Record#create},
24468  * or an {@link Roo.data.Record} object
24469  * created using {@link Roo.data.Record#create}.
24470  */
24471 Roo.data.ArrayReader = function(meta, recordType){
24472     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24473 };
24474
24475 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24476     /**
24477      * Create a data block containing Roo.data.Records from an XML document.
24478      * @param {Object} o An Array of row objects which represents the dataset.
24479      * @return {Object} data A data block which is used by an Roo.data.Store object as
24480      * a cache of Roo.data.Records.
24481      */
24482     readRecords : function(o){
24483         var sid = this.meta ? this.meta.id : null;
24484         var recordType = this.recordType, fields = recordType.prototype.fields;
24485         var records = [];
24486         var root = o;
24487             for(var i = 0; i < root.length; i++){
24488                     var n = root[i];
24489                 var values = {};
24490                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24491                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24492                 var f = fields.items[j];
24493                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24494                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24495                 v = f.convert(v);
24496                 values[f.name] = v;
24497             }
24498                 var record = new recordType(values, id);
24499                 record.json = n;
24500                 records[records.length] = record;
24501             }
24502             return {
24503                 records : records,
24504                 totalRecords : records.length
24505             };
24506     }
24507 });/*
24508  * Based on:
24509  * Ext JS Library 1.1.1
24510  * Copyright(c) 2006-2007, Ext JS, LLC.
24511  *
24512  * Originally Released Under LGPL - original licence link has changed is not relivant.
24513  *
24514  * Fork - LGPL
24515  * <script type="text/javascript">
24516  */
24517
24518
24519 /**
24520  * @class Roo.data.Tree
24521  * @extends Roo.util.Observable
24522  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24523  * in the tree have most standard DOM functionality.
24524  * @constructor
24525  * @param {Node} root (optional) The root node
24526  */
24527 Roo.data.Tree = function(root){
24528    this.nodeHash = {};
24529    /**
24530     * The root node for this tree
24531     * @type Node
24532     */
24533    this.root = null;
24534    if(root){
24535        this.setRootNode(root);
24536    }
24537    this.addEvents({
24538        /**
24539         * @event append
24540         * Fires when a new child node is appended to a node in this tree.
24541         * @param {Tree} tree The owner tree
24542         * @param {Node} parent The parent node
24543         * @param {Node} node The newly appended node
24544         * @param {Number} index The index of the newly appended node
24545         */
24546        "append" : true,
24547        /**
24548         * @event remove
24549         * Fires when a child node is removed from a node in this tree.
24550         * @param {Tree} tree The owner tree
24551         * @param {Node} parent The parent node
24552         * @param {Node} node The child node removed
24553         */
24554        "remove" : true,
24555        /**
24556         * @event move
24557         * Fires when a node is moved to a new location in the tree
24558         * @param {Tree} tree The owner tree
24559         * @param {Node} node The node moved
24560         * @param {Node} oldParent The old parent of this node
24561         * @param {Node} newParent The new parent of this node
24562         * @param {Number} index The index it was moved to
24563         */
24564        "move" : true,
24565        /**
24566         * @event insert
24567         * Fires when a new child node is inserted in a node in this tree.
24568         * @param {Tree} tree The owner tree
24569         * @param {Node} parent The parent node
24570         * @param {Node} node The child node inserted
24571         * @param {Node} refNode The child node the node was inserted before
24572         */
24573        "insert" : true,
24574        /**
24575         * @event beforeappend
24576         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24577         * @param {Tree} tree The owner tree
24578         * @param {Node} parent The parent node
24579         * @param {Node} node The child node to be appended
24580         */
24581        "beforeappend" : true,
24582        /**
24583         * @event beforeremove
24584         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24585         * @param {Tree} tree The owner tree
24586         * @param {Node} parent The parent node
24587         * @param {Node} node The child node to be removed
24588         */
24589        "beforeremove" : true,
24590        /**
24591         * @event beforemove
24592         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24593         * @param {Tree} tree The owner tree
24594         * @param {Node} node The node being moved
24595         * @param {Node} oldParent The parent of the node
24596         * @param {Node} newParent The new parent the node is moving to
24597         * @param {Number} index The index it is being moved to
24598         */
24599        "beforemove" : true,
24600        /**
24601         * @event beforeinsert
24602         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24603         * @param {Tree} tree The owner tree
24604         * @param {Node} parent The parent node
24605         * @param {Node} node The child node to be inserted
24606         * @param {Node} refNode The child node the node is being inserted before
24607         */
24608        "beforeinsert" : true
24609    });
24610
24611     Roo.data.Tree.superclass.constructor.call(this);
24612 };
24613
24614 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24615     pathSeparator: "/",
24616
24617     proxyNodeEvent : function(){
24618         return this.fireEvent.apply(this, arguments);
24619     },
24620
24621     /**
24622      * Returns the root node for this tree.
24623      * @return {Node}
24624      */
24625     getRootNode : function(){
24626         return this.root;
24627     },
24628
24629     /**
24630      * Sets the root node for this tree.
24631      * @param {Node} node
24632      * @return {Node}
24633      */
24634     setRootNode : function(node){
24635         this.root = node;
24636         node.ownerTree = this;
24637         node.isRoot = true;
24638         this.registerNode(node);
24639         return node;
24640     },
24641
24642     /**
24643      * Gets a node in this tree by its id.
24644      * @param {String} id
24645      * @return {Node}
24646      */
24647     getNodeById : function(id){
24648         return this.nodeHash[id];
24649     },
24650
24651     registerNode : function(node){
24652         this.nodeHash[node.id] = node;
24653     },
24654
24655     unregisterNode : function(node){
24656         delete this.nodeHash[node.id];
24657     },
24658
24659     toString : function(){
24660         return "[Tree"+(this.id?" "+this.id:"")+"]";
24661     }
24662 });
24663
24664 /**
24665  * @class Roo.data.Node
24666  * @extends Roo.util.Observable
24667  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24668  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24669  * @constructor
24670  * @param {Object} attributes The attributes/config for the node
24671  */
24672 Roo.data.Node = function(attributes){
24673     /**
24674      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24675      * @type {Object}
24676      */
24677     this.attributes = attributes || {};
24678     this.leaf = this.attributes.leaf;
24679     /**
24680      * The node id. @type String
24681      */
24682     this.id = this.attributes.id;
24683     if(!this.id){
24684         this.id = Roo.id(null, "ynode-");
24685         this.attributes.id = this.id;
24686     }
24687      
24688     
24689     /**
24690      * All child nodes of this node. @type Array
24691      */
24692     this.childNodes = [];
24693     if(!this.childNodes.indexOf){ // indexOf is a must
24694         this.childNodes.indexOf = function(o){
24695             for(var i = 0, len = this.length; i < len; i++){
24696                 if(this[i] == o) {
24697                     return i;
24698                 }
24699             }
24700             return -1;
24701         };
24702     }
24703     /**
24704      * The parent node for this node. @type Node
24705      */
24706     this.parentNode = null;
24707     /**
24708      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24709      */
24710     this.firstChild = null;
24711     /**
24712      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24713      */
24714     this.lastChild = null;
24715     /**
24716      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24717      */
24718     this.previousSibling = null;
24719     /**
24720      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24721      */
24722     this.nextSibling = null;
24723
24724     this.addEvents({
24725        /**
24726         * @event append
24727         * Fires when a new child node is appended
24728         * @param {Tree} tree The owner tree
24729         * @param {Node} this This node
24730         * @param {Node} node The newly appended node
24731         * @param {Number} index The index of the newly appended node
24732         */
24733        "append" : true,
24734        /**
24735         * @event remove
24736         * Fires when a child node is removed
24737         * @param {Tree} tree The owner tree
24738         * @param {Node} this This node
24739         * @param {Node} node The removed node
24740         */
24741        "remove" : true,
24742        /**
24743         * @event move
24744         * Fires when this node is moved to a new location in the tree
24745         * @param {Tree} tree The owner tree
24746         * @param {Node} this This node
24747         * @param {Node} oldParent The old parent of this node
24748         * @param {Node} newParent The new parent of this node
24749         * @param {Number} index The index it was moved to
24750         */
24751        "move" : true,
24752        /**
24753         * @event insert
24754         * Fires when a new child node is inserted.
24755         * @param {Tree} tree The owner tree
24756         * @param {Node} this This node
24757         * @param {Node} node The child node inserted
24758         * @param {Node} refNode The child node the node was inserted before
24759         */
24760        "insert" : true,
24761        /**
24762         * @event beforeappend
24763         * Fires before a new child is appended, return false to cancel the append.
24764         * @param {Tree} tree The owner tree
24765         * @param {Node} this This node
24766         * @param {Node} node The child node to be appended
24767         */
24768        "beforeappend" : true,
24769        /**
24770         * @event beforeremove
24771         * Fires before a child is removed, return false to cancel the remove.
24772         * @param {Tree} tree The owner tree
24773         * @param {Node} this This node
24774         * @param {Node} node The child node to be removed
24775         */
24776        "beforeremove" : true,
24777        /**
24778         * @event beforemove
24779         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24780         * @param {Tree} tree The owner tree
24781         * @param {Node} this This node
24782         * @param {Node} oldParent The parent of this node
24783         * @param {Node} newParent The new parent this node is moving to
24784         * @param {Number} index The index it is being moved to
24785         */
24786        "beforemove" : true,
24787        /**
24788         * @event beforeinsert
24789         * Fires before a new child is inserted, return false to cancel the insert.
24790         * @param {Tree} tree The owner tree
24791         * @param {Node} this This node
24792         * @param {Node} node The child node to be inserted
24793         * @param {Node} refNode The child node the node is being inserted before
24794         */
24795        "beforeinsert" : true
24796    });
24797     this.listeners = this.attributes.listeners;
24798     Roo.data.Node.superclass.constructor.call(this);
24799 };
24800
24801 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24802     fireEvent : function(evtName){
24803         // first do standard event for this node
24804         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24805             return false;
24806         }
24807         // then bubble it up to the tree if the event wasn't cancelled
24808         var ot = this.getOwnerTree();
24809         if(ot){
24810             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24811                 return false;
24812             }
24813         }
24814         return true;
24815     },
24816
24817     /**
24818      * Returns true if this node is a leaf
24819      * @return {Boolean}
24820      */
24821     isLeaf : function(){
24822         return this.leaf === true;
24823     },
24824
24825     // private
24826     setFirstChild : function(node){
24827         this.firstChild = node;
24828     },
24829
24830     //private
24831     setLastChild : function(node){
24832         this.lastChild = node;
24833     },
24834
24835
24836     /**
24837      * Returns true if this node is the last child of its parent
24838      * @return {Boolean}
24839      */
24840     isLast : function(){
24841        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24842     },
24843
24844     /**
24845      * Returns true if this node is the first child of its parent
24846      * @return {Boolean}
24847      */
24848     isFirst : function(){
24849        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24850     },
24851
24852     hasChildNodes : function(){
24853         return !this.isLeaf() && this.childNodes.length > 0;
24854     },
24855
24856     /**
24857      * Insert node(s) as the last child node of this node.
24858      * @param {Node/Array} node The node or Array of nodes to append
24859      * @return {Node} The appended node if single append, or null if an array was passed
24860      */
24861     appendChild : function(node){
24862         var multi = false;
24863         if(node instanceof Array){
24864             multi = node;
24865         }else if(arguments.length > 1){
24866             multi = arguments;
24867         }
24868         // if passed an array or multiple args do them one by one
24869         if(multi){
24870             for(var i = 0, len = multi.length; i < len; i++) {
24871                 this.appendChild(multi[i]);
24872             }
24873         }else{
24874             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24875                 return false;
24876             }
24877             var index = this.childNodes.length;
24878             var oldParent = node.parentNode;
24879             // it's a move, make sure we move it cleanly
24880             if(oldParent){
24881                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24882                     return false;
24883                 }
24884                 oldParent.removeChild(node);
24885             }
24886             index = this.childNodes.length;
24887             if(index == 0){
24888                 this.setFirstChild(node);
24889             }
24890             this.childNodes.push(node);
24891             node.parentNode = this;
24892             var ps = this.childNodes[index-1];
24893             if(ps){
24894                 node.previousSibling = ps;
24895                 ps.nextSibling = node;
24896             }else{
24897                 node.previousSibling = null;
24898             }
24899             node.nextSibling = null;
24900             this.setLastChild(node);
24901             node.setOwnerTree(this.getOwnerTree());
24902             this.fireEvent("append", this.ownerTree, this, node, index);
24903             if(oldParent){
24904                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24905             }
24906             return node;
24907         }
24908     },
24909
24910     /**
24911      * Removes a child node from this node.
24912      * @param {Node} node The node to remove
24913      * @return {Node} The removed node
24914      */
24915     removeChild : function(node){
24916         var index = this.childNodes.indexOf(node);
24917         if(index == -1){
24918             return false;
24919         }
24920         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24921             return false;
24922         }
24923
24924         // remove it from childNodes collection
24925         this.childNodes.splice(index, 1);
24926
24927         // update siblings
24928         if(node.previousSibling){
24929             node.previousSibling.nextSibling = node.nextSibling;
24930         }
24931         if(node.nextSibling){
24932             node.nextSibling.previousSibling = node.previousSibling;
24933         }
24934
24935         // update child refs
24936         if(this.firstChild == node){
24937             this.setFirstChild(node.nextSibling);
24938         }
24939         if(this.lastChild == node){
24940             this.setLastChild(node.previousSibling);
24941         }
24942
24943         node.setOwnerTree(null);
24944         // clear any references from the node
24945         node.parentNode = null;
24946         node.previousSibling = null;
24947         node.nextSibling = null;
24948         this.fireEvent("remove", this.ownerTree, this, node);
24949         return node;
24950     },
24951
24952     /**
24953      * Inserts the first node before the second node in this nodes childNodes collection.
24954      * @param {Node} node The node to insert
24955      * @param {Node} refNode The node to insert before (if null the node is appended)
24956      * @return {Node} The inserted node
24957      */
24958     insertBefore : function(node, refNode){
24959         if(!refNode){ // like standard Dom, refNode can be null for append
24960             return this.appendChild(node);
24961         }
24962         // nothing to do
24963         if(node == refNode){
24964             return false;
24965         }
24966
24967         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24968             return false;
24969         }
24970         var index = this.childNodes.indexOf(refNode);
24971         var oldParent = node.parentNode;
24972         var refIndex = index;
24973
24974         // when moving internally, indexes will change after remove
24975         if(oldParent == this && this.childNodes.indexOf(node) < index){
24976             refIndex--;
24977         }
24978
24979         // it's a move, make sure we move it cleanly
24980         if(oldParent){
24981             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24982                 return false;
24983             }
24984             oldParent.removeChild(node);
24985         }
24986         if(refIndex == 0){
24987             this.setFirstChild(node);
24988         }
24989         this.childNodes.splice(refIndex, 0, node);
24990         node.parentNode = this;
24991         var ps = this.childNodes[refIndex-1];
24992         if(ps){
24993             node.previousSibling = ps;
24994             ps.nextSibling = node;
24995         }else{
24996             node.previousSibling = null;
24997         }
24998         node.nextSibling = refNode;
24999         refNode.previousSibling = node;
25000         node.setOwnerTree(this.getOwnerTree());
25001         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25002         if(oldParent){
25003             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25004         }
25005         return node;
25006     },
25007
25008     /**
25009      * Returns the child node at the specified index.
25010      * @param {Number} index
25011      * @return {Node}
25012      */
25013     item : function(index){
25014         return this.childNodes[index];
25015     },
25016
25017     /**
25018      * Replaces one child node in this node with another.
25019      * @param {Node} newChild The replacement node
25020      * @param {Node} oldChild The node to replace
25021      * @return {Node} The replaced node
25022      */
25023     replaceChild : function(newChild, oldChild){
25024         this.insertBefore(newChild, oldChild);
25025         this.removeChild(oldChild);
25026         return oldChild;
25027     },
25028
25029     /**
25030      * Returns the index of a child node
25031      * @param {Node} node
25032      * @return {Number} The index of the node or -1 if it was not found
25033      */
25034     indexOf : function(child){
25035         return this.childNodes.indexOf(child);
25036     },
25037
25038     /**
25039      * Returns the tree this node is in.
25040      * @return {Tree}
25041      */
25042     getOwnerTree : function(){
25043         // if it doesn't have one, look for one
25044         if(!this.ownerTree){
25045             var p = this;
25046             while(p){
25047                 if(p.ownerTree){
25048                     this.ownerTree = p.ownerTree;
25049                     break;
25050                 }
25051                 p = p.parentNode;
25052             }
25053         }
25054         return this.ownerTree;
25055     },
25056
25057     /**
25058      * Returns depth of this node (the root node has a depth of 0)
25059      * @return {Number}
25060      */
25061     getDepth : function(){
25062         var depth = 0;
25063         var p = this;
25064         while(p.parentNode){
25065             ++depth;
25066             p = p.parentNode;
25067         }
25068         return depth;
25069     },
25070
25071     // private
25072     setOwnerTree : function(tree){
25073         // if it's move, we need to update everyone
25074         if(tree != this.ownerTree){
25075             if(this.ownerTree){
25076                 this.ownerTree.unregisterNode(this);
25077             }
25078             this.ownerTree = tree;
25079             var cs = this.childNodes;
25080             for(var i = 0, len = cs.length; i < len; i++) {
25081                 cs[i].setOwnerTree(tree);
25082             }
25083             if(tree){
25084                 tree.registerNode(this);
25085             }
25086         }
25087     },
25088
25089     /**
25090      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25091      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25092      * @return {String} The path
25093      */
25094     getPath : function(attr){
25095         attr = attr || "id";
25096         var p = this.parentNode;
25097         var b = [this.attributes[attr]];
25098         while(p){
25099             b.unshift(p.attributes[attr]);
25100             p = p.parentNode;
25101         }
25102         var sep = this.getOwnerTree().pathSeparator;
25103         return sep + b.join(sep);
25104     },
25105
25106     /**
25107      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25108      * function call will be the scope provided or the current node. The arguments to the function
25109      * will be the args provided or the current node. If the function returns false at any point,
25110      * the bubble is stopped.
25111      * @param {Function} fn The function to call
25112      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25113      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25114      */
25115     bubble : function(fn, scope, args){
25116         var p = this;
25117         while(p){
25118             if(fn.call(scope || p, args || p) === false){
25119                 break;
25120             }
25121             p = p.parentNode;
25122         }
25123     },
25124
25125     /**
25126      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25127      * function call will be the scope provided or the current node. The arguments to the function
25128      * will be the args provided or the current node. If the function returns false at any point,
25129      * the cascade is stopped on that branch.
25130      * @param {Function} fn The function to call
25131      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25132      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25133      */
25134     cascade : function(fn, scope, args){
25135         if(fn.call(scope || this, args || this) !== false){
25136             var cs = this.childNodes;
25137             for(var i = 0, len = cs.length; i < len; i++) {
25138                 cs[i].cascade(fn, scope, args);
25139             }
25140         }
25141     },
25142
25143     /**
25144      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25145      * function call will be the scope provided or the current node. The arguments to the function
25146      * will be the args provided or the current node. If the function returns false at any point,
25147      * the iteration stops.
25148      * @param {Function} fn The function to call
25149      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25150      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25151      */
25152     eachChild : function(fn, scope, args){
25153         var cs = this.childNodes;
25154         for(var i = 0, len = cs.length; i < len; i++) {
25155                 if(fn.call(scope || this, args || cs[i]) === false){
25156                     break;
25157                 }
25158         }
25159     },
25160
25161     /**
25162      * Finds the first child that has the attribute with the specified value.
25163      * @param {String} attribute The attribute name
25164      * @param {Mixed} value The value to search for
25165      * @return {Node} The found child or null if none was found
25166      */
25167     findChild : function(attribute, value){
25168         var cs = this.childNodes;
25169         for(var i = 0, len = cs.length; i < len; i++) {
25170                 if(cs[i].attributes[attribute] == value){
25171                     return cs[i];
25172                 }
25173         }
25174         return null;
25175     },
25176
25177     /**
25178      * Finds the first child by a custom function. The child matches if the function passed
25179      * returns true.
25180      * @param {Function} fn
25181      * @param {Object} scope (optional)
25182      * @return {Node} The found child or null if none was found
25183      */
25184     findChildBy : function(fn, scope){
25185         var cs = this.childNodes;
25186         for(var i = 0, len = cs.length; i < len; i++) {
25187                 if(fn.call(scope||cs[i], cs[i]) === true){
25188                     return cs[i];
25189                 }
25190         }
25191         return null;
25192     },
25193
25194     /**
25195      * Sorts this nodes children using the supplied sort function
25196      * @param {Function} fn
25197      * @param {Object} scope (optional)
25198      */
25199     sort : function(fn, scope){
25200         var cs = this.childNodes;
25201         var len = cs.length;
25202         if(len > 0){
25203             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25204             cs.sort(sortFn);
25205             for(var i = 0; i < len; i++){
25206                 var n = cs[i];
25207                 n.previousSibling = cs[i-1];
25208                 n.nextSibling = cs[i+1];
25209                 if(i == 0){
25210                     this.setFirstChild(n);
25211                 }
25212                 if(i == len-1){
25213                     this.setLastChild(n);
25214                 }
25215             }
25216         }
25217     },
25218
25219     /**
25220      * Returns true if this node is an ancestor (at any point) of the passed node.
25221      * @param {Node} node
25222      * @return {Boolean}
25223      */
25224     contains : function(node){
25225         return node.isAncestor(this);
25226     },
25227
25228     /**
25229      * Returns true if the passed node is an ancestor (at any point) of this node.
25230      * @param {Node} node
25231      * @return {Boolean}
25232      */
25233     isAncestor : function(node){
25234         var p = this.parentNode;
25235         while(p){
25236             if(p == node){
25237                 return true;
25238             }
25239             p = p.parentNode;
25240         }
25241         return false;
25242     },
25243
25244     toString : function(){
25245         return "[Node"+(this.id?" "+this.id:"")+"]";
25246     }
25247 });/*
25248  * Based on:
25249  * Ext JS Library 1.1.1
25250  * Copyright(c) 2006-2007, Ext JS, LLC.
25251  *
25252  * Originally Released Under LGPL - original licence link has changed is not relivant.
25253  *
25254  * Fork - LGPL
25255  * <script type="text/javascript">
25256  */
25257  (function(){ 
25258 /**
25259  * @class Roo.Layer
25260  * @extends Roo.Element
25261  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25262  * automatic maintaining of shadow/shim positions.
25263  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25264  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25265  * you can pass a string with a CSS class name. False turns off the shadow.
25266  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25267  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25268  * @cfg {String} cls CSS class to add to the element
25269  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25270  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25271  * @constructor
25272  * @param {Object} config An object with config options.
25273  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25274  */
25275
25276 Roo.Layer = function(config, existingEl){
25277     config = config || {};
25278     var dh = Roo.DomHelper;
25279     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25280     if(existingEl){
25281         this.dom = Roo.getDom(existingEl);
25282     }
25283     if(!this.dom){
25284         var o = config.dh || {tag: "div", cls: "x-layer"};
25285         this.dom = dh.append(pel, o);
25286     }
25287     if(config.cls){
25288         this.addClass(config.cls);
25289     }
25290     this.constrain = config.constrain !== false;
25291     this.visibilityMode = Roo.Element.VISIBILITY;
25292     if(config.id){
25293         this.id = this.dom.id = config.id;
25294     }else{
25295         this.id = Roo.id(this.dom);
25296     }
25297     this.zindex = config.zindex || this.getZIndex();
25298     this.position("absolute", this.zindex);
25299     if(config.shadow){
25300         this.shadowOffset = config.shadowOffset || 4;
25301         this.shadow = new Roo.Shadow({
25302             offset : this.shadowOffset,
25303             mode : config.shadow
25304         });
25305     }else{
25306         this.shadowOffset = 0;
25307     }
25308     this.useShim = config.shim !== false && Roo.useShims;
25309     this.useDisplay = config.useDisplay;
25310     this.hide();
25311 };
25312
25313 var supr = Roo.Element.prototype;
25314
25315 // shims are shared among layer to keep from having 100 iframes
25316 var shims = [];
25317
25318 Roo.extend(Roo.Layer, Roo.Element, {
25319
25320     getZIndex : function(){
25321         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25322     },
25323
25324     getShim : function(){
25325         if(!this.useShim){
25326             return null;
25327         }
25328         if(this.shim){
25329             return this.shim;
25330         }
25331         var shim = shims.shift();
25332         if(!shim){
25333             shim = this.createShim();
25334             shim.enableDisplayMode('block');
25335             shim.dom.style.display = 'none';
25336             shim.dom.style.visibility = 'visible';
25337         }
25338         var pn = this.dom.parentNode;
25339         if(shim.dom.parentNode != pn){
25340             pn.insertBefore(shim.dom, this.dom);
25341         }
25342         shim.setStyle('z-index', this.getZIndex()-2);
25343         this.shim = shim;
25344         return shim;
25345     },
25346
25347     hideShim : function(){
25348         if(this.shim){
25349             this.shim.setDisplayed(false);
25350             shims.push(this.shim);
25351             delete this.shim;
25352         }
25353     },
25354
25355     disableShadow : function(){
25356         if(this.shadow){
25357             this.shadowDisabled = true;
25358             this.shadow.hide();
25359             this.lastShadowOffset = this.shadowOffset;
25360             this.shadowOffset = 0;
25361         }
25362     },
25363
25364     enableShadow : function(show){
25365         if(this.shadow){
25366             this.shadowDisabled = false;
25367             this.shadowOffset = this.lastShadowOffset;
25368             delete this.lastShadowOffset;
25369             if(show){
25370                 this.sync(true);
25371             }
25372         }
25373     },
25374
25375     // private
25376     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25377     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25378     sync : function(doShow){
25379         var sw = this.shadow;
25380         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25381             var sh = this.getShim();
25382
25383             var w = this.getWidth(),
25384                 h = this.getHeight();
25385
25386             var l = this.getLeft(true),
25387                 t = this.getTop(true);
25388
25389             if(sw && !this.shadowDisabled){
25390                 if(doShow && !sw.isVisible()){
25391                     sw.show(this);
25392                 }else{
25393                     sw.realign(l, t, w, h);
25394                 }
25395                 if(sh){
25396                     if(doShow){
25397                        sh.show();
25398                     }
25399                     // fit the shim behind the shadow, so it is shimmed too
25400                     var a = sw.adjusts, s = sh.dom.style;
25401                     s.left = (Math.min(l, l+a.l))+"px";
25402                     s.top = (Math.min(t, t+a.t))+"px";
25403                     s.width = (w+a.w)+"px";
25404                     s.height = (h+a.h)+"px";
25405                 }
25406             }else if(sh){
25407                 if(doShow){
25408                    sh.show();
25409                 }
25410                 sh.setSize(w, h);
25411                 sh.setLeftTop(l, t);
25412             }
25413             
25414         }
25415     },
25416
25417     // private
25418     destroy : function(){
25419         this.hideShim();
25420         if(this.shadow){
25421             this.shadow.hide();
25422         }
25423         this.removeAllListeners();
25424         var pn = this.dom.parentNode;
25425         if(pn){
25426             pn.removeChild(this.dom);
25427         }
25428         Roo.Element.uncache(this.id);
25429     },
25430
25431     remove : function(){
25432         this.destroy();
25433     },
25434
25435     // private
25436     beginUpdate : function(){
25437         this.updating = true;
25438     },
25439
25440     // private
25441     endUpdate : function(){
25442         this.updating = false;
25443         this.sync(true);
25444     },
25445
25446     // private
25447     hideUnders : function(negOffset){
25448         if(this.shadow){
25449             this.shadow.hide();
25450         }
25451         this.hideShim();
25452     },
25453
25454     // private
25455     constrainXY : function(){
25456         if(this.constrain){
25457             var vw = Roo.lib.Dom.getViewWidth(),
25458                 vh = Roo.lib.Dom.getViewHeight();
25459             var s = Roo.get(document).getScroll();
25460
25461             var xy = this.getXY();
25462             var x = xy[0], y = xy[1];   
25463             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25464             // only move it if it needs it
25465             var moved = false;
25466             // first validate right/bottom
25467             if((x + w) > vw+s.left){
25468                 x = vw - w - this.shadowOffset;
25469                 moved = true;
25470             }
25471             if((y + h) > vh+s.top){
25472                 y = vh - h - this.shadowOffset;
25473                 moved = true;
25474             }
25475             // then make sure top/left isn't negative
25476             if(x < s.left){
25477                 x = s.left;
25478                 moved = true;
25479             }
25480             if(y < s.top){
25481                 y = s.top;
25482                 moved = true;
25483             }
25484             if(moved){
25485                 if(this.avoidY){
25486                     var ay = this.avoidY;
25487                     if(y <= ay && (y+h) >= ay){
25488                         y = ay-h-5;   
25489                     }
25490                 }
25491                 xy = [x, y];
25492                 this.storeXY(xy);
25493                 supr.setXY.call(this, xy);
25494                 this.sync();
25495             }
25496         }
25497     },
25498
25499     isVisible : function(){
25500         return this.visible;    
25501     },
25502
25503     // private
25504     showAction : function(){
25505         this.visible = true; // track visibility to prevent getStyle calls
25506         if(this.useDisplay === true){
25507             this.setDisplayed("");
25508         }else if(this.lastXY){
25509             supr.setXY.call(this, this.lastXY);
25510         }else if(this.lastLT){
25511             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25512         }
25513     },
25514
25515     // private
25516     hideAction : function(){
25517         this.visible = false;
25518         if(this.useDisplay === true){
25519             this.setDisplayed(false);
25520         }else{
25521             this.setLeftTop(-10000,-10000);
25522         }
25523     },
25524
25525     // overridden Element method
25526     setVisible : function(v, a, d, c, e){
25527         if(v){
25528             this.showAction();
25529         }
25530         if(a && v){
25531             var cb = function(){
25532                 this.sync(true);
25533                 if(c){
25534                     c();
25535                 }
25536             }.createDelegate(this);
25537             supr.setVisible.call(this, true, true, d, cb, e);
25538         }else{
25539             if(!v){
25540                 this.hideUnders(true);
25541             }
25542             var cb = c;
25543             if(a){
25544                 cb = function(){
25545                     this.hideAction();
25546                     if(c){
25547                         c();
25548                     }
25549                 }.createDelegate(this);
25550             }
25551             supr.setVisible.call(this, v, a, d, cb, e);
25552             if(v){
25553                 this.sync(true);
25554             }else if(!a){
25555                 this.hideAction();
25556             }
25557         }
25558     },
25559
25560     storeXY : function(xy){
25561         delete this.lastLT;
25562         this.lastXY = xy;
25563     },
25564
25565     storeLeftTop : function(left, top){
25566         delete this.lastXY;
25567         this.lastLT = [left, top];
25568     },
25569
25570     // private
25571     beforeFx : function(){
25572         this.beforeAction();
25573         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25574     },
25575
25576     // private
25577     afterFx : function(){
25578         Roo.Layer.superclass.afterFx.apply(this, arguments);
25579         this.sync(this.isVisible());
25580     },
25581
25582     // private
25583     beforeAction : function(){
25584         if(!this.updating && this.shadow){
25585             this.shadow.hide();
25586         }
25587     },
25588
25589     // overridden Element method
25590     setLeft : function(left){
25591         this.storeLeftTop(left, this.getTop(true));
25592         supr.setLeft.apply(this, arguments);
25593         this.sync();
25594     },
25595
25596     setTop : function(top){
25597         this.storeLeftTop(this.getLeft(true), top);
25598         supr.setTop.apply(this, arguments);
25599         this.sync();
25600     },
25601
25602     setLeftTop : function(left, top){
25603         this.storeLeftTop(left, top);
25604         supr.setLeftTop.apply(this, arguments);
25605         this.sync();
25606     },
25607
25608     setXY : function(xy, a, d, c, e){
25609         this.fixDisplay();
25610         this.beforeAction();
25611         this.storeXY(xy);
25612         var cb = this.createCB(c);
25613         supr.setXY.call(this, xy, a, d, cb, e);
25614         if(!a){
25615             cb();
25616         }
25617     },
25618
25619     // private
25620     createCB : function(c){
25621         var el = this;
25622         return function(){
25623             el.constrainXY();
25624             el.sync(true);
25625             if(c){
25626                 c();
25627             }
25628         };
25629     },
25630
25631     // overridden Element method
25632     setX : function(x, a, d, c, e){
25633         this.setXY([x, this.getY()], a, d, c, e);
25634     },
25635
25636     // overridden Element method
25637     setY : function(y, a, d, c, e){
25638         this.setXY([this.getX(), y], a, d, c, e);
25639     },
25640
25641     // overridden Element method
25642     setSize : function(w, h, a, d, c, e){
25643         this.beforeAction();
25644         var cb = this.createCB(c);
25645         supr.setSize.call(this, w, h, a, d, cb, e);
25646         if(!a){
25647             cb();
25648         }
25649     },
25650
25651     // overridden Element method
25652     setWidth : function(w, a, d, c, e){
25653         this.beforeAction();
25654         var cb = this.createCB(c);
25655         supr.setWidth.call(this, w, a, d, cb, e);
25656         if(!a){
25657             cb();
25658         }
25659     },
25660
25661     // overridden Element method
25662     setHeight : function(h, a, d, c, e){
25663         this.beforeAction();
25664         var cb = this.createCB(c);
25665         supr.setHeight.call(this, h, a, d, cb, e);
25666         if(!a){
25667             cb();
25668         }
25669     },
25670
25671     // overridden Element method
25672     setBounds : function(x, y, w, h, a, d, c, e){
25673         this.beforeAction();
25674         var cb = this.createCB(c);
25675         if(!a){
25676             this.storeXY([x, y]);
25677             supr.setXY.call(this, [x, y]);
25678             supr.setSize.call(this, w, h, a, d, cb, e);
25679             cb();
25680         }else{
25681             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25682         }
25683         return this;
25684     },
25685     
25686     /**
25687      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25688      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25689      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25690      * @param {Number} zindex The new z-index to set
25691      * @return {this} The Layer
25692      */
25693     setZIndex : function(zindex){
25694         this.zindex = zindex;
25695         this.setStyle("z-index", zindex + 2);
25696         if(this.shadow){
25697             this.shadow.setZIndex(zindex + 1);
25698         }
25699         if(this.shim){
25700             this.shim.setStyle("z-index", zindex);
25701         }
25702     }
25703 });
25704 })();/*
25705  * Based on:
25706  * Ext JS Library 1.1.1
25707  * Copyright(c) 2006-2007, Ext JS, LLC.
25708  *
25709  * Originally Released Under LGPL - original licence link has changed is not relivant.
25710  *
25711  * Fork - LGPL
25712  * <script type="text/javascript">
25713  */
25714
25715
25716 /**
25717  * @class Roo.Shadow
25718  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25719  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25720  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25721  * @constructor
25722  * Create a new Shadow
25723  * @param {Object} config The config object
25724  */
25725 Roo.Shadow = function(config){
25726     Roo.apply(this, config);
25727     if(typeof this.mode != "string"){
25728         this.mode = this.defaultMode;
25729     }
25730     var o = this.offset, a = {h: 0};
25731     var rad = Math.floor(this.offset/2);
25732     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25733         case "drop":
25734             a.w = 0;
25735             a.l = a.t = o;
25736             a.t -= 1;
25737             if(Roo.isIE){
25738                 a.l -= this.offset + rad;
25739                 a.t -= this.offset + rad;
25740                 a.w -= rad;
25741                 a.h -= rad;
25742                 a.t += 1;
25743             }
25744         break;
25745         case "sides":
25746             a.w = (o*2);
25747             a.l = -o;
25748             a.t = o-1;
25749             if(Roo.isIE){
25750                 a.l -= (this.offset - rad);
25751                 a.t -= this.offset + rad;
25752                 a.l += 1;
25753                 a.w -= (this.offset - rad)*2;
25754                 a.w -= rad + 1;
25755                 a.h -= 1;
25756             }
25757         break;
25758         case "frame":
25759             a.w = a.h = (o*2);
25760             a.l = a.t = -o;
25761             a.t += 1;
25762             a.h -= 2;
25763             if(Roo.isIE){
25764                 a.l -= (this.offset - rad);
25765                 a.t -= (this.offset - rad);
25766                 a.l += 1;
25767                 a.w -= (this.offset + rad + 1);
25768                 a.h -= (this.offset + rad);
25769                 a.h += 1;
25770             }
25771         break;
25772     };
25773
25774     this.adjusts = a;
25775 };
25776
25777 Roo.Shadow.prototype = {
25778     /**
25779      * @cfg {String} mode
25780      * The shadow display mode.  Supports the following options:<br />
25781      * sides: Shadow displays on both sides and bottom only<br />
25782      * frame: Shadow displays equally on all four sides<br />
25783      * drop: Traditional bottom-right drop shadow (default)
25784      */
25785     /**
25786      * @cfg {String} offset
25787      * The number of pixels to offset the shadow from the element (defaults to 4)
25788      */
25789     offset: 4,
25790
25791     // private
25792     defaultMode: "drop",
25793
25794     /**
25795      * Displays the shadow under the target element
25796      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25797      */
25798     show : function(target){
25799         target = Roo.get(target);
25800         if(!this.el){
25801             this.el = Roo.Shadow.Pool.pull();
25802             if(this.el.dom.nextSibling != target.dom){
25803                 this.el.insertBefore(target);
25804             }
25805         }
25806         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25807         if(Roo.isIE){
25808             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25809         }
25810         this.realign(
25811             target.getLeft(true),
25812             target.getTop(true),
25813             target.getWidth(),
25814             target.getHeight()
25815         );
25816         this.el.dom.style.display = "block";
25817     },
25818
25819     /**
25820      * Returns true if the shadow is visible, else false
25821      */
25822     isVisible : function(){
25823         return this.el ? true : false;  
25824     },
25825
25826     /**
25827      * Direct alignment when values are already available. Show must be called at least once before
25828      * calling this method to ensure it is initialized.
25829      * @param {Number} left The target element left position
25830      * @param {Number} top The target element top position
25831      * @param {Number} width The target element width
25832      * @param {Number} height The target element height
25833      */
25834     realign : function(l, t, w, h){
25835         if(!this.el){
25836             return;
25837         }
25838         var a = this.adjusts, d = this.el.dom, s = d.style;
25839         var iea = 0;
25840         s.left = (l+a.l)+"px";
25841         s.top = (t+a.t)+"px";
25842         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25843  
25844         if(s.width != sws || s.height != shs){
25845             s.width = sws;
25846             s.height = shs;
25847             if(!Roo.isIE){
25848                 var cn = d.childNodes;
25849                 var sww = Math.max(0, (sw-12))+"px";
25850                 cn[0].childNodes[1].style.width = sww;
25851                 cn[1].childNodes[1].style.width = sww;
25852                 cn[2].childNodes[1].style.width = sww;
25853                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25854             }
25855         }
25856     },
25857
25858     /**
25859      * Hides this shadow
25860      */
25861     hide : function(){
25862         if(this.el){
25863             this.el.dom.style.display = "none";
25864             Roo.Shadow.Pool.push(this.el);
25865             delete this.el;
25866         }
25867     },
25868
25869     /**
25870      * Adjust the z-index of this shadow
25871      * @param {Number} zindex The new z-index
25872      */
25873     setZIndex : function(z){
25874         this.zIndex = z;
25875         if(this.el){
25876             this.el.setStyle("z-index", z);
25877         }
25878     }
25879 };
25880
25881 // Private utility class that manages the internal Shadow cache
25882 Roo.Shadow.Pool = function(){
25883     var p = [];
25884     var markup = Roo.isIE ?
25885                  '<div class="x-ie-shadow"></div>' :
25886                  '<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>';
25887     return {
25888         pull : function(){
25889             var sh = p.shift();
25890             if(!sh){
25891                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25892                 sh.autoBoxAdjust = false;
25893             }
25894             return sh;
25895         },
25896
25897         push : function(sh){
25898             p.push(sh);
25899         }
25900     };
25901 }();/*
25902  * Based on:
25903  * Ext JS Library 1.1.1
25904  * Copyright(c) 2006-2007, Ext JS, LLC.
25905  *
25906  * Originally Released Under LGPL - original licence link has changed is not relivant.
25907  *
25908  * Fork - LGPL
25909  * <script type="text/javascript">
25910  */
25911
25912
25913 /**
25914  * @class Roo.SplitBar
25915  * @extends Roo.util.Observable
25916  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25917  * <br><br>
25918  * Usage:
25919  * <pre><code>
25920 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25921                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25922 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25923 split.minSize = 100;
25924 split.maxSize = 600;
25925 split.animate = true;
25926 split.on('moved', splitterMoved);
25927 </code></pre>
25928  * @constructor
25929  * Create a new SplitBar
25930  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25931  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25932  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25933  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25934                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25935                         position of the SplitBar).
25936  */
25937 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25938     
25939     /** @private */
25940     this.el = Roo.get(dragElement, true);
25941     this.el.dom.unselectable = "on";
25942     /** @private */
25943     this.resizingEl = Roo.get(resizingElement, true);
25944
25945     /**
25946      * @private
25947      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25948      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25949      * @type Number
25950      */
25951     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25952     
25953     /**
25954      * The minimum size of the resizing element. (Defaults to 0)
25955      * @type Number
25956      */
25957     this.minSize = 0;
25958     
25959     /**
25960      * The maximum size of the resizing element. (Defaults to 2000)
25961      * @type Number
25962      */
25963     this.maxSize = 2000;
25964     
25965     /**
25966      * Whether to animate the transition to the new size
25967      * @type Boolean
25968      */
25969     this.animate = false;
25970     
25971     /**
25972      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25973      * @type Boolean
25974      */
25975     this.useShim = false;
25976     
25977     /** @private */
25978     this.shim = null;
25979     
25980     if(!existingProxy){
25981         /** @private */
25982         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25983     }else{
25984         this.proxy = Roo.get(existingProxy).dom;
25985     }
25986     /** @private */
25987     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25988     
25989     /** @private */
25990     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25991     
25992     /** @private */
25993     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25994     
25995     /** @private */
25996     this.dragSpecs = {};
25997     
25998     /**
25999      * @private The adapter to use to positon and resize elements
26000      */
26001     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26002     this.adapter.init(this);
26003     
26004     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26005         /** @private */
26006         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26007         this.el.addClass("x-splitbar-h");
26008     }else{
26009         /** @private */
26010         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26011         this.el.addClass("x-splitbar-v");
26012     }
26013     
26014     this.addEvents({
26015         /**
26016          * @event resize
26017          * Fires when the splitter is moved (alias for {@link #event-moved})
26018          * @param {Roo.SplitBar} this
26019          * @param {Number} newSize the new width or height
26020          */
26021         "resize" : true,
26022         /**
26023          * @event moved
26024          * Fires when the splitter is moved
26025          * @param {Roo.SplitBar} this
26026          * @param {Number} newSize the new width or height
26027          */
26028         "moved" : true,
26029         /**
26030          * @event beforeresize
26031          * Fires before the splitter is dragged
26032          * @param {Roo.SplitBar} this
26033          */
26034         "beforeresize" : true,
26035
26036         "beforeapply" : true
26037     });
26038
26039     Roo.util.Observable.call(this);
26040 };
26041
26042 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26043     onStartProxyDrag : function(x, y){
26044         this.fireEvent("beforeresize", this);
26045         if(!this.overlay){
26046             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26047             o.unselectable();
26048             o.enableDisplayMode("block");
26049             // all splitbars share the same overlay
26050             Roo.SplitBar.prototype.overlay = o;
26051         }
26052         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26053         this.overlay.show();
26054         Roo.get(this.proxy).setDisplayed("block");
26055         var size = this.adapter.getElementSize(this);
26056         this.activeMinSize = this.getMinimumSize();;
26057         this.activeMaxSize = this.getMaximumSize();;
26058         var c1 = size - this.activeMinSize;
26059         var c2 = Math.max(this.activeMaxSize - size, 0);
26060         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26061             this.dd.resetConstraints();
26062             this.dd.setXConstraint(
26063                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26064                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26065             );
26066             this.dd.setYConstraint(0, 0);
26067         }else{
26068             this.dd.resetConstraints();
26069             this.dd.setXConstraint(0, 0);
26070             this.dd.setYConstraint(
26071                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26072                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26073             );
26074          }
26075         this.dragSpecs.startSize = size;
26076         this.dragSpecs.startPoint = [x, y];
26077         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26078     },
26079     
26080     /** 
26081      * @private Called after the drag operation by the DDProxy
26082      */
26083     onEndProxyDrag : function(e){
26084         Roo.get(this.proxy).setDisplayed(false);
26085         var endPoint = Roo.lib.Event.getXY(e);
26086         if(this.overlay){
26087             this.overlay.hide();
26088         }
26089         var newSize;
26090         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26091             newSize = this.dragSpecs.startSize + 
26092                 (this.placement == Roo.SplitBar.LEFT ?
26093                     endPoint[0] - this.dragSpecs.startPoint[0] :
26094                     this.dragSpecs.startPoint[0] - endPoint[0]
26095                 );
26096         }else{
26097             newSize = this.dragSpecs.startSize + 
26098                 (this.placement == Roo.SplitBar.TOP ?
26099                     endPoint[1] - this.dragSpecs.startPoint[1] :
26100                     this.dragSpecs.startPoint[1] - endPoint[1]
26101                 );
26102         }
26103         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26104         if(newSize != this.dragSpecs.startSize){
26105             if(this.fireEvent('beforeapply', this, newSize) !== false){
26106                 this.adapter.setElementSize(this, newSize);
26107                 this.fireEvent("moved", this, newSize);
26108                 this.fireEvent("resize", this, newSize);
26109             }
26110         }
26111     },
26112     
26113     /**
26114      * Get the adapter this SplitBar uses
26115      * @return The adapter object
26116      */
26117     getAdapter : function(){
26118         return this.adapter;
26119     },
26120     
26121     /**
26122      * Set the adapter this SplitBar uses
26123      * @param {Object} adapter A SplitBar adapter object
26124      */
26125     setAdapter : function(adapter){
26126         this.adapter = adapter;
26127         this.adapter.init(this);
26128     },
26129     
26130     /**
26131      * Gets the minimum size for the resizing element
26132      * @return {Number} The minimum size
26133      */
26134     getMinimumSize : function(){
26135         return this.minSize;
26136     },
26137     
26138     /**
26139      * Sets the minimum size for the resizing element
26140      * @param {Number} minSize The minimum size
26141      */
26142     setMinimumSize : function(minSize){
26143         this.minSize = minSize;
26144     },
26145     
26146     /**
26147      * Gets the maximum size for the resizing element
26148      * @return {Number} The maximum size
26149      */
26150     getMaximumSize : function(){
26151         return this.maxSize;
26152     },
26153     
26154     /**
26155      * Sets the maximum size for the resizing element
26156      * @param {Number} maxSize The maximum size
26157      */
26158     setMaximumSize : function(maxSize){
26159         this.maxSize = maxSize;
26160     },
26161     
26162     /**
26163      * Sets the initialize size for the resizing element
26164      * @param {Number} size The initial size
26165      */
26166     setCurrentSize : function(size){
26167         var oldAnimate = this.animate;
26168         this.animate = false;
26169         this.adapter.setElementSize(this, size);
26170         this.animate = oldAnimate;
26171     },
26172     
26173     /**
26174      * Destroy this splitbar. 
26175      * @param {Boolean} removeEl True to remove the element
26176      */
26177     destroy : function(removeEl){
26178         if(this.shim){
26179             this.shim.remove();
26180         }
26181         this.dd.unreg();
26182         this.proxy.parentNode.removeChild(this.proxy);
26183         if(removeEl){
26184             this.el.remove();
26185         }
26186     }
26187 });
26188
26189 /**
26190  * @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.
26191  */
26192 Roo.SplitBar.createProxy = function(dir){
26193     var proxy = new Roo.Element(document.createElement("div"));
26194     proxy.unselectable();
26195     var cls = 'x-splitbar-proxy';
26196     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26197     document.body.appendChild(proxy.dom);
26198     return proxy.dom;
26199 };
26200
26201 /** 
26202  * @class Roo.SplitBar.BasicLayoutAdapter
26203  * Default Adapter. It assumes the splitter and resizing element are not positioned
26204  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26205  */
26206 Roo.SplitBar.BasicLayoutAdapter = function(){
26207 };
26208
26209 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26210     // do nothing for now
26211     init : function(s){
26212     
26213     },
26214     /**
26215      * Called before drag operations to get the current size of the resizing element. 
26216      * @param {Roo.SplitBar} s The SplitBar using this adapter
26217      */
26218      getElementSize : function(s){
26219         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26220             return s.resizingEl.getWidth();
26221         }else{
26222             return s.resizingEl.getHeight();
26223         }
26224     },
26225     
26226     /**
26227      * Called after drag operations to set the size of the resizing element.
26228      * @param {Roo.SplitBar} s The SplitBar using this adapter
26229      * @param {Number} newSize The new size to set
26230      * @param {Function} onComplete A function to be invoked when resizing is complete
26231      */
26232     setElementSize : function(s, newSize, onComplete){
26233         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26234             if(!s.animate){
26235                 s.resizingEl.setWidth(newSize);
26236                 if(onComplete){
26237                     onComplete(s, newSize);
26238                 }
26239             }else{
26240                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26241             }
26242         }else{
26243             
26244             if(!s.animate){
26245                 s.resizingEl.setHeight(newSize);
26246                 if(onComplete){
26247                     onComplete(s, newSize);
26248                 }
26249             }else{
26250                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26251             }
26252         }
26253     }
26254 };
26255
26256 /** 
26257  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26258  * @extends Roo.SplitBar.BasicLayoutAdapter
26259  * Adapter that  moves the splitter element to align with the resized sizing element. 
26260  * Used with an absolute positioned SplitBar.
26261  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26262  * document.body, make sure you assign an id to the body element.
26263  */
26264 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26265     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26266     this.container = Roo.get(container);
26267 };
26268
26269 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26270     init : function(s){
26271         this.basic.init(s);
26272     },
26273     
26274     getElementSize : function(s){
26275         return this.basic.getElementSize(s);
26276     },
26277     
26278     setElementSize : function(s, newSize, onComplete){
26279         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26280     },
26281     
26282     moveSplitter : function(s){
26283         var yes = Roo.SplitBar;
26284         switch(s.placement){
26285             case yes.LEFT:
26286                 s.el.setX(s.resizingEl.getRight());
26287                 break;
26288             case yes.RIGHT:
26289                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26290                 break;
26291             case yes.TOP:
26292                 s.el.setY(s.resizingEl.getBottom());
26293                 break;
26294             case yes.BOTTOM:
26295                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26296                 break;
26297         }
26298     }
26299 };
26300
26301 /**
26302  * Orientation constant - Create a vertical SplitBar
26303  * @static
26304  * @type Number
26305  */
26306 Roo.SplitBar.VERTICAL = 1;
26307
26308 /**
26309  * Orientation constant - Create a horizontal SplitBar
26310  * @static
26311  * @type Number
26312  */
26313 Roo.SplitBar.HORIZONTAL = 2;
26314
26315 /**
26316  * Placement constant - The resizing element is to the left of the splitter element
26317  * @static
26318  * @type Number
26319  */
26320 Roo.SplitBar.LEFT = 1;
26321
26322 /**
26323  * Placement constant - The resizing element is to the right of the splitter element
26324  * @static
26325  * @type Number
26326  */
26327 Roo.SplitBar.RIGHT = 2;
26328
26329 /**
26330  * Placement constant - The resizing element is positioned above the splitter element
26331  * @static
26332  * @type Number
26333  */
26334 Roo.SplitBar.TOP = 3;
26335
26336 /**
26337  * Placement constant - The resizing element is positioned under splitter element
26338  * @static
26339  * @type Number
26340  */
26341 Roo.SplitBar.BOTTOM = 4;
26342 /*
26343  * Based on:
26344  * Ext JS Library 1.1.1
26345  * Copyright(c) 2006-2007, Ext JS, LLC.
26346  *
26347  * Originally Released Under LGPL - original licence link has changed is not relivant.
26348  *
26349  * Fork - LGPL
26350  * <script type="text/javascript">
26351  */
26352
26353 /**
26354  * @class Roo.View
26355  * @extends Roo.util.Observable
26356  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26357  * This class also supports single and multi selection modes. <br>
26358  * Create a data model bound view:
26359  <pre><code>
26360  var store = new Roo.data.Store(...);
26361
26362  var view = new Roo.View({
26363     el : "my-element",
26364     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26365  
26366     singleSelect: true,
26367     selectedClass: "ydataview-selected",
26368     store: store
26369  });
26370
26371  // listen for node click?
26372  view.on("click", function(vw, index, node, e){
26373  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26374  });
26375
26376  // load XML data
26377  dataModel.load("foobar.xml");
26378  </code></pre>
26379  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26380  * <br><br>
26381  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26382  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26383  * 
26384  * Note: old style constructor is still suported (container, template, config)
26385  * 
26386  * @constructor
26387  * Create a new View
26388  * @param {Object} config The config object
26389  * 
26390  */
26391 Roo.View = function(config, depreciated_tpl, depreciated_config){
26392     
26393     this.parent = false;
26394     
26395     if (typeof(depreciated_tpl) == 'undefined') {
26396         // new way.. - universal constructor.
26397         Roo.apply(this, config);
26398         this.el  = Roo.get(this.el);
26399     } else {
26400         // old format..
26401         this.el  = Roo.get(config);
26402         this.tpl = depreciated_tpl;
26403         Roo.apply(this, depreciated_config);
26404     }
26405     this.wrapEl  = this.el.wrap().wrap();
26406     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26407     
26408     
26409     if(typeof(this.tpl) == "string"){
26410         this.tpl = new Roo.Template(this.tpl);
26411     } else {
26412         // support xtype ctors..
26413         this.tpl = new Roo.factory(this.tpl, Roo);
26414     }
26415     
26416     
26417     this.tpl.compile();
26418     
26419     /** @private */
26420     this.addEvents({
26421         /**
26422          * @event beforeclick
26423          * Fires before a click is processed. Returns false to cancel the default action.
26424          * @param {Roo.View} this
26425          * @param {Number} index The index of the target node
26426          * @param {HTMLElement} node The target node
26427          * @param {Roo.EventObject} e The raw event object
26428          */
26429             "beforeclick" : true,
26430         /**
26431          * @event click
26432          * Fires when a template node is clicked.
26433          * @param {Roo.View} this
26434          * @param {Number} index The index of the target node
26435          * @param {HTMLElement} node The target node
26436          * @param {Roo.EventObject} e The raw event object
26437          */
26438             "click" : true,
26439         /**
26440          * @event dblclick
26441          * Fires when a template node is double clicked.
26442          * @param {Roo.View} this
26443          * @param {Number} index The index of the target node
26444          * @param {HTMLElement} node The target node
26445          * @param {Roo.EventObject} e The raw event object
26446          */
26447             "dblclick" : true,
26448         /**
26449          * @event contextmenu
26450          * Fires when a template node is right clicked.
26451          * @param {Roo.View} this
26452          * @param {Number} index The index of the target node
26453          * @param {HTMLElement} node The target node
26454          * @param {Roo.EventObject} e The raw event object
26455          */
26456             "contextmenu" : true,
26457         /**
26458          * @event selectionchange
26459          * Fires when the selected nodes change.
26460          * @param {Roo.View} this
26461          * @param {Array} selections Array of the selected nodes
26462          */
26463             "selectionchange" : true,
26464     
26465         /**
26466          * @event beforeselect
26467          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26468          * @param {Roo.View} this
26469          * @param {HTMLElement} node The node to be selected
26470          * @param {Array} selections Array of currently selected nodes
26471          */
26472             "beforeselect" : true,
26473         /**
26474          * @event preparedata
26475          * Fires on every row to render, to allow you to change the data.
26476          * @param {Roo.View} this
26477          * @param {Object} data to be rendered (change this)
26478          */
26479           "preparedata" : true
26480           
26481           
26482         });
26483
26484
26485
26486     this.el.on({
26487         "click": this.onClick,
26488         "dblclick": this.onDblClick,
26489         "contextmenu": this.onContextMenu,
26490         scope:this
26491     });
26492
26493     this.selections = [];
26494     this.nodes = [];
26495     this.cmp = new Roo.CompositeElementLite([]);
26496     if(this.store){
26497         this.store = Roo.factory(this.store, Roo.data);
26498         this.setStore(this.store, true);
26499     }
26500     
26501     if ( this.footer && this.footer.xtype) {
26502            
26503          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26504         
26505         this.footer.dataSource = this.store;
26506         this.footer.container = fctr;
26507         this.footer = Roo.factory(this.footer, Roo);
26508         fctr.insertFirst(this.el);
26509         
26510         // this is a bit insane - as the paging toolbar seems to detach the el..
26511 //        dom.parentNode.parentNode.parentNode
26512          // they get detached?
26513     }
26514     
26515     
26516     Roo.View.superclass.constructor.call(this);
26517     
26518     
26519 };
26520
26521 Roo.extend(Roo.View, Roo.util.Observable, {
26522     
26523      /**
26524      * @cfg {Roo.data.Store} store Data store to load data from.
26525      */
26526     store : false,
26527     
26528     /**
26529      * @cfg {String|Roo.Element} el The container element.
26530      */
26531     el : '',
26532     
26533     /**
26534      * @cfg {String|Roo.Template} tpl The template used by this View 
26535      */
26536     tpl : false,
26537     /**
26538      * @cfg {String} dataName the named area of the template to use as the data area
26539      *                          Works with domtemplates roo-name="name"
26540      */
26541     dataName: false,
26542     /**
26543      * @cfg {String} selectedClass The css class to add to selected nodes
26544      */
26545     selectedClass : "x-view-selected",
26546      /**
26547      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26548      */
26549     emptyText : "",
26550     
26551     /**
26552      * @cfg {String} text to display on mask (default Loading)
26553      */
26554     mask : false,
26555     /**
26556      * @cfg {Boolean} multiSelect Allow multiple selection
26557      */
26558     multiSelect : false,
26559     /**
26560      * @cfg {Boolean} singleSelect Allow single selection
26561      */
26562     singleSelect:  false,
26563     
26564     /**
26565      * @cfg {Boolean} toggleSelect - selecting 
26566      */
26567     toggleSelect : false,
26568     
26569     /**
26570      * @cfg {Boolean} tickable - selecting 
26571      */
26572     tickable : false,
26573     
26574     /**
26575      * Returns the element this view is bound to.
26576      * @return {Roo.Element}
26577      */
26578     getEl : function(){
26579         return this.wrapEl;
26580     },
26581     
26582     
26583
26584     /**
26585      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26586      */
26587     refresh : function(){
26588         //Roo.log('refresh');
26589         var t = this.tpl;
26590         
26591         // if we are using something like 'domtemplate', then
26592         // the what gets used is:
26593         // t.applySubtemplate(NAME, data, wrapping data..)
26594         // the outer template then get' applied with
26595         //     the store 'extra data'
26596         // and the body get's added to the
26597         //      roo-name="data" node?
26598         //      <span class='roo-tpl-{name}'></span> ?????
26599         
26600         
26601         
26602         this.clearSelections();
26603         this.el.update("");
26604         var html = [];
26605         var records = this.store.getRange();
26606         if(records.length < 1) {
26607             
26608             // is this valid??  = should it render a template??
26609             
26610             this.el.update(this.emptyText);
26611             return;
26612         }
26613         var el = this.el;
26614         if (this.dataName) {
26615             this.el.update(t.apply(this.store.meta)); //????
26616             el = this.el.child('.roo-tpl-' + this.dataName);
26617         }
26618         
26619         for(var i = 0, len = records.length; i < len; i++){
26620             var data = this.prepareData(records[i].data, i, records[i]);
26621             this.fireEvent("preparedata", this, data, i, records[i]);
26622             
26623             var d = Roo.apply({}, data);
26624             
26625             if(this.tickable){
26626                 Roo.apply(d, {'roo-id' : Roo.id()});
26627                 
26628                 var _this = this;
26629             
26630                 Roo.each(this.parent.item, function(item){
26631                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26632                         return;
26633                     }
26634                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26635                 });
26636             }
26637             
26638             html[html.length] = Roo.util.Format.trim(
26639                 this.dataName ?
26640                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26641                     t.apply(d)
26642             );
26643         }
26644         
26645         
26646         
26647         el.update(html.join(""));
26648         this.nodes = el.dom.childNodes;
26649         this.updateIndexes(0);
26650     },
26651     
26652
26653     /**
26654      * Function to override to reformat the data that is sent to
26655      * the template for each node.
26656      * DEPRICATED - use the preparedata event handler.
26657      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26658      * a JSON object for an UpdateManager bound view).
26659      */
26660     prepareData : function(data, index, record)
26661     {
26662         this.fireEvent("preparedata", this, data, index, record);
26663         return data;
26664     },
26665
26666     onUpdate : function(ds, record){
26667         // Roo.log('on update');   
26668         this.clearSelections();
26669         var index = this.store.indexOf(record);
26670         var n = this.nodes[index];
26671         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26672         n.parentNode.removeChild(n);
26673         this.updateIndexes(index, index);
26674     },
26675
26676     
26677     
26678 // --------- FIXME     
26679     onAdd : function(ds, records, index)
26680     {
26681         //Roo.log(['on Add', ds, records, index] );        
26682         this.clearSelections();
26683         if(this.nodes.length == 0){
26684             this.refresh();
26685             return;
26686         }
26687         var n = this.nodes[index];
26688         for(var i = 0, len = records.length; i < len; i++){
26689             var d = this.prepareData(records[i].data, i, records[i]);
26690             if(n){
26691                 this.tpl.insertBefore(n, d);
26692             }else{
26693                 
26694                 this.tpl.append(this.el, d);
26695             }
26696         }
26697         this.updateIndexes(index);
26698     },
26699
26700     onRemove : function(ds, record, index){
26701        // Roo.log('onRemove');
26702         this.clearSelections();
26703         var el = this.dataName  ?
26704             this.el.child('.roo-tpl-' + this.dataName) :
26705             this.el; 
26706         
26707         el.dom.removeChild(this.nodes[index]);
26708         this.updateIndexes(index);
26709     },
26710
26711     /**
26712      * Refresh an individual node.
26713      * @param {Number} index
26714      */
26715     refreshNode : function(index){
26716         this.onUpdate(this.store, this.store.getAt(index));
26717     },
26718
26719     updateIndexes : function(startIndex, endIndex){
26720         var ns = this.nodes;
26721         startIndex = startIndex || 0;
26722         endIndex = endIndex || ns.length - 1;
26723         for(var i = startIndex; i <= endIndex; i++){
26724             ns[i].nodeIndex = i;
26725         }
26726     },
26727
26728     /**
26729      * Changes the data store this view uses and refresh the view.
26730      * @param {Store} store
26731      */
26732     setStore : function(store, initial){
26733         if(!initial && this.store){
26734             this.store.un("datachanged", this.refresh);
26735             this.store.un("add", this.onAdd);
26736             this.store.un("remove", this.onRemove);
26737             this.store.un("update", this.onUpdate);
26738             this.store.un("clear", this.refresh);
26739             this.store.un("beforeload", this.onBeforeLoad);
26740             this.store.un("load", this.onLoad);
26741             this.store.un("loadexception", this.onLoad);
26742         }
26743         if(store){
26744           
26745             store.on("datachanged", this.refresh, this);
26746             store.on("add", this.onAdd, this);
26747             store.on("remove", this.onRemove, this);
26748             store.on("update", this.onUpdate, this);
26749             store.on("clear", this.refresh, this);
26750             store.on("beforeload", this.onBeforeLoad, this);
26751             store.on("load", this.onLoad, this);
26752             store.on("loadexception", this.onLoad, this);
26753         }
26754         
26755         if(store){
26756             this.refresh();
26757         }
26758     },
26759     /**
26760      * onbeforeLoad - masks the loading area.
26761      *
26762      */
26763     onBeforeLoad : function(store,opts)
26764     {
26765          //Roo.log('onBeforeLoad');   
26766         if (!opts.add) {
26767             this.el.update("");
26768         }
26769         this.el.mask(this.mask ? this.mask : "Loading" ); 
26770     },
26771     onLoad : function ()
26772     {
26773         this.el.unmask();
26774     },
26775     
26776
26777     /**
26778      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26779      * @param {HTMLElement} node
26780      * @return {HTMLElement} The template node
26781      */
26782     findItemFromChild : function(node){
26783         var el = this.dataName  ?
26784             this.el.child('.roo-tpl-' + this.dataName,true) :
26785             this.el.dom; 
26786         
26787         if(!node || node.parentNode == el){
26788                     return node;
26789             }
26790             var p = node.parentNode;
26791             while(p && p != el){
26792             if(p.parentNode == el){
26793                 return p;
26794             }
26795             p = p.parentNode;
26796         }
26797             return null;
26798     },
26799
26800     /** @ignore */
26801     onClick : function(e){
26802         var item = this.findItemFromChild(e.getTarget());
26803         if(item){
26804             var index = this.indexOf(item);
26805             if(this.onItemClick(item, index, e) !== false){
26806                 this.fireEvent("click", this, index, item, e);
26807             }
26808         }else{
26809             this.clearSelections();
26810         }
26811     },
26812
26813     /** @ignore */
26814     onContextMenu : function(e){
26815         var item = this.findItemFromChild(e.getTarget());
26816         if(item){
26817             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26818         }
26819     },
26820
26821     /** @ignore */
26822     onDblClick : function(e){
26823         var item = this.findItemFromChild(e.getTarget());
26824         if(item){
26825             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26826         }
26827     },
26828
26829     onItemClick : function(item, index, e)
26830     {
26831         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26832             return false;
26833         }
26834         if (this.toggleSelect) {
26835             var m = this.isSelected(item) ? 'unselect' : 'select';
26836             //Roo.log(m);
26837             var _t = this;
26838             _t[m](item, true, false);
26839             return true;
26840         }
26841         if(this.multiSelect || this.singleSelect){
26842             if(this.multiSelect && e.shiftKey && this.lastSelection){
26843                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26844             }else{
26845                 this.select(item, this.multiSelect && e.ctrlKey);
26846                 this.lastSelection = item;
26847             }
26848             
26849             if(!this.tickable){
26850                 e.preventDefault();
26851             }
26852             
26853         }
26854         return true;
26855     },
26856
26857     /**
26858      * Get the number of selected nodes.
26859      * @return {Number}
26860      */
26861     getSelectionCount : function(){
26862         return this.selections.length;
26863     },
26864
26865     /**
26866      * Get the currently selected nodes.
26867      * @return {Array} An array of HTMLElements
26868      */
26869     getSelectedNodes : function(){
26870         return this.selections;
26871     },
26872
26873     /**
26874      * Get the indexes of the selected nodes.
26875      * @return {Array}
26876      */
26877     getSelectedIndexes : function(){
26878         var indexes = [], s = this.selections;
26879         for(var i = 0, len = s.length; i < len; i++){
26880             indexes.push(s[i].nodeIndex);
26881         }
26882         return indexes;
26883     },
26884
26885     /**
26886      * Clear all selections
26887      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26888      */
26889     clearSelections : function(suppressEvent){
26890         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26891             this.cmp.elements = this.selections;
26892             this.cmp.removeClass(this.selectedClass);
26893             this.selections = [];
26894             if(!suppressEvent){
26895                 this.fireEvent("selectionchange", this, this.selections);
26896             }
26897         }
26898     },
26899
26900     /**
26901      * Returns true if the passed node is selected
26902      * @param {HTMLElement/Number} node The node or node index
26903      * @return {Boolean}
26904      */
26905     isSelected : function(node){
26906         var s = this.selections;
26907         if(s.length < 1){
26908             return false;
26909         }
26910         node = this.getNode(node);
26911         return s.indexOf(node) !== -1;
26912     },
26913
26914     /**
26915      * Selects nodes.
26916      * @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
26917      * @param {Boolean} keepExisting (optional) true to keep existing selections
26918      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26919      */
26920     select : function(nodeInfo, keepExisting, suppressEvent){
26921         if(nodeInfo instanceof Array){
26922             if(!keepExisting){
26923                 this.clearSelections(true);
26924             }
26925             for(var i = 0, len = nodeInfo.length; i < len; i++){
26926                 this.select(nodeInfo[i], true, true);
26927             }
26928             return;
26929         } 
26930         var node = this.getNode(nodeInfo);
26931         if(!node || this.isSelected(node)){
26932             return; // already selected.
26933         }
26934         if(!keepExisting){
26935             this.clearSelections(true);
26936         }
26937         
26938         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26939             Roo.fly(node).addClass(this.selectedClass);
26940             this.selections.push(node);
26941             if(!suppressEvent){
26942                 this.fireEvent("selectionchange", this, this.selections);
26943             }
26944         }
26945         
26946         
26947     },
26948       /**
26949      * Unselects nodes.
26950      * @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
26951      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26952      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26953      */
26954     unselect : function(nodeInfo, keepExisting, suppressEvent)
26955     {
26956         if(nodeInfo instanceof Array){
26957             Roo.each(this.selections, function(s) {
26958                 this.unselect(s, nodeInfo);
26959             }, this);
26960             return;
26961         }
26962         var node = this.getNode(nodeInfo);
26963         if(!node || !this.isSelected(node)){
26964             //Roo.log("not selected");
26965             return; // not selected.
26966         }
26967         // fireevent???
26968         var ns = [];
26969         Roo.each(this.selections, function(s) {
26970             if (s == node ) {
26971                 Roo.fly(node).removeClass(this.selectedClass);
26972
26973                 return;
26974             }
26975             ns.push(s);
26976         },this);
26977         
26978         this.selections= ns;
26979         this.fireEvent("selectionchange", this, this.selections);
26980     },
26981
26982     /**
26983      * Gets a template node.
26984      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26985      * @return {HTMLElement} The node or null if it wasn't found
26986      */
26987     getNode : function(nodeInfo){
26988         if(typeof nodeInfo == "string"){
26989             return document.getElementById(nodeInfo);
26990         }else if(typeof nodeInfo == "number"){
26991             return this.nodes[nodeInfo];
26992         }
26993         return nodeInfo;
26994     },
26995
26996     /**
26997      * Gets a range template nodes.
26998      * @param {Number} startIndex
26999      * @param {Number} endIndex
27000      * @return {Array} An array of nodes
27001      */
27002     getNodes : function(start, end){
27003         var ns = this.nodes;
27004         start = start || 0;
27005         end = typeof end == "undefined" ? ns.length - 1 : end;
27006         var nodes = [];
27007         if(start <= end){
27008             for(var i = start; i <= end; i++){
27009                 nodes.push(ns[i]);
27010             }
27011         } else{
27012             for(var i = start; i >= end; i--){
27013                 nodes.push(ns[i]);
27014             }
27015         }
27016         return nodes;
27017     },
27018
27019     /**
27020      * Finds the index of the passed node
27021      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27022      * @return {Number} The index of the node or -1
27023      */
27024     indexOf : function(node){
27025         node = this.getNode(node);
27026         if(typeof node.nodeIndex == "number"){
27027             return node.nodeIndex;
27028         }
27029         var ns = this.nodes;
27030         for(var i = 0, len = ns.length; i < len; i++){
27031             if(ns[i] == node){
27032                 return i;
27033             }
27034         }
27035         return -1;
27036     }
27037 });
27038 /*
27039  * Based on:
27040  * Ext JS Library 1.1.1
27041  * Copyright(c) 2006-2007, Ext JS, LLC.
27042  *
27043  * Originally Released Under LGPL - original licence link has changed is not relivant.
27044  *
27045  * Fork - LGPL
27046  * <script type="text/javascript">
27047  */
27048
27049 /**
27050  * @class Roo.JsonView
27051  * @extends Roo.View
27052  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27053 <pre><code>
27054 var view = new Roo.JsonView({
27055     container: "my-element",
27056     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27057     multiSelect: true, 
27058     jsonRoot: "data" 
27059 });
27060
27061 // listen for node click?
27062 view.on("click", function(vw, index, node, e){
27063     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27064 });
27065
27066 // direct load of JSON data
27067 view.load("foobar.php");
27068
27069 // Example from my blog list
27070 var tpl = new Roo.Template(
27071     '&lt;div class="entry"&gt;' +
27072     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27073     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27074     "&lt;/div&gt;&lt;hr /&gt;"
27075 );
27076
27077 var moreView = new Roo.JsonView({
27078     container :  "entry-list", 
27079     template : tpl,
27080     jsonRoot: "posts"
27081 });
27082 moreView.on("beforerender", this.sortEntries, this);
27083 moreView.load({
27084     url: "/blog/get-posts.php",
27085     params: "allposts=true",
27086     text: "Loading Blog Entries..."
27087 });
27088 </code></pre>
27089
27090 * Note: old code is supported with arguments : (container, template, config)
27091
27092
27093  * @constructor
27094  * Create a new JsonView
27095  * 
27096  * @param {Object} config The config object
27097  * 
27098  */
27099 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27100     
27101     
27102     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27103
27104     var um = this.el.getUpdateManager();
27105     um.setRenderer(this);
27106     um.on("update", this.onLoad, this);
27107     um.on("failure", this.onLoadException, this);
27108
27109     /**
27110      * @event beforerender
27111      * Fires before rendering of the downloaded JSON data.
27112      * @param {Roo.JsonView} this
27113      * @param {Object} data The JSON data loaded
27114      */
27115     /**
27116      * @event load
27117      * Fires when data is loaded.
27118      * @param {Roo.JsonView} this
27119      * @param {Object} data The JSON data loaded
27120      * @param {Object} response The raw Connect response object
27121      */
27122     /**
27123      * @event loadexception
27124      * Fires when loading fails.
27125      * @param {Roo.JsonView} this
27126      * @param {Object} response The raw Connect response object
27127      */
27128     this.addEvents({
27129         'beforerender' : true,
27130         'load' : true,
27131         'loadexception' : true
27132     });
27133 };
27134 Roo.extend(Roo.JsonView, Roo.View, {
27135     /**
27136      * @type {String} The root property in the loaded JSON object that contains the data
27137      */
27138     jsonRoot : "",
27139
27140     /**
27141      * Refreshes the view.
27142      */
27143     refresh : function(){
27144         this.clearSelections();
27145         this.el.update("");
27146         var html = [];
27147         var o = this.jsonData;
27148         if(o && o.length > 0){
27149             for(var i = 0, len = o.length; i < len; i++){
27150                 var data = this.prepareData(o[i], i, o);
27151                 html[html.length] = this.tpl.apply(data);
27152             }
27153         }else{
27154             html.push(this.emptyText);
27155         }
27156         this.el.update(html.join(""));
27157         this.nodes = this.el.dom.childNodes;
27158         this.updateIndexes(0);
27159     },
27160
27161     /**
27162      * 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.
27163      * @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:
27164      <pre><code>
27165      view.load({
27166          url: "your-url.php",
27167          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27168          callback: yourFunction,
27169          scope: yourObject, //(optional scope)
27170          discardUrl: false,
27171          nocache: false,
27172          text: "Loading...",
27173          timeout: 30,
27174          scripts: false
27175      });
27176      </code></pre>
27177      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27178      * 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.
27179      * @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}
27180      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27181      * @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.
27182      */
27183     load : function(){
27184         var um = this.el.getUpdateManager();
27185         um.update.apply(um, arguments);
27186     },
27187
27188     // note - render is a standard framework call...
27189     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27190     render : function(el, response){
27191         
27192         this.clearSelections();
27193         this.el.update("");
27194         var o;
27195         try{
27196             if (response != '') {
27197                 o = Roo.util.JSON.decode(response.responseText);
27198                 if(this.jsonRoot){
27199                     
27200                     o = o[this.jsonRoot];
27201                 }
27202             }
27203         } catch(e){
27204         }
27205         /**
27206          * The current JSON data or null
27207          */
27208         this.jsonData = o;
27209         this.beforeRender();
27210         this.refresh();
27211     },
27212
27213 /**
27214  * Get the number of records in the current JSON dataset
27215  * @return {Number}
27216  */
27217     getCount : function(){
27218         return this.jsonData ? this.jsonData.length : 0;
27219     },
27220
27221 /**
27222  * Returns the JSON object for the specified node(s)
27223  * @param {HTMLElement/Array} node The node or an array of nodes
27224  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27225  * you get the JSON object for the node
27226  */
27227     getNodeData : function(node){
27228         if(node instanceof Array){
27229             var data = [];
27230             for(var i = 0, len = node.length; i < len; i++){
27231                 data.push(this.getNodeData(node[i]));
27232             }
27233             return data;
27234         }
27235         return this.jsonData[this.indexOf(node)] || null;
27236     },
27237
27238     beforeRender : function(){
27239         this.snapshot = this.jsonData;
27240         if(this.sortInfo){
27241             this.sort.apply(this, this.sortInfo);
27242         }
27243         this.fireEvent("beforerender", this, this.jsonData);
27244     },
27245
27246     onLoad : function(el, o){
27247         this.fireEvent("load", this, this.jsonData, o);
27248     },
27249
27250     onLoadException : function(el, o){
27251         this.fireEvent("loadexception", this, o);
27252     },
27253
27254 /**
27255  * Filter the data by a specific property.
27256  * @param {String} property A property on your JSON objects
27257  * @param {String/RegExp} value Either string that the property values
27258  * should start with, or a RegExp to test against the property
27259  */
27260     filter : function(property, value){
27261         if(this.jsonData){
27262             var data = [];
27263             var ss = this.snapshot;
27264             if(typeof value == "string"){
27265                 var vlen = value.length;
27266                 if(vlen == 0){
27267                     this.clearFilter();
27268                     return;
27269                 }
27270                 value = value.toLowerCase();
27271                 for(var i = 0, len = ss.length; i < len; i++){
27272                     var o = ss[i];
27273                     if(o[property].substr(0, vlen).toLowerCase() == value){
27274                         data.push(o);
27275                     }
27276                 }
27277             } else if(value.exec){ // regex?
27278                 for(var i = 0, len = ss.length; i < len; i++){
27279                     var o = ss[i];
27280                     if(value.test(o[property])){
27281                         data.push(o);
27282                     }
27283                 }
27284             } else{
27285                 return;
27286             }
27287             this.jsonData = data;
27288             this.refresh();
27289         }
27290     },
27291
27292 /**
27293  * Filter by a function. The passed function will be called with each
27294  * object in the current dataset. If the function returns true the value is kept,
27295  * otherwise it is filtered.
27296  * @param {Function} fn
27297  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27298  */
27299     filterBy : function(fn, scope){
27300         if(this.jsonData){
27301             var data = [];
27302             var ss = this.snapshot;
27303             for(var i = 0, len = ss.length; i < len; i++){
27304                 var o = ss[i];
27305                 if(fn.call(scope || this, o)){
27306                     data.push(o);
27307                 }
27308             }
27309             this.jsonData = data;
27310             this.refresh();
27311         }
27312     },
27313
27314 /**
27315  * Clears the current filter.
27316  */
27317     clearFilter : function(){
27318         if(this.snapshot && this.jsonData != this.snapshot){
27319             this.jsonData = this.snapshot;
27320             this.refresh();
27321         }
27322     },
27323
27324
27325 /**
27326  * Sorts the data for this view and refreshes it.
27327  * @param {String} property A property on your JSON objects to sort on
27328  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27329  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27330  */
27331     sort : function(property, dir, sortType){
27332         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27333         if(this.jsonData){
27334             var p = property;
27335             var dsc = dir && dir.toLowerCase() == "desc";
27336             var f = function(o1, o2){
27337                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27338                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27339                 ;
27340                 if(v1 < v2){
27341                     return dsc ? +1 : -1;
27342                 } else if(v1 > v2){
27343                     return dsc ? -1 : +1;
27344                 } else{
27345                     return 0;
27346                 }
27347             };
27348             this.jsonData.sort(f);
27349             this.refresh();
27350             if(this.jsonData != this.snapshot){
27351                 this.snapshot.sort(f);
27352             }
27353         }
27354     }
27355 });/*
27356  * Based on:
27357  * Ext JS Library 1.1.1
27358  * Copyright(c) 2006-2007, Ext JS, LLC.
27359  *
27360  * Originally Released Under LGPL - original licence link has changed is not relivant.
27361  *
27362  * Fork - LGPL
27363  * <script type="text/javascript">
27364  */
27365  
27366
27367 /**
27368  * @class Roo.ColorPalette
27369  * @extends Roo.Component
27370  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27371  * Here's an example of typical usage:
27372  * <pre><code>
27373 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27374 cp.render('my-div');
27375
27376 cp.on('select', function(palette, selColor){
27377     // do something with selColor
27378 });
27379 </code></pre>
27380  * @constructor
27381  * Create a new ColorPalette
27382  * @param {Object} config The config object
27383  */
27384 Roo.ColorPalette = function(config){
27385     Roo.ColorPalette.superclass.constructor.call(this, config);
27386     this.addEvents({
27387         /**
27388              * @event select
27389              * Fires when a color is selected
27390              * @param {ColorPalette} this
27391              * @param {String} color The 6-digit color hex code (without the # symbol)
27392              */
27393         select: true
27394     });
27395
27396     if(this.handler){
27397         this.on("select", this.handler, this.scope, true);
27398     }
27399 };
27400 Roo.extend(Roo.ColorPalette, Roo.Component, {
27401     /**
27402      * @cfg {String} itemCls
27403      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27404      */
27405     itemCls : "x-color-palette",
27406     /**
27407      * @cfg {String} value
27408      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27409      * the hex codes are case-sensitive.
27410      */
27411     value : null,
27412     clickEvent:'click',
27413     // private
27414     ctype: "Roo.ColorPalette",
27415
27416     /**
27417      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27418      */
27419     allowReselect : false,
27420
27421     /**
27422      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27423      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27424      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27425      * of colors with the width setting until the box is symmetrical.</p>
27426      * <p>You can override individual colors if needed:</p>
27427      * <pre><code>
27428 var cp = new Roo.ColorPalette();
27429 cp.colors[0] = "FF0000";  // change the first box to red
27430 </code></pre>
27431
27432 Or you can provide a custom array of your own for complete control:
27433 <pre><code>
27434 var cp = new Roo.ColorPalette();
27435 cp.colors = ["000000", "993300", "333300"];
27436 </code></pre>
27437      * @type Array
27438      */
27439     colors : [
27440         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27441         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27442         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27443         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27444         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27445     ],
27446
27447     // private
27448     onRender : function(container, position){
27449         var t = new Roo.MasterTemplate(
27450             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27451         );
27452         var c = this.colors;
27453         for(var i = 0, len = c.length; i < len; i++){
27454             t.add([c[i]]);
27455         }
27456         var el = document.createElement("div");
27457         el.className = this.itemCls;
27458         t.overwrite(el);
27459         container.dom.insertBefore(el, position);
27460         this.el = Roo.get(el);
27461         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27462         if(this.clickEvent != 'click'){
27463             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27464         }
27465     },
27466
27467     // private
27468     afterRender : function(){
27469         Roo.ColorPalette.superclass.afterRender.call(this);
27470         if(this.value){
27471             var s = this.value;
27472             this.value = null;
27473             this.select(s);
27474         }
27475     },
27476
27477     // private
27478     handleClick : function(e, t){
27479         e.preventDefault();
27480         if(!this.disabled){
27481             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27482             this.select(c.toUpperCase());
27483         }
27484     },
27485
27486     /**
27487      * Selects the specified color in the palette (fires the select event)
27488      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27489      */
27490     select : function(color){
27491         color = color.replace("#", "");
27492         if(color != this.value || this.allowReselect){
27493             var el = this.el;
27494             if(this.value){
27495                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27496             }
27497             el.child("a.color-"+color).addClass("x-color-palette-sel");
27498             this.value = color;
27499             this.fireEvent("select", this, color);
27500         }
27501     }
27502 });/*
27503  * Based on:
27504  * Ext JS Library 1.1.1
27505  * Copyright(c) 2006-2007, Ext JS, LLC.
27506  *
27507  * Originally Released Under LGPL - original licence link has changed is not relivant.
27508  *
27509  * Fork - LGPL
27510  * <script type="text/javascript">
27511  */
27512  
27513 /**
27514  * @class Roo.DatePicker
27515  * @extends Roo.Component
27516  * Simple date picker class.
27517  * @constructor
27518  * Create a new DatePicker
27519  * @param {Object} config The config object
27520  */
27521 Roo.DatePicker = function(config){
27522     Roo.DatePicker.superclass.constructor.call(this, config);
27523
27524     this.value = config && config.value ?
27525                  config.value.clearTime() : new Date().clearTime();
27526
27527     this.addEvents({
27528         /**
27529              * @event select
27530              * Fires when a date is selected
27531              * @param {DatePicker} this
27532              * @param {Date} date The selected date
27533              */
27534         'select': true,
27535         /**
27536              * @event monthchange
27537              * Fires when the displayed month changes 
27538              * @param {DatePicker} this
27539              * @param {Date} date The selected month
27540              */
27541         'monthchange': true
27542     });
27543
27544     if(this.handler){
27545         this.on("select", this.handler,  this.scope || this);
27546     }
27547     // build the disabledDatesRE
27548     if(!this.disabledDatesRE && this.disabledDates){
27549         var dd = this.disabledDates;
27550         var re = "(?:";
27551         for(var i = 0; i < dd.length; i++){
27552             re += dd[i];
27553             if(i != dd.length-1) {
27554                 re += "|";
27555             }
27556         }
27557         this.disabledDatesRE = new RegExp(re + ")");
27558     }
27559 };
27560
27561 Roo.extend(Roo.DatePicker, Roo.Component, {
27562     /**
27563      * @cfg {String} todayText
27564      * The text to display on the button that selects the current date (defaults to "Today")
27565      */
27566     todayText : "Today",
27567     /**
27568      * @cfg {String} okText
27569      * The text to display on the ok button
27570      */
27571     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27572     /**
27573      * @cfg {String} cancelText
27574      * The text to display on the cancel button
27575      */
27576     cancelText : "Cancel",
27577     /**
27578      * @cfg {String} todayTip
27579      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27580      */
27581     todayTip : "{0} (Spacebar)",
27582     /**
27583      * @cfg {Date} minDate
27584      * Minimum allowable date (JavaScript date object, defaults to null)
27585      */
27586     minDate : null,
27587     /**
27588      * @cfg {Date} maxDate
27589      * Maximum allowable date (JavaScript date object, defaults to null)
27590      */
27591     maxDate : null,
27592     /**
27593      * @cfg {String} minText
27594      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27595      */
27596     minText : "This date is before the minimum date",
27597     /**
27598      * @cfg {String} maxText
27599      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27600      */
27601     maxText : "This date is after the maximum date",
27602     /**
27603      * @cfg {String} format
27604      * The default date format string which can be overriden for localization support.  The format must be
27605      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27606      */
27607     format : "m/d/y",
27608     /**
27609      * @cfg {Array} disabledDays
27610      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27611      */
27612     disabledDays : null,
27613     /**
27614      * @cfg {String} disabledDaysText
27615      * The tooltip to display when the date falls on a disabled day (defaults to "")
27616      */
27617     disabledDaysText : "",
27618     /**
27619      * @cfg {RegExp} disabledDatesRE
27620      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27621      */
27622     disabledDatesRE : null,
27623     /**
27624      * @cfg {String} disabledDatesText
27625      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27626      */
27627     disabledDatesText : "",
27628     /**
27629      * @cfg {Boolean} constrainToViewport
27630      * True to constrain the date picker to the viewport (defaults to true)
27631      */
27632     constrainToViewport : true,
27633     /**
27634      * @cfg {Array} monthNames
27635      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27636      */
27637     monthNames : Date.monthNames,
27638     /**
27639      * @cfg {Array} dayNames
27640      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27641      */
27642     dayNames : Date.dayNames,
27643     /**
27644      * @cfg {String} nextText
27645      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27646      */
27647     nextText: 'Next Month (Control+Right)',
27648     /**
27649      * @cfg {String} prevText
27650      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27651      */
27652     prevText: 'Previous Month (Control+Left)',
27653     /**
27654      * @cfg {String} monthYearText
27655      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27656      */
27657     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27658     /**
27659      * @cfg {Number} startDay
27660      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27661      */
27662     startDay : 0,
27663     /**
27664      * @cfg {Bool} showClear
27665      * Show a clear button (usefull for date form elements that can be blank.)
27666      */
27667     
27668     showClear: false,
27669     
27670     /**
27671      * Sets the value of the date field
27672      * @param {Date} value The date to set
27673      */
27674     setValue : function(value){
27675         var old = this.value;
27676         
27677         if (typeof(value) == 'string') {
27678          
27679             value = Date.parseDate(value, this.format);
27680         }
27681         if (!value) {
27682             value = new Date();
27683         }
27684         
27685         this.value = value.clearTime(true);
27686         if(this.el){
27687             this.update(this.value);
27688         }
27689     },
27690
27691     /**
27692      * Gets the current selected value of the date field
27693      * @return {Date} The selected date
27694      */
27695     getValue : function(){
27696         return this.value;
27697     },
27698
27699     // private
27700     focus : function(){
27701         if(this.el){
27702             this.update(this.activeDate);
27703         }
27704     },
27705
27706     // privateval
27707     onRender : function(container, position){
27708         
27709         var m = [
27710              '<table cellspacing="0">',
27711                 '<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>',
27712                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27713         var dn = this.dayNames;
27714         for(var i = 0; i < 7; i++){
27715             var d = this.startDay+i;
27716             if(d > 6){
27717                 d = d-7;
27718             }
27719             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27720         }
27721         m[m.length] = "</tr></thead><tbody><tr>";
27722         for(var i = 0; i < 42; i++) {
27723             if(i % 7 == 0 && i != 0){
27724                 m[m.length] = "</tr><tr>";
27725             }
27726             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27727         }
27728         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27729             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27730
27731         var el = document.createElement("div");
27732         el.className = "x-date-picker";
27733         el.innerHTML = m.join("");
27734
27735         container.dom.insertBefore(el, position);
27736
27737         this.el = Roo.get(el);
27738         this.eventEl = Roo.get(el.firstChild);
27739
27740         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27741             handler: this.showPrevMonth,
27742             scope: this,
27743             preventDefault:true,
27744             stopDefault:true
27745         });
27746
27747         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27748             handler: this.showNextMonth,
27749             scope: this,
27750             preventDefault:true,
27751             stopDefault:true
27752         });
27753
27754         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27755
27756         this.monthPicker = this.el.down('div.x-date-mp');
27757         this.monthPicker.enableDisplayMode('block');
27758         
27759         var kn = new Roo.KeyNav(this.eventEl, {
27760             "left" : function(e){
27761                 e.ctrlKey ?
27762                     this.showPrevMonth() :
27763                     this.update(this.activeDate.add("d", -1));
27764             },
27765
27766             "right" : function(e){
27767                 e.ctrlKey ?
27768                     this.showNextMonth() :
27769                     this.update(this.activeDate.add("d", 1));
27770             },
27771
27772             "up" : function(e){
27773                 e.ctrlKey ?
27774                     this.showNextYear() :
27775                     this.update(this.activeDate.add("d", -7));
27776             },
27777
27778             "down" : function(e){
27779                 e.ctrlKey ?
27780                     this.showPrevYear() :
27781                     this.update(this.activeDate.add("d", 7));
27782             },
27783
27784             "pageUp" : function(e){
27785                 this.showNextMonth();
27786             },
27787
27788             "pageDown" : function(e){
27789                 this.showPrevMonth();
27790             },
27791
27792             "enter" : function(e){
27793                 e.stopPropagation();
27794                 return true;
27795             },
27796
27797             scope : this
27798         });
27799
27800         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27801
27802         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27803
27804         this.el.unselectable();
27805         
27806         this.cells = this.el.select("table.x-date-inner tbody td");
27807         this.textNodes = this.el.query("table.x-date-inner tbody span");
27808
27809         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27810             text: "&#160;",
27811             tooltip: this.monthYearText
27812         });
27813
27814         this.mbtn.on('click', this.showMonthPicker, this);
27815         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27816
27817
27818         var today = (new Date()).dateFormat(this.format);
27819         
27820         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27821         if (this.showClear) {
27822             baseTb.add( new Roo.Toolbar.Fill());
27823         }
27824         baseTb.add({
27825             text: String.format(this.todayText, today),
27826             tooltip: String.format(this.todayTip, today),
27827             handler: this.selectToday,
27828             scope: this
27829         });
27830         
27831         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27832             
27833         //});
27834         if (this.showClear) {
27835             
27836             baseTb.add( new Roo.Toolbar.Fill());
27837             baseTb.add({
27838                 text: '&#160;',
27839                 cls: 'x-btn-icon x-btn-clear',
27840                 handler: function() {
27841                     //this.value = '';
27842                     this.fireEvent("select", this, '');
27843                 },
27844                 scope: this
27845             });
27846         }
27847         
27848         
27849         if(Roo.isIE){
27850             this.el.repaint();
27851         }
27852         this.update(this.value);
27853     },
27854
27855     createMonthPicker : function(){
27856         if(!this.monthPicker.dom.firstChild){
27857             var buf = ['<table border="0" cellspacing="0">'];
27858             for(var i = 0; i < 6; i++){
27859                 buf.push(
27860                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27861                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27862                     i == 0 ?
27863                     '<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>' :
27864                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27865                 );
27866             }
27867             buf.push(
27868                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27869                     this.okText,
27870                     '</button><button type="button" class="x-date-mp-cancel">',
27871                     this.cancelText,
27872                     '</button></td></tr>',
27873                 '</table>'
27874             );
27875             this.monthPicker.update(buf.join(''));
27876             this.monthPicker.on('click', this.onMonthClick, this);
27877             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27878
27879             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27880             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27881
27882             this.mpMonths.each(function(m, a, i){
27883                 i += 1;
27884                 if((i%2) == 0){
27885                     m.dom.xmonth = 5 + Math.round(i * .5);
27886                 }else{
27887                     m.dom.xmonth = Math.round((i-1) * .5);
27888                 }
27889             });
27890         }
27891     },
27892
27893     showMonthPicker : function(){
27894         this.createMonthPicker();
27895         var size = this.el.getSize();
27896         this.monthPicker.setSize(size);
27897         this.monthPicker.child('table').setSize(size);
27898
27899         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27900         this.updateMPMonth(this.mpSelMonth);
27901         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27902         this.updateMPYear(this.mpSelYear);
27903
27904         this.monthPicker.slideIn('t', {duration:.2});
27905     },
27906
27907     updateMPYear : function(y){
27908         this.mpyear = y;
27909         var ys = this.mpYears.elements;
27910         for(var i = 1; i <= 10; i++){
27911             var td = ys[i-1], y2;
27912             if((i%2) == 0){
27913                 y2 = y + Math.round(i * .5);
27914                 td.firstChild.innerHTML = y2;
27915                 td.xyear = y2;
27916             }else{
27917                 y2 = y - (5-Math.round(i * .5));
27918                 td.firstChild.innerHTML = y2;
27919                 td.xyear = y2;
27920             }
27921             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27922         }
27923     },
27924
27925     updateMPMonth : function(sm){
27926         this.mpMonths.each(function(m, a, i){
27927             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27928         });
27929     },
27930
27931     selectMPMonth: function(m){
27932         
27933     },
27934
27935     onMonthClick : function(e, t){
27936         e.stopEvent();
27937         var el = new Roo.Element(t), pn;
27938         if(el.is('button.x-date-mp-cancel')){
27939             this.hideMonthPicker();
27940         }
27941         else if(el.is('button.x-date-mp-ok')){
27942             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27943             this.hideMonthPicker();
27944         }
27945         else if(pn = el.up('td.x-date-mp-month', 2)){
27946             this.mpMonths.removeClass('x-date-mp-sel');
27947             pn.addClass('x-date-mp-sel');
27948             this.mpSelMonth = pn.dom.xmonth;
27949         }
27950         else if(pn = el.up('td.x-date-mp-year', 2)){
27951             this.mpYears.removeClass('x-date-mp-sel');
27952             pn.addClass('x-date-mp-sel');
27953             this.mpSelYear = pn.dom.xyear;
27954         }
27955         else if(el.is('a.x-date-mp-prev')){
27956             this.updateMPYear(this.mpyear-10);
27957         }
27958         else if(el.is('a.x-date-mp-next')){
27959             this.updateMPYear(this.mpyear+10);
27960         }
27961     },
27962
27963     onMonthDblClick : function(e, t){
27964         e.stopEvent();
27965         var el = new Roo.Element(t), pn;
27966         if(pn = el.up('td.x-date-mp-month', 2)){
27967             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27968             this.hideMonthPicker();
27969         }
27970         else if(pn = el.up('td.x-date-mp-year', 2)){
27971             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27972             this.hideMonthPicker();
27973         }
27974     },
27975
27976     hideMonthPicker : function(disableAnim){
27977         if(this.monthPicker){
27978             if(disableAnim === true){
27979                 this.monthPicker.hide();
27980             }else{
27981                 this.monthPicker.slideOut('t', {duration:.2});
27982             }
27983         }
27984     },
27985
27986     // private
27987     showPrevMonth : function(e){
27988         this.update(this.activeDate.add("mo", -1));
27989     },
27990
27991     // private
27992     showNextMonth : function(e){
27993         this.update(this.activeDate.add("mo", 1));
27994     },
27995
27996     // private
27997     showPrevYear : function(){
27998         this.update(this.activeDate.add("y", -1));
27999     },
28000
28001     // private
28002     showNextYear : function(){
28003         this.update(this.activeDate.add("y", 1));
28004     },
28005
28006     // private
28007     handleMouseWheel : function(e){
28008         var delta = e.getWheelDelta();
28009         if(delta > 0){
28010             this.showPrevMonth();
28011             e.stopEvent();
28012         } else if(delta < 0){
28013             this.showNextMonth();
28014             e.stopEvent();
28015         }
28016     },
28017
28018     // private
28019     handleDateClick : function(e, t){
28020         e.stopEvent();
28021         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28022             this.setValue(new Date(t.dateValue));
28023             this.fireEvent("select", this, this.value);
28024         }
28025     },
28026
28027     // private
28028     selectToday : function(){
28029         this.setValue(new Date().clearTime());
28030         this.fireEvent("select", this, this.value);
28031     },
28032
28033     // private
28034     update : function(date)
28035     {
28036         var vd = this.activeDate;
28037         this.activeDate = date;
28038         if(vd && this.el){
28039             var t = date.getTime();
28040             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28041                 this.cells.removeClass("x-date-selected");
28042                 this.cells.each(function(c){
28043                    if(c.dom.firstChild.dateValue == t){
28044                        c.addClass("x-date-selected");
28045                        setTimeout(function(){
28046                             try{c.dom.firstChild.focus();}catch(e){}
28047                        }, 50);
28048                        return false;
28049                    }
28050                 });
28051                 return;
28052             }
28053         }
28054         
28055         var days = date.getDaysInMonth();
28056         var firstOfMonth = date.getFirstDateOfMonth();
28057         var startingPos = firstOfMonth.getDay()-this.startDay;
28058
28059         if(startingPos <= this.startDay){
28060             startingPos += 7;
28061         }
28062
28063         var pm = date.add("mo", -1);
28064         var prevStart = pm.getDaysInMonth()-startingPos;
28065
28066         var cells = this.cells.elements;
28067         var textEls = this.textNodes;
28068         days += startingPos;
28069
28070         // convert everything to numbers so it's fast
28071         var day = 86400000;
28072         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28073         var today = new Date().clearTime().getTime();
28074         var sel = date.clearTime().getTime();
28075         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28076         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28077         var ddMatch = this.disabledDatesRE;
28078         var ddText = this.disabledDatesText;
28079         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28080         var ddaysText = this.disabledDaysText;
28081         var format = this.format;
28082
28083         var setCellClass = function(cal, cell){
28084             cell.title = "";
28085             var t = d.getTime();
28086             cell.firstChild.dateValue = t;
28087             if(t == today){
28088                 cell.className += " x-date-today";
28089                 cell.title = cal.todayText;
28090             }
28091             if(t == sel){
28092                 cell.className += " x-date-selected";
28093                 setTimeout(function(){
28094                     try{cell.firstChild.focus();}catch(e){}
28095                 }, 50);
28096             }
28097             // disabling
28098             if(t < min) {
28099                 cell.className = " x-date-disabled";
28100                 cell.title = cal.minText;
28101                 return;
28102             }
28103             if(t > max) {
28104                 cell.className = " x-date-disabled";
28105                 cell.title = cal.maxText;
28106                 return;
28107             }
28108             if(ddays){
28109                 if(ddays.indexOf(d.getDay()) != -1){
28110                     cell.title = ddaysText;
28111                     cell.className = " x-date-disabled";
28112                 }
28113             }
28114             if(ddMatch && format){
28115                 var fvalue = d.dateFormat(format);
28116                 if(ddMatch.test(fvalue)){
28117                     cell.title = ddText.replace("%0", fvalue);
28118                     cell.className = " x-date-disabled";
28119                 }
28120             }
28121         };
28122
28123         var i = 0;
28124         for(; i < startingPos; i++) {
28125             textEls[i].innerHTML = (++prevStart);
28126             d.setDate(d.getDate()+1);
28127             cells[i].className = "x-date-prevday";
28128             setCellClass(this, cells[i]);
28129         }
28130         for(; i < days; i++){
28131             intDay = i - startingPos + 1;
28132             textEls[i].innerHTML = (intDay);
28133             d.setDate(d.getDate()+1);
28134             cells[i].className = "x-date-active";
28135             setCellClass(this, cells[i]);
28136         }
28137         var extraDays = 0;
28138         for(; i < 42; i++) {
28139              textEls[i].innerHTML = (++extraDays);
28140              d.setDate(d.getDate()+1);
28141              cells[i].className = "x-date-nextday";
28142              setCellClass(this, cells[i]);
28143         }
28144
28145         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28146         this.fireEvent('monthchange', this, date);
28147         
28148         if(!this.internalRender){
28149             var main = this.el.dom.firstChild;
28150             var w = main.offsetWidth;
28151             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28152             Roo.fly(main).setWidth(w);
28153             this.internalRender = true;
28154             // opera does not respect the auto grow header center column
28155             // then, after it gets a width opera refuses to recalculate
28156             // without a second pass
28157             if(Roo.isOpera && !this.secondPass){
28158                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28159                 this.secondPass = true;
28160                 this.update.defer(10, this, [date]);
28161             }
28162         }
28163         
28164         
28165     }
28166 });        /*
28167  * Based on:
28168  * Ext JS Library 1.1.1
28169  * Copyright(c) 2006-2007, Ext JS, LLC.
28170  *
28171  * Originally Released Under LGPL - original licence link has changed is not relivant.
28172  *
28173  * Fork - LGPL
28174  * <script type="text/javascript">
28175  */
28176 /**
28177  * @class Roo.TabPanel
28178  * @extends Roo.util.Observable
28179  * A lightweight tab container.
28180  * <br><br>
28181  * Usage:
28182  * <pre><code>
28183 // basic tabs 1, built from existing content
28184 var tabs = new Roo.TabPanel("tabs1");
28185 tabs.addTab("script", "View Script");
28186 tabs.addTab("markup", "View Markup");
28187 tabs.activate("script");
28188
28189 // more advanced tabs, built from javascript
28190 var jtabs = new Roo.TabPanel("jtabs");
28191 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28192
28193 // set up the UpdateManager
28194 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28195 var updater = tab2.getUpdateManager();
28196 updater.setDefaultUrl("ajax1.htm");
28197 tab2.on('activate', updater.refresh, updater, true);
28198
28199 // Use setUrl for Ajax loading
28200 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28201 tab3.setUrl("ajax2.htm", null, true);
28202
28203 // Disabled tab
28204 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28205 tab4.disable();
28206
28207 jtabs.activate("jtabs-1");
28208  * </code></pre>
28209  * @constructor
28210  * Create a new TabPanel.
28211  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28212  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28213  */
28214 Roo.TabPanel = function(container, config){
28215     /**
28216     * The container element for this TabPanel.
28217     * @type Roo.Element
28218     */
28219     this.el = Roo.get(container, true);
28220     if(config){
28221         if(typeof config == "boolean"){
28222             this.tabPosition = config ? "bottom" : "top";
28223         }else{
28224             Roo.apply(this, config);
28225         }
28226     }
28227     if(this.tabPosition == "bottom"){
28228         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28229         this.el.addClass("x-tabs-bottom");
28230     }
28231     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28232     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28233     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28234     if(Roo.isIE){
28235         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28236     }
28237     if(this.tabPosition != "bottom"){
28238         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28239          * @type Roo.Element
28240          */
28241         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28242         this.el.addClass("x-tabs-top");
28243     }
28244     this.items = [];
28245
28246     this.bodyEl.setStyle("position", "relative");
28247
28248     this.active = null;
28249     this.activateDelegate = this.activate.createDelegate(this);
28250
28251     this.addEvents({
28252         /**
28253          * @event tabchange
28254          * Fires when the active tab changes
28255          * @param {Roo.TabPanel} this
28256          * @param {Roo.TabPanelItem} activePanel The new active tab
28257          */
28258         "tabchange": true,
28259         /**
28260          * @event beforetabchange
28261          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28262          * @param {Roo.TabPanel} this
28263          * @param {Object} e Set cancel to true on this object to cancel the tab change
28264          * @param {Roo.TabPanelItem} tab The tab being changed to
28265          */
28266         "beforetabchange" : true
28267     });
28268
28269     Roo.EventManager.onWindowResize(this.onResize, this);
28270     this.cpad = this.el.getPadding("lr");
28271     this.hiddenCount = 0;
28272
28273
28274     // toolbar on the tabbar support...
28275     if (this.toolbar) {
28276         var tcfg = this.toolbar;
28277         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28278         this.toolbar = new Roo.Toolbar(tcfg);
28279         if (Roo.isSafari) {
28280             var tbl = tcfg.container.child('table', true);
28281             tbl.setAttribute('width', '100%');
28282         }
28283         
28284     }
28285    
28286
28287
28288     Roo.TabPanel.superclass.constructor.call(this);
28289 };
28290
28291 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28292     /*
28293      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28294      */
28295     tabPosition : "top",
28296     /*
28297      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28298      */
28299     currentTabWidth : 0,
28300     /*
28301      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28302      */
28303     minTabWidth : 40,
28304     /*
28305      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28306      */
28307     maxTabWidth : 250,
28308     /*
28309      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28310      */
28311     preferredTabWidth : 175,
28312     /*
28313      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28314      */
28315     resizeTabs : false,
28316     /*
28317      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28318      */
28319     monitorResize : true,
28320     /*
28321      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28322      */
28323     toolbar : false,
28324
28325     /**
28326      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28327      * @param {String} id The id of the div to use <b>or create</b>
28328      * @param {String} text The text for the tab
28329      * @param {String} content (optional) Content to put in the TabPanelItem body
28330      * @param {Boolean} closable (optional) True to create a close icon on the tab
28331      * @return {Roo.TabPanelItem} The created TabPanelItem
28332      */
28333     addTab : function(id, text, content, closable){
28334         var item = new Roo.TabPanelItem(this, id, text, closable);
28335         this.addTabItem(item);
28336         if(content){
28337             item.setContent(content);
28338         }
28339         return item;
28340     },
28341
28342     /**
28343      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28344      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28345      * @return {Roo.TabPanelItem}
28346      */
28347     getTab : function(id){
28348         return this.items[id];
28349     },
28350
28351     /**
28352      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28353      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28354      */
28355     hideTab : function(id){
28356         var t = this.items[id];
28357         if(!t.isHidden()){
28358            t.setHidden(true);
28359            this.hiddenCount++;
28360            this.autoSizeTabs();
28361         }
28362     },
28363
28364     /**
28365      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28366      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28367      */
28368     unhideTab : function(id){
28369         var t = this.items[id];
28370         if(t.isHidden()){
28371            t.setHidden(false);
28372            this.hiddenCount--;
28373            this.autoSizeTabs();
28374         }
28375     },
28376
28377     /**
28378      * Adds an existing {@link Roo.TabPanelItem}.
28379      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28380      */
28381     addTabItem : function(item){
28382         this.items[item.id] = item;
28383         this.items.push(item);
28384         if(this.resizeTabs){
28385            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28386            this.autoSizeTabs();
28387         }else{
28388             item.autoSize();
28389         }
28390     },
28391
28392     /**
28393      * Removes a {@link Roo.TabPanelItem}.
28394      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28395      */
28396     removeTab : function(id){
28397         var items = this.items;
28398         var tab = items[id];
28399         if(!tab) { return; }
28400         var index = items.indexOf(tab);
28401         if(this.active == tab && items.length > 1){
28402             var newTab = this.getNextAvailable(index);
28403             if(newTab) {
28404                 newTab.activate();
28405             }
28406         }
28407         this.stripEl.dom.removeChild(tab.pnode.dom);
28408         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28409             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28410         }
28411         items.splice(index, 1);
28412         delete this.items[tab.id];
28413         tab.fireEvent("close", tab);
28414         tab.purgeListeners();
28415         this.autoSizeTabs();
28416     },
28417
28418     getNextAvailable : function(start){
28419         var items = this.items;
28420         var index = start;
28421         // look for a next tab that will slide over to
28422         // replace the one being removed
28423         while(index < items.length){
28424             var item = items[++index];
28425             if(item && !item.isHidden()){
28426                 return item;
28427             }
28428         }
28429         // if one isn't found select the previous tab (on the left)
28430         index = start;
28431         while(index >= 0){
28432             var item = items[--index];
28433             if(item && !item.isHidden()){
28434                 return item;
28435             }
28436         }
28437         return null;
28438     },
28439
28440     /**
28441      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28442      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28443      */
28444     disableTab : function(id){
28445         var tab = this.items[id];
28446         if(tab && this.active != tab){
28447             tab.disable();
28448         }
28449     },
28450
28451     /**
28452      * Enables a {@link Roo.TabPanelItem} that is disabled.
28453      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28454      */
28455     enableTab : function(id){
28456         var tab = this.items[id];
28457         tab.enable();
28458     },
28459
28460     /**
28461      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28462      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28463      * @return {Roo.TabPanelItem} The TabPanelItem.
28464      */
28465     activate : function(id){
28466         var tab = this.items[id];
28467         if(!tab){
28468             return null;
28469         }
28470         if(tab == this.active || tab.disabled){
28471             return tab;
28472         }
28473         var e = {};
28474         this.fireEvent("beforetabchange", this, e, tab);
28475         if(e.cancel !== true && !tab.disabled){
28476             if(this.active){
28477                 this.active.hide();
28478             }
28479             this.active = this.items[id];
28480             this.active.show();
28481             this.fireEvent("tabchange", this, this.active);
28482         }
28483         return tab;
28484     },
28485
28486     /**
28487      * Gets the active {@link Roo.TabPanelItem}.
28488      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28489      */
28490     getActiveTab : function(){
28491         return this.active;
28492     },
28493
28494     /**
28495      * Updates the tab body element to fit the height of the container element
28496      * for overflow scrolling
28497      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28498      */
28499     syncHeight : function(targetHeight){
28500         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28501         var bm = this.bodyEl.getMargins();
28502         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28503         this.bodyEl.setHeight(newHeight);
28504         return newHeight;
28505     },
28506
28507     onResize : function(){
28508         if(this.monitorResize){
28509             this.autoSizeTabs();
28510         }
28511     },
28512
28513     /**
28514      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28515      */
28516     beginUpdate : function(){
28517         this.updating = true;
28518     },
28519
28520     /**
28521      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28522      */
28523     endUpdate : function(){
28524         this.updating = false;
28525         this.autoSizeTabs();
28526     },
28527
28528     /**
28529      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28530      */
28531     autoSizeTabs : function(){
28532         var count = this.items.length;
28533         var vcount = count - this.hiddenCount;
28534         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28535             return;
28536         }
28537         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28538         var availWidth = Math.floor(w / vcount);
28539         var b = this.stripBody;
28540         if(b.getWidth() > w){
28541             var tabs = this.items;
28542             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28543             if(availWidth < this.minTabWidth){
28544                 /*if(!this.sleft){    // incomplete scrolling code
28545                     this.createScrollButtons();
28546                 }
28547                 this.showScroll();
28548                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28549             }
28550         }else{
28551             if(this.currentTabWidth < this.preferredTabWidth){
28552                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28553             }
28554         }
28555     },
28556
28557     /**
28558      * Returns the number of tabs in this TabPanel.
28559      * @return {Number}
28560      */
28561      getCount : function(){
28562          return this.items.length;
28563      },
28564
28565     /**
28566      * Resizes all the tabs to the passed width
28567      * @param {Number} The new width
28568      */
28569     setTabWidth : function(width){
28570         this.currentTabWidth = width;
28571         for(var i = 0, len = this.items.length; i < len; i++) {
28572                 if(!this.items[i].isHidden()) {
28573                 this.items[i].setWidth(width);
28574             }
28575         }
28576     },
28577
28578     /**
28579      * Destroys this TabPanel
28580      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28581      */
28582     destroy : function(removeEl){
28583         Roo.EventManager.removeResizeListener(this.onResize, this);
28584         for(var i = 0, len = this.items.length; i < len; i++){
28585             this.items[i].purgeListeners();
28586         }
28587         if(removeEl === true){
28588             this.el.update("");
28589             this.el.remove();
28590         }
28591     }
28592 });
28593
28594 /**
28595  * @class Roo.TabPanelItem
28596  * @extends Roo.util.Observable
28597  * Represents an individual item (tab plus body) in a TabPanel.
28598  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28599  * @param {String} id The id of this TabPanelItem
28600  * @param {String} text The text for the tab of this TabPanelItem
28601  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28602  */
28603 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28604     /**
28605      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28606      * @type Roo.TabPanel
28607      */
28608     this.tabPanel = tabPanel;
28609     /**
28610      * The id for this TabPanelItem
28611      * @type String
28612      */
28613     this.id = id;
28614     /** @private */
28615     this.disabled = false;
28616     /** @private */
28617     this.text = text;
28618     /** @private */
28619     this.loaded = false;
28620     this.closable = closable;
28621
28622     /**
28623      * The body element for this TabPanelItem.
28624      * @type Roo.Element
28625      */
28626     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28627     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28628     this.bodyEl.setStyle("display", "block");
28629     this.bodyEl.setStyle("zoom", "1");
28630     this.hideAction();
28631
28632     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28633     /** @private */
28634     this.el = Roo.get(els.el, true);
28635     this.inner = Roo.get(els.inner, true);
28636     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28637     this.pnode = Roo.get(els.el.parentNode, true);
28638     this.el.on("mousedown", this.onTabMouseDown, this);
28639     this.el.on("click", this.onTabClick, this);
28640     /** @private */
28641     if(closable){
28642         var c = Roo.get(els.close, true);
28643         c.dom.title = this.closeText;
28644         c.addClassOnOver("close-over");
28645         c.on("click", this.closeClick, this);
28646      }
28647
28648     this.addEvents({
28649          /**
28650          * @event activate
28651          * Fires when this tab becomes the active tab.
28652          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28653          * @param {Roo.TabPanelItem} this
28654          */
28655         "activate": true,
28656         /**
28657          * @event beforeclose
28658          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28659          * @param {Roo.TabPanelItem} this
28660          * @param {Object} e Set cancel to true on this object to cancel the close.
28661          */
28662         "beforeclose": true,
28663         /**
28664          * @event close
28665          * Fires when this tab is closed.
28666          * @param {Roo.TabPanelItem} this
28667          */
28668          "close": true,
28669         /**
28670          * @event deactivate
28671          * Fires when this tab is no longer the active tab.
28672          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28673          * @param {Roo.TabPanelItem} this
28674          */
28675          "deactivate" : true
28676     });
28677     this.hidden = false;
28678
28679     Roo.TabPanelItem.superclass.constructor.call(this);
28680 };
28681
28682 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28683     purgeListeners : function(){
28684        Roo.util.Observable.prototype.purgeListeners.call(this);
28685        this.el.removeAllListeners();
28686     },
28687     /**
28688      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28689      */
28690     show : function(){
28691         this.pnode.addClass("on");
28692         this.showAction();
28693         if(Roo.isOpera){
28694             this.tabPanel.stripWrap.repaint();
28695         }
28696         this.fireEvent("activate", this.tabPanel, this);
28697     },
28698
28699     /**
28700      * Returns true if this tab is the active tab.
28701      * @return {Boolean}
28702      */
28703     isActive : function(){
28704         return this.tabPanel.getActiveTab() == this;
28705     },
28706
28707     /**
28708      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28709      */
28710     hide : function(){
28711         this.pnode.removeClass("on");
28712         this.hideAction();
28713         this.fireEvent("deactivate", this.tabPanel, this);
28714     },
28715
28716     hideAction : function(){
28717         this.bodyEl.hide();
28718         this.bodyEl.setStyle("position", "absolute");
28719         this.bodyEl.setLeft("-20000px");
28720         this.bodyEl.setTop("-20000px");
28721     },
28722
28723     showAction : function(){
28724         this.bodyEl.setStyle("position", "relative");
28725         this.bodyEl.setTop("");
28726         this.bodyEl.setLeft("");
28727         this.bodyEl.show();
28728     },
28729
28730     /**
28731      * Set the tooltip for the tab.
28732      * @param {String} tooltip The tab's tooltip
28733      */
28734     setTooltip : function(text){
28735         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28736             this.textEl.dom.qtip = text;
28737             this.textEl.dom.removeAttribute('title');
28738         }else{
28739             this.textEl.dom.title = text;
28740         }
28741     },
28742
28743     onTabClick : function(e){
28744         e.preventDefault();
28745         this.tabPanel.activate(this.id);
28746     },
28747
28748     onTabMouseDown : function(e){
28749         e.preventDefault();
28750         this.tabPanel.activate(this.id);
28751     },
28752
28753     getWidth : function(){
28754         return this.inner.getWidth();
28755     },
28756
28757     setWidth : function(width){
28758         var iwidth = width - this.pnode.getPadding("lr");
28759         this.inner.setWidth(iwidth);
28760         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28761         this.pnode.setWidth(width);
28762     },
28763
28764     /**
28765      * Show or hide the tab
28766      * @param {Boolean} hidden True to hide or false to show.
28767      */
28768     setHidden : function(hidden){
28769         this.hidden = hidden;
28770         this.pnode.setStyle("display", hidden ? "none" : "");
28771     },
28772
28773     /**
28774      * Returns true if this tab is "hidden"
28775      * @return {Boolean}
28776      */
28777     isHidden : function(){
28778         return this.hidden;
28779     },
28780
28781     /**
28782      * Returns the text for this tab
28783      * @return {String}
28784      */
28785     getText : function(){
28786         return this.text;
28787     },
28788
28789     autoSize : function(){
28790         //this.el.beginMeasure();
28791         this.textEl.setWidth(1);
28792         /*
28793          *  #2804 [new] Tabs in Roojs
28794          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28795          */
28796         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28797         //this.el.endMeasure();
28798     },
28799
28800     /**
28801      * Sets the text for the tab (Note: this also sets the tooltip text)
28802      * @param {String} text The tab's text and tooltip
28803      */
28804     setText : function(text){
28805         this.text = text;
28806         this.textEl.update(text);
28807         this.setTooltip(text);
28808         if(!this.tabPanel.resizeTabs){
28809             this.autoSize();
28810         }
28811     },
28812     /**
28813      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28814      */
28815     activate : function(){
28816         this.tabPanel.activate(this.id);
28817     },
28818
28819     /**
28820      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28821      */
28822     disable : function(){
28823         if(this.tabPanel.active != this){
28824             this.disabled = true;
28825             this.pnode.addClass("disabled");
28826         }
28827     },
28828
28829     /**
28830      * Enables this TabPanelItem if it was previously disabled.
28831      */
28832     enable : function(){
28833         this.disabled = false;
28834         this.pnode.removeClass("disabled");
28835     },
28836
28837     /**
28838      * Sets the content for this TabPanelItem.
28839      * @param {String} content The content
28840      * @param {Boolean} loadScripts true to look for and load scripts
28841      */
28842     setContent : function(content, loadScripts){
28843         this.bodyEl.update(content, loadScripts);
28844     },
28845
28846     /**
28847      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28848      * @return {Roo.UpdateManager} The UpdateManager
28849      */
28850     getUpdateManager : function(){
28851         return this.bodyEl.getUpdateManager();
28852     },
28853
28854     /**
28855      * Set a URL to be used to load the content for this TabPanelItem.
28856      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28857      * @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)
28858      * @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)
28859      * @return {Roo.UpdateManager} The UpdateManager
28860      */
28861     setUrl : function(url, params, loadOnce){
28862         if(this.refreshDelegate){
28863             this.un('activate', this.refreshDelegate);
28864         }
28865         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28866         this.on("activate", this.refreshDelegate);
28867         return this.bodyEl.getUpdateManager();
28868     },
28869
28870     /** @private */
28871     _handleRefresh : function(url, params, loadOnce){
28872         if(!loadOnce || !this.loaded){
28873             var updater = this.bodyEl.getUpdateManager();
28874             updater.update(url, params, this._setLoaded.createDelegate(this));
28875         }
28876     },
28877
28878     /**
28879      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28880      *   Will fail silently if the setUrl method has not been called.
28881      *   This does not activate the panel, just updates its content.
28882      */
28883     refresh : function(){
28884         if(this.refreshDelegate){
28885            this.loaded = false;
28886            this.refreshDelegate();
28887         }
28888     },
28889
28890     /** @private */
28891     _setLoaded : function(){
28892         this.loaded = true;
28893     },
28894
28895     /** @private */
28896     closeClick : function(e){
28897         var o = {};
28898         e.stopEvent();
28899         this.fireEvent("beforeclose", this, o);
28900         if(o.cancel !== true){
28901             this.tabPanel.removeTab(this.id);
28902         }
28903     },
28904     /**
28905      * The text displayed in the tooltip for the close icon.
28906      * @type String
28907      */
28908     closeText : "Close this tab"
28909 });
28910
28911 /** @private */
28912 Roo.TabPanel.prototype.createStrip = function(container){
28913     var strip = document.createElement("div");
28914     strip.className = "x-tabs-wrap";
28915     container.appendChild(strip);
28916     return strip;
28917 };
28918 /** @private */
28919 Roo.TabPanel.prototype.createStripList = function(strip){
28920     // div wrapper for retard IE
28921     // returns the "tr" element.
28922     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28923         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28924         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28925     return strip.firstChild.firstChild.firstChild.firstChild;
28926 };
28927 /** @private */
28928 Roo.TabPanel.prototype.createBody = function(container){
28929     var body = document.createElement("div");
28930     Roo.id(body, "tab-body");
28931     Roo.fly(body).addClass("x-tabs-body");
28932     container.appendChild(body);
28933     return body;
28934 };
28935 /** @private */
28936 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28937     var body = Roo.getDom(id);
28938     if(!body){
28939         body = document.createElement("div");
28940         body.id = id;
28941     }
28942     Roo.fly(body).addClass("x-tabs-item-body");
28943     bodyEl.insertBefore(body, bodyEl.firstChild);
28944     return body;
28945 };
28946 /** @private */
28947 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28948     var td = document.createElement("td");
28949     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28950     //stripEl.appendChild(td);
28951     if(closable){
28952         td.className = "x-tabs-closable";
28953         if(!this.closeTpl){
28954             this.closeTpl = 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>' +
28957                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28958             );
28959         }
28960         var el = this.closeTpl.overwrite(td, {"text": text});
28961         var close = el.getElementsByTagName("div")[0];
28962         var inner = el.getElementsByTagName("em")[0];
28963         return {"el": el, "close": close, "inner": inner};
28964     } else {
28965         if(!this.tabTpl){
28966             this.tabTpl = new Roo.Template(
28967                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28968                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28969             );
28970         }
28971         var el = this.tabTpl.overwrite(td, {"text": text});
28972         var inner = el.getElementsByTagName("em")[0];
28973         return {"el": el, "inner": inner};
28974     }
28975 };/*
28976  * Based on:
28977  * Ext JS Library 1.1.1
28978  * Copyright(c) 2006-2007, Ext JS, LLC.
28979  *
28980  * Originally Released Under LGPL - original licence link has changed is not relivant.
28981  *
28982  * Fork - LGPL
28983  * <script type="text/javascript">
28984  */
28985
28986 /**
28987  * @class Roo.Button
28988  * @extends Roo.util.Observable
28989  * Simple Button class
28990  * @cfg {String} text The button text
28991  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28992  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28993  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28994  * @cfg {Object} scope The scope of the handler
28995  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28996  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28997  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28998  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28999  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29000  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29001    applies if enableToggle = true)
29002  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29003  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29004   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29005  * @constructor
29006  * Create a new button
29007  * @param {Object} config The config object
29008  */
29009 Roo.Button = function(renderTo, config)
29010 {
29011     if (!config) {
29012         config = renderTo;
29013         renderTo = config.renderTo || false;
29014     }
29015     
29016     Roo.apply(this, config);
29017     this.addEvents({
29018         /**
29019              * @event click
29020              * Fires when this button is clicked
29021              * @param {Button} this
29022              * @param {EventObject} e The click event
29023              */
29024             "click" : true,
29025         /**
29026              * @event toggle
29027              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29028              * @param {Button} this
29029              * @param {Boolean} pressed
29030              */
29031             "toggle" : true,
29032         /**
29033              * @event mouseover
29034              * Fires when the mouse hovers over the button
29035              * @param {Button} this
29036              * @param {Event} e The event object
29037              */
29038         'mouseover' : true,
29039         /**
29040              * @event mouseout
29041              * Fires when the mouse exits the button
29042              * @param {Button} this
29043              * @param {Event} e The event object
29044              */
29045         'mouseout': true,
29046          /**
29047              * @event render
29048              * Fires when the button is rendered
29049              * @param {Button} this
29050              */
29051         'render': true
29052     });
29053     if(this.menu){
29054         this.menu = Roo.menu.MenuMgr.get(this.menu);
29055     }
29056     // register listeners first!!  - so render can be captured..
29057     Roo.util.Observable.call(this);
29058     if(renderTo){
29059         this.render(renderTo);
29060     }
29061     
29062   
29063 };
29064
29065 Roo.extend(Roo.Button, Roo.util.Observable, {
29066     /**
29067      * 
29068      */
29069     
29070     /**
29071      * Read-only. True if this button is hidden
29072      * @type Boolean
29073      */
29074     hidden : false,
29075     /**
29076      * Read-only. True if this button is disabled
29077      * @type Boolean
29078      */
29079     disabled : false,
29080     /**
29081      * Read-only. True if this button is pressed (only if enableToggle = true)
29082      * @type Boolean
29083      */
29084     pressed : false,
29085
29086     /**
29087      * @cfg {Number} tabIndex 
29088      * The DOM tabIndex for this button (defaults to undefined)
29089      */
29090     tabIndex : undefined,
29091
29092     /**
29093      * @cfg {Boolean} enableToggle
29094      * True to enable pressed/not pressed toggling (defaults to false)
29095      */
29096     enableToggle: false,
29097     /**
29098      * @cfg {Mixed} menu
29099      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29100      */
29101     menu : undefined,
29102     /**
29103      * @cfg {String} menuAlign
29104      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29105      */
29106     menuAlign : "tl-bl?",
29107
29108     /**
29109      * @cfg {String} iconCls
29110      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29111      */
29112     iconCls : undefined,
29113     /**
29114      * @cfg {String} type
29115      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29116      */
29117     type : 'button',
29118
29119     // private
29120     menuClassTarget: 'tr',
29121
29122     /**
29123      * @cfg {String} clickEvent
29124      * The type of event to map to the button's event handler (defaults to 'click')
29125      */
29126     clickEvent : 'click',
29127
29128     /**
29129      * @cfg {Boolean} handleMouseEvents
29130      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29131      */
29132     handleMouseEvents : true,
29133
29134     /**
29135      * @cfg {String} tooltipType
29136      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29137      */
29138     tooltipType : 'qtip',
29139
29140     /**
29141      * @cfg {String} cls
29142      * A CSS class to apply to the button's main element.
29143      */
29144     
29145     /**
29146      * @cfg {Roo.Template} template (Optional)
29147      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29148      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29149      * require code modifications if required elements (e.g. a button) aren't present.
29150      */
29151
29152     // private
29153     render : function(renderTo){
29154         var btn;
29155         if(this.hideParent){
29156             this.parentEl = Roo.get(renderTo);
29157         }
29158         if(!this.dhconfig){
29159             if(!this.template){
29160                 if(!Roo.Button.buttonTemplate){
29161                     // hideous table template
29162                     Roo.Button.buttonTemplate = new Roo.Template(
29163                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29164                         '<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>',
29165                         "</tr></tbody></table>");
29166                 }
29167                 this.template = Roo.Button.buttonTemplate;
29168             }
29169             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29170             var btnEl = btn.child("button:first");
29171             btnEl.on('focus', this.onFocus, this);
29172             btnEl.on('blur', this.onBlur, this);
29173             if(this.cls){
29174                 btn.addClass(this.cls);
29175             }
29176             if(this.icon){
29177                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29178             }
29179             if(this.iconCls){
29180                 btnEl.addClass(this.iconCls);
29181                 if(!this.cls){
29182                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29183                 }
29184             }
29185             if(this.tabIndex !== undefined){
29186                 btnEl.dom.tabIndex = this.tabIndex;
29187             }
29188             if(this.tooltip){
29189                 if(typeof this.tooltip == 'object'){
29190                     Roo.QuickTips.tips(Roo.apply({
29191                           target: btnEl.id
29192                     }, this.tooltip));
29193                 } else {
29194                     btnEl.dom[this.tooltipType] = this.tooltip;
29195                 }
29196             }
29197         }else{
29198             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29199         }
29200         this.el = btn;
29201         if(this.id){
29202             this.el.dom.id = this.el.id = this.id;
29203         }
29204         if(this.menu){
29205             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29206             this.menu.on("show", this.onMenuShow, this);
29207             this.menu.on("hide", this.onMenuHide, this);
29208         }
29209         btn.addClass("x-btn");
29210         if(Roo.isIE && !Roo.isIE7){
29211             this.autoWidth.defer(1, this);
29212         }else{
29213             this.autoWidth();
29214         }
29215         if(this.handleMouseEvents){
29216             btn.on("mouseover", this.onMouseOver, this);
29217             btn.on("mouseout", this.onMouseOut, this);
29218             btn.on("mousedown", this.onMouseDown, this);
29219         }
29220         btn.on(this.clickEvent, this.onClick, this);
29221         //btn.on("mouseup", this.onMouseUp, this);
29222         if(this.hidden){
29223             this.hide();
29224         }
29225         if(this.disabled){
29226             this.disable();
29227         }
29228         Roo.ButtonToggleMgr.register(this);
29229         if(this.pressed){
29230             this.el.addClass("x-btn-pressed");
29231         }
29232         if(this.repeat){
29233             var repeater = new Roo.util.ClickRepeater(btn,
29234                 typeof this.repeat == "object" ? this.repeat : {}
29235             );
29236             repeater.on("click", this.onClick,  this);
29237         }
29238         
29239         this.fireEvent('render', this);
29240         
29241     },
29242     /**
29243      * Returns the button's underlying element
29244      * @return {Roo.Element} The element
29245      */
29246     getEl : function(){
29247         return this.el;  
29248     },
29249     
29250     /**
29251      * Destroys this Button and removes any listeners.
29252      */
29253     destroy : function(){
29254         Roo.ButtonToggleMgr.unregister(this);
29255         this.el.removeAllListeners();
29256         this.purgeListeners();
29257         this.el.remove();
29258     },
29259
29260     // private
29261     autoWidth : function(){
29262         if(this.el){
29263             this.el.setWidth("auto");
29264             if(Roo.isIE7 && Roo.isStrict){
29265                 var ib = this.el.child('button');
29266                 if(ib && ib.getWidth() > 20){
29267                     ib.clip();
29268                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29269                 }
29270             }
29271             if(this.minWidth){
29272                 if(this.hidden){
29273                     this.el.beginMeasure();
29274                 }
29275                 if(this.el.getWidth() < this.minWidth){
29276                     this.el.setWidth(this.minWidth);
29277                 }
29278                 if(this.hidden){
29279                     this.el.endMeasure();
29280                 }
29281             }
29282         }
29283     },
29284
29285     /**
29286      * Assigns this button's click handler
29287      * @param {Function} handler The function to call when the button is clicked
29288      * @param {Object} scope (optional) Scope for the function passed in
29289      */
29290     setHandler : function(handler, scope){
29291         this.handler = handler;
29292         this.scope = scope;  
29293     },
29294     
29295     /**
29296      * Sets this button's text
29297      * @param {String} text The button text
29298      */
29299     setText : function(text){
29300         this.text = text;
29301         if(this.el){
29302             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29303         }
29304         this.autoWidth();
29305     },
29306     
29307     /**
29308      * Gets the text for this button
29309      * @return {String} The button text
29310      */
29311     getText : function(){
29312         return this.text;  
29313     },
29314     
29315     /**
29316      * Show this button
29317      */
29318     show: function(){
29319         this.hidden = false;
29320         if(this.el){
29321             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29322         }
29323     },
29324     
29325     /**
29326      * Hide this button
29327      */
29328     hide: function(){
29329         this.hidden = true;
29330         if(this.el){
29331             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29332         }
29333     },
29334     
29335     /**
29336      * Convenience function for boolean show/hide
29337      * @param {Boolean} visible True to show, false to hide
29338      */
29339     setVisible: function(visible){
29340         if(visible) {
29341             this.show();
29342         }else{
29343             this.hide();
29344         }
29345     },
29346     
29347     /**
29348      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29349      * @param {Boolean} state (optional) Force a particular state
29350      */
29351     toggle : function(state){
29352         state = state === undefined ? !this.pressed : state;
29353         if(state != this.pressed){
29354             if(state){
29355                 this.el.addClass("x-btn-pressed");
29356                 this.pressed = true;
29357                 this.fireEvent("toggle", this, true);
29358             }else{
29359                 this.el.removeClass("x-btn-pressed");
29360                 this.pressed = false;
29361                 this.fireEvent("toggle", this, false);
29362             }
29363             if(this.toggleHandler){
29364                 this.toggleHandler.call(this.scope || this, this, state);
29365             }
29366         }
29367     },
29368     
29369     /**
29370      * Focus the button
29371      */
29372     focus : function(){
29373         this.el.child('button:first').focus();
29374     },
29375     
29376     /**
29377      * Disable this button
29378      */
29379     disable : function(){
29380         if(this.el){
29381             this.el.addClass("x-btn-disabled");
29382         }
29383         this.disabled = true;
29384     },
29385     
29386     /**
29387      * Enable this button
29388      */
29389     enable : function(){
29390         if(this.el){
29391             this.el.removeClass("x-btn-disabled");
29392         }
29393         this.disabled = false;
29394     },
29395
29396     /**
29397      * Convenience function for boolean enable/disable
29398      * @param {Boolean} enabled True to enable, false to disable
29399      */
29400     setDisabled : function(v){
29401         this[v !== true ? "enable" : "disable"]();
29402     },
29403
29404     // private
29405     onClick : function(e)
29406     {
29407         if(e){
29408             e.preventDefault();
29409         }
29410         if(e.button != 0){
29411             return;
29412         }
29413         if(!this.disabled){
29414             if(this.enableToggle){
29415                 this.toggle();
29416             }
29417             if(this.menu && !this.menu.isVisible()){
29418                 this.menu.show(this.el, this.menuAlign);
29419             }
29420             this.fireEvent("click", this, e);
29421             if(this.handler){
29422                 this.el.removeClass("x-btn-over");
29423                 this.handler.call(this.scope || this, this, e);
29424             }
29425         }
29426     },
29427     // private
29428     onMouseOver : function(e){
29429         if(!this.disabled){
29430             this.el.addClass("x-btn-over");
29431             this.fireEvent('mouseover', this, e);
29432         }
29433     },
29434     // private
29435     onMouseOut : function(e){
29436         if(!e.within(this.el,  true)){
29437             this.el.removeClass("x-btn-over");
29438             this.fireEvent('mouseout', this, e);
29439         }
29440     },
29441     // private
29442     onFocus : function(e){
29443         if(!this.disabled){
29444             this.el.addClass("x-btn-focus");
29445         }
29446     },
29447     // private
29448     onBlur : function(e){
29449         this.el.removeClass("x-btn-focus");
29450     },
29451     // private
29452     onMouseDown : function(e){
29453         if(!this.disabled && e.button == 0){
29454             this.el.addClass("x-btn-click");
29455             Roo.get(document).on('mouseup', this.onMouseUp, this);
29456         }
29457     },
29458     // private
29459     onMouseUp : function(e){
29460         if(e.button == 0){
29461             this.el.removeClass("x-btn-click");
29462             Roo.get(document).un('mouseup', this.onMouseUp, this);
29463         }
29464     },
29465     // private
29466     onMenuShow : function(e){
29467         this.el.addClass("x-btn-menu-active");
29468     },
29469     // private
29470     onMenuHide : function(e){
29471         this.el.removeClass("x-btn-menu-active");
29472     }   
29473 });
29474
29475 // Private utility class used by Button
29476 Roo.ButtonToggleMgr = function(){
29477    var groups = {};
29478    
29479    function toggleGroup(btn, state){
29480        if(state){
29481            var g = groups[btn.toggleGroup];
29482            for(var i = 0, l = g.length; i < l; i++){
29483                if(g[i] != btn){
29484                    g[i].toggle(false);
29485                }
29486            }
29487        }
29488    }
29489    
29490    return {
29491        register : function(btn){
29492            if(!btn.toggleGroup){
29493                return;
29494            }
29495            var g = groups[btn.toggleGroup];
29496            if(!g){
29497                g = groups[btn.toggleGroup] = [];
29498            }
29499            g.push(btn);
29500            btn.on("toggle", toggleGroup);
29501        },
29502        
29503        unregister : function(btn){
29504            if(!btn.toggleGroup){
29505                return;
29506            }
29507            var g = groups[btn.toggleGroup];
29508            if(g){
29509                g.remove(btn);
29510                btn.un("toggle", toggleGroup);
29511            }
29512        }
29513    };
29514 }();/*
29515  * Based on:
29516  * Ext JS Library 1.1.1
29517  * Copyright(c) 2006-2007, Ext JS, LLC.
29518  *
29519  * Originally Released Under LGPL - original licence link has changed is not relivant.
29520  *
29521  * Fork - LGPL
29522  * <script type="text/javascript">
29523  */
29524  
29525 /**
29526  * @class Roo.SplitButton
29527  * @extends Roo.Button
29528  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29529  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29530  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29531  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29532  * @cfg {String} arrowTooltip The title attribute of the arrow
29533  * @constructor
29534  * Create a new menu button
29535  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29536  * @param {Object} config The config object
29537  */
29538 Roo.SplitButton = function(renderTo, config){
29539     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29540     /**
29541      * @event arrowclick
29542      * Fires when this button's arrow is clicked
29543      * @param {SplitButton} this
29544      * @param {EventObject} e The click event
29545      */
29546     this.addEvents({"arrowclick":true});
29547 };
29548
29549 Roo.extend(Roo.SplitButton, Roo.Button, {
29550     render : function(renderTo){
29551         // this is one sweet looking template!
29552         var tpl = new Roo.Template(
29553             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29554             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29555             '<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>',
29556             "</tbody></table></td><td>",
29557             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29558             '<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>',
29559             "</tbody></table></td></tr></table>"
29560         );
29561         var btn = tpl.append(renderTo, [this.text, this.type], true);
29562         var btnEl = btn.child("button");
29563         if(this.cls){
29564             btn.addClass(this.cls);
29565         }
29566         if(this.icon){
29567             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29568         }
29569         if(this.iconCls){
29570             btnEl.addClass(this.iconCls);
29571             if(!this.cls){
29572                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29573             }
29574         }
29575         this.el = btn;
29576         if(this.handleMouseEvents){
29577             btn.on("mouseover", this.onMouseOver, this);
29578             btn.on("mouseout", this.onMouseOut, this);
29579             btn.on("mousedown", this.onMouseDown, this);
29580             btn.on("mouseup", this.onMouseUp, this);
29581         }
29582         btn.on(this.clickEvent, this.onClick, this);
29583         if(this.tooltip){
29584             if(typeof this.tooltip == 'object'){
29585                 Roo.QuickTips.tips(Roo.apply({
29586                       target: btnEl.id
29587                 }, this.tooltip));
29588             } else {
29589                 btnEl.dom[this.tooltipType] = this.tooltip;
29590             }
29591         }
29592         if(this.arrowTooltip){
29593             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29594         }
29595         if(this.hidden){
29596             this.hide();
29597         }
29598         if(this.disabled){
29599             this.disable();
29600         }
29601         if(this.pressed){
29602             this.el.addClass("x-btn-pressed");
29603         }
29604         if(Roo.isIE && !Roo.isIE7){
29605             this.autoWidth.defer(1, this);
29606         }else{
29607             this.autoWidth();
29608         }
29609         if(this.menu){
29610             this.menu.on("show", this.onMenuShow, this);
29611             this.menu.on("hide", this.onMenuHide, this);
29612         }
29613         this.fireEvent('render', this);
29614     },
29615
29616     // private
29617     autoWidth : function(){
29618         if(this.el){
29619             var tbl = this.el.child("table:first");
29620             var tbl2 = this.el.child("table:last");
29621             this.el.setWidth("auto");
29622             tbl.setWidth("auto");
29623             if(Roo.isIE7 && Roo.isStrict){
29624                 var ib = this.el.child('button:first');
29625                 if(ib && ib.getWidth() > 20){
29626                     ib.clip();
29627                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29628                 }
29629             }
29630             if(this.minWidth){
29631                 if(this.hidden){
29632                     this.el.beginMeasure();
29633                 }
29634                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29635                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29636                 }
29637                 if(this.hidden){
29638                     this.el.endMeasure();
29639                 }
29640             }
29641             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29642         } 
29643     },
29644     /**
29645      * Sets this button's click handler
29646      * @param {Function} handler The function to call when the button is clicked
29647      * @param {Object} scope (optional) Scope for the function passed above
29648      */
29649     setHandler : function(handler, scope){
29650         this.handler = handler;
29651         this.scope = scope;  
29652     },
29653     
29654     /**
29655      * Sets this button's arrow click handler
29656      * @param {Function} handler The function to call when the arrow is clicked
29657      * @param {Object} scope (optional) Scope for the function passed above
29658      */
29659     setArrowHandler : function(handler, scope){
29660         this.arrowHandler = handler;
29661         this.scope = scope;  
29662     },
29663     
29664     /**
29665      * Focus the button
29666      */
29667     focus : function(){
29668         if(this.el){
29669             this.el.child("button:first").focus();
29670         }
29671     },
29672
29673     // private
29674     onClick : function(e){
29675         e.preventDefault();
29676         if(!this.disabled){
29677             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29678                 if(this.menu && !this.menu.isVisible()){
29679                     this.menu.show(this.el, this.menuAlign);
29680                 }
29681                 this.fireEvent("arrowclick", this, e);
29682                 if(this.arrowHandler){
29683                     this.arrowHandler.call(this.scope || this, this, e);
29684                 }
29685             }else{
29686                 this.fireEvent("click", this, e);
29687                 if(this.handler){
29688                     this.handler.call(this.scope || this, this, e);
29689                 }
29690             }
29691         }
29692     },
29693     // private
29694     onMouseDown : function(e){
29695         if(!this.disabled){
29696             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29697         }
29698     },
29699     // private
29700     onMouseUp : function(e){
29701         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29702     }   
29703 });
29704
29705
29706 // backwards compat
29707 Roo.MenuButton = Roo.SplitButton;/*
29708  * Based on:
29709  * Ext JS Library 1.1.1
29710  * Copyright(c) 2006-2007, Ext JS, LLC.
29711  *
29712  * Originally Released Under LGPL - original licence link has changed is not relivant.
29713  *
29714  * Fork - LGPL
29715  * <script type="text/javascript">
29716  */
29717
29718 /**
29719  * @class Roo.Toolbar
29720  * Basic Toolbar class.
29721  * @constructor
29722  * Creates a new Toolbar
29723  * @param {Object} container The config object
29724  */ 
29725 Roo.Toolbar = function(container, buttons, config)
29726 {
29727     /// old consturctor format still supported..
29728     if(container instanceof Array){ // omit the container for later rendering
29729         buttons = container;
29730         config = buttons;
29731         container = null;
29732     }
29733     if (typeof(container) == 'object' && container.xtype) {
29734         config = container;
29735         container = config.container;
29736         buttons = config.buttons || []; // not really - use items!!
29737     }
29738     var xitems = [];
29739     if (config && config.items) {
29740         xitems = config.items;
29741         delete config.items;
29742     }
29743     Roo.apply(this, config);
29744     this.buttons = buttons;
29745     
29746     if(container){
29747         this.render(container);
29748     }
29749     this.xitems = xitems;
29750     Roo.each(xitems, function(b) {
29751         this.add(b);
29752     }, this);
29753     
29754 };
29755
29756 Roo.Toolbar.prototype = {
29757     /**
29758      * @cfg {Array} items
29759      * array of button configs or elements to add (will be converted to a MixedCollection)
29760      */
29761     
29762     /**
29763      * @cfg {String/HTMLElement/Element} container
29764      * The id or element that will contain the toolbar
29765      */
29766     // private
29767     render : function(ct){
29768         this.el = Roo.get(ct);
29769         if(this.cls){
29770             this.el.addClass(this.cls);
29771         }
29772         // using a table allows for vertical alignment
29773         // 100% width is needed by Safari...
29774         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29775         this.tr = this.el.child("tr", true);
29776         var autoId = 0;
29777         this.items = new Roo.util.MixedCollection(false, function(o){
29778             return o.id || ("item" + (++autoId));
29779         });
29780         if(this.buttons){
29781             this.add.apply(this, this.buttons);
29782             delete this.buttons;
29783         }
29784     },
29785
29786     /**
29787      * Adds element(s) to the toolbar -- this function takes a variable number of 
29788      * arguments of mixed type and adds them to the toolbar.
29789      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29790      * <ul>
29791      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29792      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29793      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29794      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29795      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29796      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29797      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29798      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29799      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29800      * </ul>
29801      * @param {Mixed} arg2
29802      * @param {Mixed} etc.
29803      */
29804     add : function(){
29805         var a = arguments, l = a.length;
29806         for(var i = 0; i < l; i++){
29807             this._add(a[i]);
29808         }
29809     },
29810     // private..
29811     _add : function(el) {
29812         
29813         if (el.xtype) {
29814             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29815         }
29816         
29817         if (el.applyTo){ // some kind of form field
29818             return this.addField(el);
29819         } 
29820         if (el.render){ // some kind of Toolbar.Item
29821             return this.addItem(el);
29822         }
29823         if (typeof el == "string"){ // string
29824             if(el == "separator" || el == "-"){
29825                 return this.addSeparator();
29826             }
29827             if (el == " "){
29828                 return this.addSpacer();
29829             }
29830             if(el == "->"){
29831                 return this.addFill();
29832             }
29833             return this.addText(el);
29834             
29835         }
29836         if(el.tagName){ // element
29837             return this.addElement(el);
29838         }
29839         if(typeof el == "object"){ // must be button config?
29840             return this.addButton(el);
29841         }
29842         // and now what?!?!
29843         return false;
29844         
29845     },
29846     
29847     /**
29848      * Add an Xtype element
29849      * @param {Object} xtype Xtype Object
29850      * @return {Object} created Object
29851      */
29852     addxtype : function(e){
29853         return this.add(e);  
29854     },
29855     
29856     /**
29857      * Returns the Element for this toolbar.
29858      * @return {Roo.Element}
29859      */
29860     getEl : function(){
29861         return this.el;  
29862     },
29863     
29864     /**
29865      * Adds a separator
29866      * @return {Roo.Toolbar.Item} The separator item
29867      */
29868     addSeparator : function(){
29869         return this.addItem(new Roo.Toolbar.Separator());
29870     },
29871
29872     /**
29873      * Adds a spacer element
29874      * @return {Roo.Toolbar.Spacer} The spacer item
29875      */
29876     addSpacer : function(){
29877         return this.addItem(new Roo.Toolbar.Spacer());
29878     },
29879
29880     /**
29881      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29882      * @return {Roo.Toolbar.Fill} The fill item
29883      */
29884     addFill : function(){
29885         return this.addItem(new Roo.Toolbar.Fill());
29886     },
29887
29888     /**
29889      * Adds any standard HTML element to the toolbar
29890      * @param {String/HTMLElement/Element} el The element or id of the element to add
29891      * @return {Roo.Toolbar.Item} The element's item
29892      */
29893     addElement : function(el){
29894         return this.addItem(new Roo.Toolbar.Item(el));
29895     },
29896     /**
29897      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29898      * @type Roo.util.MixedCollection  
29899      */
29900     items : false,
29901      
29902     /**
29903      * Adds any Toolbar.Item or subclass
29904      * @param {Roo.Toolbar.Item} item
29905      * @return {Roo.Toolbar.Item} The item
29906      */
29907     addItem : function(item){
29908         var td = this.nextBlock();
29909         item.render(td);
29910         this.items.add(item);
29911         return item;
29912     },
29913     
29914     /**
29915      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29916      * @param {Object/Array} config A button config or array of configs
29917      * @return {Roo.Toolbar.Button/Array}
29918      */
29919     addButton : function(config){
29920         if(config instanceof Array){
29921             var buttons = [];
29922             for(var i = 0, len = config.length; i < len; i++) {
29923                 buttons.push(this.addButton(config[i]));
29924             }
29925             return buttons;
29926         }
29927         var b = config;
29928         if(!(config instanceof Roo.Toolbar.Button)){
29929             b = config.split ?
29930                 new Roo.Toolbar.SplitButton(config) :
29931                 new Roo.Toolbar.Button(config);
29932         }
29933         var td = this.nextBlock();
29934         b.render(td);
29935         this.items.add(b);
29936         return b;
29937     },
29938     
29939     /**
29940      * Adds text to the toolbar
29941      * @param {String} text The text to add
29942      * @return {Roo.Toolbar.Item} The element's item
29943      */
29944     addText : function(text){
29945         return this.addItem(new Roo.Toolbar.TextItem(text));
29946     },
29947     
29948     /**
29949      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29950      * @param {Number} index The index where the item is to be inserted
29951      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29952      * @return {Roo.Toolbar.Button/Item}
29953      */
29954     insertButton : function(index, item){
29955         if(item instanceof Array){
29956             var buttons = [];
29957             for(var i = 0, len = item.length; i < len; i++) {
29958                buttons.push(this.insertButton(index + i, item[i]));
29959             }
29960             return buttons;
29961         }
29962         if (!(item instanceof Roo.Toolbar.Button)){
29963            item = new Roo.Toolbar.Button(item);
29964         }
29965         var td = document.createElement("td");
29966         this.tr.insertBefore(td, this.tr.childNodes[index]);
29967         item.render(td);
29968         this.items.insert(index, item);
29969         return item;
29970     },
29971     
29972     /**
29973      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29974      * @param {Object} config
29975      * @return {Roo.Toolbar.Item} The element's item
29976      */
29977     addDom : function(config, returnEl){
29978         var td = this.nextBlock();
29979         Roo.DomHelper.overwrite(td, config);
29980         var ti = new Roo.Toolbar.Item(td.firstChild);
29981         ti.render(td);
29982         this.items.add(ti);
29983         return ti;
29984     },
29985
29986     /**
29987      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29988      * @type Roo.util.MixedCollection  
29989      */
29990     fields : false,
29991     
29992     /**
29993      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29994      * Note: the field should not have been rendered yet. For a field that has already been
29995      * rendered, use {@link #addElement}.
29996      * @param {Roo.form.Field} field
29997      * @return {Roo.ToolbarItem}
29998      */
29999      
30000       
30001     addField : function(field) {
30002         if (!this.fields) {
30003             var autoId = 0;
30004             this.fields = new Roo.util.MixedCollection(false, function(o){
30005                 return o.id || ("item" + (++autoId));
30006             });
30007
30008         }
30009         
30010         var td = this.nextBlock();
30011         field.render(td);
30012         var ti = new Roo.Toolbar.Item(td.firstChild);
30013         ti.render(td);
30014         this.items.add(ti);
30015         this.fields.add(field);
30016         return ti;
30017     },
30018     /**
30019      * Hide the toolbar
30020      * @method hide
30021      */
30022      
30023       
30024     hide : function()
30025     {
30026         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30027         this.el.child('div').hide();
30028     },
30029     /**
30030      * Show the toolbar
30031      * @method show
30032      */
30033     show : function()
30034     {
30035         this.el.child('div').show();
30036     },
30037       
30038     // private
30039     nextBlock : function(){
30040         var td = document.createElement("td");
30041         this.tr.appendChild(td);
30042         return td;
30043     },
30044
30045     // private
30046     destroy : function(){
30047         if(this.items){ // rendered?
30048             Roo.destroy.apply(Roo, this.items.items);
30049         }
30050         if(this.fields){ // rendered?
30051             Roo.destroy.apply(Roo, this.fields.items);
30052         }
30053         Roo.Element.uncache(this.el, this.tr);
30054     }
30055 };
30056
30057 /**
30058  * @class Roo.Toolbar.Item
30059  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30060  * @constructor
30061  * Creates a new Item
30062  * @param {HTMLElement} el 
30063  */
30064 Roo.Toolbar.Item = function(el){
30065     var cfg = {};
30066     if (typeof (el.xtype) != 'undefined') {
30067         cfg = el;
30068         el = cfg.el;
30069     }
30070     
30071     this.el = Roo.getDom(el);
30072     this.id = Roo.id(this.el);
30073     this.hidden = false;
30074     
30075     this.addEvents({
30076          /**
30077              * @event render
30078              * Fires when the button is rendered
30079              * @param {Button} this
30080              */
30081         'render': true
30082     });
30083     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30084 };
30085 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30086 //Roo.Toolbar.Item.prototype = {
30087     
30088     /**
30089      * Get this item's HTML Element
30090      * @return {HTMLElement}
30091      */
30092     getEl : function(){
30093        return this.el;  
30094     },
30095
30096     // private
30097     render : function(td){
30098         
30099          this.td = td;
30100         td.appendChild(this.el);
30101         
30102         this.fireEvent('render', this);
30103     },
30104     
30105     /**
30106      * Removes and destroys this item.
30107      */
30108     destroy : function(){
30109         this.td.parentNode.removeChild(this.td);
30110     },
30111     
30112     /**
30113      * Shows this item.
30114      */
30115     show: function(){
30116         this.hidden = false;
30117         this.td.style.display = "";
30118     },
30119     
30120     /**
30121      * Hides this item.
30122      */
30123     hide: function(){
30124         this.hidden = true;
30125         this.td.style.display = "none";
30126     },
30127     
30128     /**
30129      * Convenience function for boolean show/hide.
30130      * @param {Boolean} visible true to show/false to hide
30131      */
30132     setVisible: function(visible){
30133         if(visible) {
30134             this.show();
30135         }else{
30136             this.hide();
30137         }
30138     },
30139     
30140     /**
30141      * Try to focus this item.
30142      */
30143     focus : function(){
30144         Roo.fly(this.el).focus();
30145     },
30146     
30147     /**
30148      * Disables this item.
30149      */
30150     disable : function(){
30151         Roo.fly(this.td).addClass("x-item-disabled");
30152         this.disabled = true;
30153         this.el.disabled = true;
30154     },
30155     
30156     /**
30157      * Enables this item.
30158      */
30159     enable : function(){
30160         Roo.fly(this.td).removeClass("x-item-disabled");
30161         this.disabled = false;
30162         this.el.disabled = false;
30163     }
30164 });
30165
30166
30167 /**
30168  * @class Roo.Toolbar.Separator
30169  * @extends Roo.Toolbar.Item
30170  * A simple toolbar separator class
30171  * @constructor
30172  * Creates a new Separator
30173  */
30174 Roo.Toolbar.Separator = function(cfg){
30175     
30176     var s = document.createElement("span");
30177     s.className = "ytb-sep";
30178     if (cfg) {
30179         cfg.el = s;
30180     }
30181     
30182     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30183 };
30184 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30185     enable:Roo.emptyFn,
30186     disable:Roo.emptyFn,
30187     focus:Roo.emptyFn
30188 });
30189
30190 /**
30191  * @class Roo.Toolbar.Spacer
30192  * @extends Roo.Toolbar.Item
30193  * A simple element that adds extra horizontal space to a toolbar.
30194  * @constructor
30195  * Creates a new Spacer
30196  */
30197 Roo.Toolbar.Spacer = function(cfg){
30198     var s = document.createElement("div");
30199     s.className = "ytb-spacer";
30200     if (cfg) {
30201         cfg.el = s;
30202     }
30203     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30204 };
30205 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30206     enable:Roo.emptyFn,
30207     disable:Roo.emptyFn,
30208     focus:Roo.emptyFn
30209 });
30210
30211 /**
30212  * @class Roo.Toolbar.Fill
30213  * @extends Roo.Toolbar.Spacer
30214  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30215  * @constructor
30216  * Creates a new Spacer
30217  */
30218 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30219     // private
30220     render : function(td){
30221         td.style.width = '100%';
30222         Roo.Toolbar.Fill.superclass.render.call(this, td);
30223     }
30224 });
30225
30226 /**
30227  * @class Roo.Toolbar.TextItem
30228  * @extends Roo.Toolbar.Item
30229  * A simple class that renders text directly into a toolbar.
30230  * @constructor
30231  * Creates a new TextItem
30232  * @param {String} text
30233  */
30234 Roo.Toolbar.TextItem = function(cfg){
30235     var  text = cfg || "";
30236     if (typeof(cfg) == 'object') {
30237         text = cfg.text || "";
30238     }  else {
30239         cfg = null;
30240     }
30241     var s = document.createElement("span");
30242     s.className = "ytb-text";
30243     s.innerHTML = text;
30244     if (cfg) {
30245         cfg.el  = s;
30246     }
30247     
30248     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30249 };
30250 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30251     
30252      
30253     enable:Roo.emptyFn,
30254     disable:Roo.emptyFn,
30255     focus:Roo.emptyFn
30256 });
30257
30258 /**
30259  * @class Roo.Toolbar.Button
30260  * @extends Roo.Button
30261  * A button that renders into a toolbar.
30262  * @constructor
30263  * Creates a new Button
30264  * @param {Object} config A standard {@link Roo.Button} config object
30265  */
30266 Roo.Toolbar.Button = function(config){
30267     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30268 };
30269 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30270     render : function(td){
30271         this.td = td;
30272         Roo.Toolbar.Button.superclass.render.call(this, td);
30273     },
30274     
30275     /**
30276      * Removes and destroys this button
30277      */
30278     destroy : function(){
30279         Roo.Toolbar.Button.superclass.destroy.call(this);
30280         this.td.parentNode.removeChild(this.td);
30281     },
30282     
30283     /**
30284      * Shows this button
30285      */
30286     show: function(){
30287         this.hidden = false;
30288         this.td.style.display = "";
30289     },
30290     
30291     /**
30292      * Hides this button
30293      */
30294     hide: function(){
30295         this.hidden = true;
30296         this.td.style.display = "none";
30297     },
30298
30299     /**
30300      * Disables this item
30301      */
30302     disable : function(){
30303         Roo.fly(this.td).addClass("x-item-disabled");
30304         this.disabled = true;
30305     },
30306
30307     /**
30308      * Enables this item
30309      */
30310     enable : function(){
30311         Roo.fly(this.td).removeClass("x-item-disabled");
30312         this.disabled = false;
30313     }
30314 });
30315 // backwards compat
30316 Roo.ToolbarButton = Roo.Toolbar.Button;
30317
30318 /**
30319  * @class Roo.Toolbar.SplitButton
30320  * @extends Roo.SplitButton
30321  * A menu button that renders into a toolbar.
30322  * @constructor
30323  * Creates a new SplitButton
30324  * @param {Object} config A standard {@link Roo.SplitButton} config object
30325  */
30326 Roo.Toolbar.SplitButton = function(config){
30327     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30328 };
30329 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30330     render : function(td){
30331         this.td = td;
30332         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30333     },
30334     
30335     /**
30336      * Removes and destroys this button
30337      */
30338     destroy : function(){
30339         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30340         this.td.parentNode.removeChild(this.td);
30341     },
30342     
30343     /**
30344      * Shows this button
30345      */
30346     show: function(){
30347         this.hidden = false;
30348         this.td.style.display = "";
30349     },
30350     
30351     /**
30352      * Hides this button
30353      */
30354     hide: function(){
30355         this.hidden = true;
30356         this.td.style.display = "none";
30357     }
30358 });
30359
30360 // backwards compat
30361 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30362  * Based on:
30363  * Ext JS Library 1.1.1
30364  * Copyright(c) 2006-2007, Ext JS, LLC.
30365  *
30366  * Originally Released Under LGPL - original licence link has changed is not relivant.
30367  *
30368  * Fork - LGPL
30369  * <script type="text/javascript">
30370  */
30371  
30372 /**
30373  * @class Roo.PagingToolbar
30374  * @extends Roo.Toolbar
30375  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30376  * @constructor
30377  * Create a new PagingToolbar
30378  * @param {Object} config The config object
30379  */
30380 Roo.PagingToolbar = function(el, ds, config)
30381 {
30382     // old args format still supported... - xtype is prefered..
30383     if (typeof(el) == 'object' && el.xtype) {
30384         // created from xtype...
30385         config = el;
30386         ds = el.dataSource;
30387         el = config.container;
30388     }
30389     var items = [];
30390     if (config.items) {
30391         items = config.items;
30392         config.items = [];
30393     }
30394     
30395     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30396     this.ds = ds;
30397     this.cursor = 0;
30398     this.renderButtons(this.el);
30399     this.bind(ds);
30400     
30401     // supprot items array.
30402    
30403     Roo.each(items, function(e) {
30404         this.add(Roo.factory(e));
30405     },this);
30406     
30407 };
30408
30409 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30410     /**
30411      * @cfg {Roo.data.Store} dataSource
30412      * The underlying data store providing the paged data
30413      */
30414     /**
30415      * @cfg {String/HTMLElement/Element} container
30416      * container The id or element that will contain the toolbar
30417      */
30418     /**
30419      * @cfg {Boolean} displayInfo
30420      * True to display the displayMsg (defaults to false)
30421      */
30422     /**
30423      * @cfg {Number} pageSize
30424      * The number of records to display per page (defaults to 20)
30425      */
30426     pageSize: 20,
30427     /**
30428      * @cfg {String} displayMsg
30429      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30430      */
30431     displayMsg : 'Displaying {0} - {1} of {2}',
30432     /**
30433      * @cfg {String} emptyMsg
30434      * The message to display when no records are found (defaults to "No data to display")
30435      */
30436     emptyMsg : 'No data to display',
30437     /**
30438      * Customizable piece of the default paging text (defaults to "Page")
30439      * @type String
30440      */
30441     beforePageText : "Page",
30442     /**
30443      * Customizable piece of the default paging text (defaults to "of %0")
30444      * @type String
30445      */
30446     afterPageText : "of {0}",
30447     /**
30448      * Customizable piece of the default paging text (defaults to "First Page")
30449      * @type String
30450      */
30451     firstText : "First Page",
30452     /**
30453      * Customizable piece of the default paging text (defaults to "Previous Page")
30454      * @type String
30455      */
30456     prevText : "Previous Page",
30457     /**
30458      * Customizable piece of the default paging text (defaults to "Next Page")
30459      * @type String
30460      */
30461     nextText : "Next Page",
30462     /**
30463      * Customizable piece of the default paging text (defaults to "Last Page")
30464      * @type String
30465      */
30466     lastText : "Last Page",
30467     /**
30468      * Customizable piece of the default paging text (defaults to "Refresh")
30469      * @type String
30470      */
30471     refreshText : "Refresh",
30472
30473     // private
30474     renderButtons : function(el){
30475         Roo.PagingToolbar.superclass.render.call(this, el);
30476         this.first = this.addButton({
30477             tooltip: this.firstText,
30478             cls: "x-btn-icon x-grid-page-first",
30479             disabled: true,
30480             handler: this.onClick.createDelegate(this, ["first"])
30481         });
30482         this.prev = this.addButton({
30483             tooltip: this.prevText,
30484             cls: "x-btn-icon x-grid-page-prev",
30485             disabled: true,
30486             handler: this.onClick.createDelegate(this, ["prev"])
30487         });
30488         //this.addSeparator();
30489         this.add(this.beforePageText);
30490         this.field = Roo.get(this.addDom({
30491            tag: "input",
30492            type: "text",
30493            size: "3",
30494            value: "1",
30495            cls: "x-grid-page-number"
30496         }).el);
30497         this.field.on("keydown", this.onPagingKeydown, this);
30498         this.field.on("focus", function(){this.dom.select();});
30499         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30500         this.field.setHeight(18);
30501         //this.addSeparator();
30502         this.next = this.addButton({
30503             tooltip: this.nextText,
30504             cls: "x-btn-icon x-grid-page-next",
30505             disabled: true,
30506             handler: this.onClick.createDelegate(this, ["next"])
30507         });
30508         this.last = this.addButton({
30509             tooltip: this.lastText,
30510             cls: "x-btn-icon x-grid-page-last",
30511             disabled: true,
30512             handler: this.onClick.createDelegate(this, ["last"])
30513         });
30514         //this.addSeparator();
30515         this.loading = this.addButton({
30516             tooltip: this.refreshText,
30517             cls: "x-btn-icon x-grid-loading",
30518             handler: this.onClick.createDelegate(this, ["refresh"])
30519         });
30520
30521         if(this.displayInfo){
30522             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30523         }
30524     },
30525
30526     // private
30527     updateInfo : function(){
30528         if(this.displayEl){
30529             var count = this.ds.getCount();
30530             var msg = count == 0 ?
30531                 this.emptyMsg :
30532                 String.format(
30533                     this.displayMsg,
30534                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30535                 );
30536             this.displayEl.update(msg);
30537         }
30538     },
30539
30540     // private
30541     onLoad : function(ds, r, o){
30542        this.cursor = o.params ? o.params.start : 0;
30543        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30544
30545        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30546        this.field.dom.value = ap;
30547        this.first.setDisabled(ap == 1);
30548        this.prev.setDisabled(ap == 1);
30549        this.next.setDisabled(ap == ps);
30550        this.last.setDisabled(ap == ps);
30551        this.loading.enable();
30552        this.updateInfo();
30553     },
30554
30555     // private
30556     getPageData : function(){
30557         var total = this.ds.getTotalCount();
30558         return {
30559             total : total,
30560             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30561             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30562         };
30563     },
30564
30565     // private
30566     onLoadError : function(){
30567         this.loading.enable();
30568     },
30569
30570     // private
30571     onPagingKeydown : function(e){
30572         var k = e.getKey();
30573         var d = this.getPageData();
30574         if(k == e.RETURN){
30575             var v = this.field.dom.value, pageNum;
30576             if(!v || isNaN(pageNum = parseInt(v, 10))){
30577                 this.field.dom.value = d.activePage;
30578                 return;
30579             }
30580             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30581             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30582             e.stopEvent();
30583         }
30584         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))
30585         {
30586           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30587           this.field.dom.value = pageNum;
30588           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30589           e.stopEvent();
30590         }
30591         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30592         {
30593           var v = this.field.dom.value, pageNum; 
30594           var increment = (e.shiftKey) ? 10 : 1;
30595           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30596             increment *= -1;
30597           }
30598           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30599             this.field.dom.value = d.activePage;
30600             return;
30601           }
30602           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30603           {
30604             this.field.dom.value = parseInt(v, 10) + increment;
30605             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30606             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30607           }
30608           e.stopEvent();
30609         }
30610     },
30611
30612     // private
30613     beforeLoad : function(){
30614         if(this.loading){
30615             this.loading.disable();
30616         }
30617     },
30618
30619     // private
30620     onClick : function(which){
30621         var ds = this.ds;
30622         switch(which){
30623             case "first":
30624                 ds.load({params:{start: 0, limit: this.pageSize}});
30625             break;
30626             case "prev":
30627                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30628             break;
30629             case "next":
30630                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30631             break;
30632             case "last":
30633                 var total = ds.getTotalCount();
30634                 var extra = total % this.pageSize;
30635                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30636                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30637             break;
30638             case "refresh":
30639                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30640             break;
30641         }
30642     },
30643
30644     /**
30645      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30646      * @param {Roo.data.Store} store The data store to unbind
30647      */
30648     unbind : function(ds){
30649         ds.un("beforeload", this.beforeLoad, this);
30650         ds.un("load", this.onLoad, this);
30651         ds.un("loadexception", this.onLoadError, this);
30652         ds.un("remove", this.updateInfo, this);
30653         ds.un("add", this.updateInfo, this);
30654         this.ds = undefined;
30655     },
30656
30657     /**
30658      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30659      * @param {Roo.data.Store} store The data store to bind
30660      */
30661     bind : function(ds){
30662         ds.on("beforeload", this.beforeLoad, this);
30663         ds.on("load", this.onLoad, this);
30664         ds.on("loadexception", this.onLoadError, this);
30665         ds.on("remove", this.updateInfo, this);
30666         ds.on("add", this.updateInfo, this);
30667         this.ds = ds;
30668     }
30669 });/*
30670  * Based on:
30671  * Ext JS Library 1.1.1
30672  * Copyright(c) 2006-2007, Ext JS, LLC.
30673  *
30674  * Originally Released Under LGPL - original licence link has changed is not relivant.
30675  *
30676  * Fork - LGPL
30677  * <script type="text/javascript">
30678  */
30679
30680 /**
30681  * @class Roo.Resizable
30682  * @extends Roo.util.Observable
30683  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30684  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30685  * 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
30686  * the element will be wrapped for you automatically.</p>
30687  * <p>Here is the list of valid resize handles:</p>
30688  * <pre>
30689 Value   Description
30690 ------  -------------------
30691  'n'     north
30692  's'     south
30693  'e'     east
30694  'w'     west
30695  'nw'    northwest
30696  'sw'    southwest
30697  'se'    southeast
30698  'ne'    northeast
30699  'hd'    horizontal drag
30700  'all'   all
30701 </pre>
30702  * <p>Here's an example showing the creation of a typical Resizable:</p>
30703  * <pre><code>
30704 var resizer = new Roo.Resizable("element-id", {
30705     handles: 'all',
30706     minWidth: 200,
30707     minHeight: 100,
30708     maxWidth: 500,
30709     maxHeight: 400,
30710     pinned: true
30711 });
30712 resizer.on("resize", myHandler);
30713 </code></pre>
30714  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30715  * resizer.east.setDisplayed(false);</p>
30716  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30717  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30718  * resize operation's new size (defaults to [0, 0])
30719  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30720  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30721  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30722  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30723  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30724  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30725  * @cfg {Number} width The width of the element in pixels (defaults to null)
30726  * @cfg {Number} height The height of the element in pixels (defaults to null)
30727  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30728  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30729  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30730  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30731  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30732  * in favor of the handles config option (defaults to false)
30733  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30734  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30735  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30736  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30737  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30738  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30739  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30740  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30741  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30742  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30743  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30744  * @constructor
30745  * Create a new resizable component
30746  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30747  * @param {Object} config configuration options
30748   */
30749 Roo.Resizable = function(el, config)
30750 {
30751     this.el = Roo.get(el);
30752
30753     if(config && config.wrap){
30754         config.resizeChild = this.el;
30755         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30756         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30757         this.el.setStyle("overflow", "hidden");
30758         this.el.setPositioning(config.resizeChild.getPositioning());
30759         config.resizeChild.clearPositioning();
30760         if(!config.width || !config.height){
30761             var csize = config.resizeChild.getSize();
30762             this.el.setSize(csize.width, csize.height);
30763         }
30764         if(config.pinned && !config.adjustments){
30765             config.adjustments = "auto";
30766         }
30767     }
30768
30769     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30770     this.proxy.unselectable();
30771     this.proxy.enableDisplayMode('block');
30772
30773     Roo.apply(this, config);
30774
30775     if(this.pinned){
30776         this.disableTrackOver = true;
30777         this.el.addClass("x-resizable-pinned");
30778     }
30779     // if the element isn't positioned, make it relative
30780     var position = this.el.getStyle("position");
30781     if(position != "absolute" && position != "fixed"){
30782         this.el.setStyle("position", "relative");
30783     }
30784     if(!this.handles){ // no handles passed, must be legacy style
30785         this.handles = 's,e,se';
30786         if(this.multiDirectional){
30787             this.handles += ',n,w';
30788         }
30789     }
30790     if(this.handles == "all"){
30791         this.handles = "n s e w ne nw se sw";
30792     }
30793     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30794     var ps = Roo.Resizable.positions;
30795     for(var i = 0, len = hs.length; i < len; i++){
30796         if(hs[i] && ps[hs[i]]){
30797             var pos = ps[hs[i]];
30798             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30799         }
30800     }
30801     // legacy
30802     this.corner = this.southeast;
30803     
30804     // updateBox = the box can move..
30805     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30806         this.updateBox = true;
30807     }
30808
30809     this.activeHandle = null;
30810
30811     if(this.resizeChild){
30812         if(typeof this.resizeChild == "boolean"){
30813             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30814         }else{
30815             this.resizeChild = Roo.get(this.resizeChild, true);
30816         }
30817     }
30818     
30819     if(this.adjustments == "auto"){
30820         var rc = this.resizeChild;
30821         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30822         if(rc && (hw || hn)){
30823             rc.position("relative");
30824             rc.setLeft(hw ? hw.el.getWidth() : 0);
30825             rc.setTop(hn ? hn.el.getHeight() : 0);
30826         }
30827         this.adjustments = [
30828             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30829             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30830         ];
30831     }
30832
30833     if(this.draggable){
30834         this.dd = this.dynamic ?
30835             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30836         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30837     }
30838
30839     // public events
30840     this.addEvents({
30841         /**
30842          * @event beforeresize
30843          * Fired before resize is allowed. Set enabled to false to cancel resize.
30844          * @param {Roo.Resizable} this
30845          * @param {Roo.EventObject} e The mousedown event
30846          */
30847         "beforeresize" : true,
30848         /**
30849          * @event resizing
30850          * Fired a resizing.
30851          * @param {Roo.Resizable} this
30852          * @param {Number} x The new x position
30853          * @param {Number} y The new y position
30854          * @param {Number} w The new w width
30855          * @param {Number} h The new h hight
30856          * @param {Roo.EventObject} e The mouseup event
30857          */
30858         "resizing" : true,
30859         /**
30860          * @event resize
30861          * Fired after a resize.
30862          * @param {Roo.Resizable} this
30863          * @param {Number} width The new width
30864          * @param {Number} height The new height
30865          * @param {Roo.EventObject} e The mouseup event
30866          */
30867         "resize" : true
30868     });
30869
30870     if(this.width !== null && this.height !== null){
30871         this.resizeTo(this.width, this.height);
30872     }else{
30873         this.updateChildSize();
30874     }
30875     if(Roo.isIE){
30876         this.el.dom.style.zoom = 1;
30877     }
30878     Roo.Resizable.superclass.constructor.call(this);
30879 };
30880
30881 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30882         resizeChild : false,
30883         adjustments : [0, 0],
30884         minWidth : 5,
30885         minHeight : 5,
30886         maxWidth : 10000,
30887         maxHeight : 10000,
30888         enabled : true,
30889         animate : false,
30890         duration : .35,
30891         dynamic : false,
30892         handles : false,
30893         multiDirectional : false,
30894         disableTrackOver : false,
30895         easing : 'easeOutStrong',
30896         widthIncrement : 0,
30897         heightIncrement : 0,
30898         pinned : false,
30899         width : null,
30900         height : null,
30901         preserveRatio : false,
30902         transparent: false,
30903         minX: 0,
30904         minY: 0,
30905         draggable: false,
30906
30907         /**
30908          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30909          */
30910         constrainTo: undefined,
30911         /**
30912          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30913          */
30914         resizeRegion: undefined,
30915
30916
30917     /**
30918      * Perform a manual resize
30919      * @param {Number} width
30920      * @param {Number} height
30921      */
30922     resizeTo : function(width, height){
30923         this.el.setSize(width, height);
30924         this.updateChildSize();
30925         this.fireEvent("resize", this, width, height, null);
30926     },
30927
30928     // private
30929     startSizing : function(e, handle){
30930         this.fireEvent("beforeresize", this, e);
30931         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30932
30933             if(!this.overlay){
30934                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30935                 this.overlay.unselectable();
30936                 this.overlay.enableDisplayMode("block");
30937                 this.overlay.on("mousemove", this.onMouseMove, this);
30938                 this.overlay.on("mouseup", this.onMouseUp, this);
30939             }
30940             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30941
30942             this.resizing = true;
30943             this.startBox = this.el.getBox();
30944             this.startPoint = e.getXY();
30945             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30946                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30947
30948             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30949             this.overlay.show();
30950
30951             if(this.constrainTo) {
30952                 var ct = Roo.get(this.constrainTo);
30953                 this.resizeRegion = ct.getRegion().adjust(
30954                     ct.getFrameWidth('t'),
30955                     ct.getFrameWidth('l'),
30956                     -ct.getFrameWidth('b'),
30957                     -ct.getFrameWidth('r')
30958                 );
30959             }
30960
30961             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30962             this.proxy.show();
30963             this.proxy.setBox(this.startBox);
30964             if(!this.dynamic){
30965                 this.proxy.setStyle('visibility', 'visible');
30966             }
30967         }
30968     },
30969
30970     // private
30971     onMouseDown : function(handle, e){
30972         if(this.enabled){
30973             e.stopEvent();
30974             this.activeHandle = handle;
30975             this.startSizing(e, handle);
30976         }
30977     },
30978
30979     // private
30980     onMouseUp : function(e){
30981         var size = this.resizeElement();
30982         this.resizing = false;
30983         this.handleOut();
30984         this.overlay.hide();
30985         this.proxy.hide();
30986         this.fireEvent("resize", this, size.width, size.height, e);
30987     },
30988
30989     // private
30990     updateChildSize : function(){
30991         
30992         if(this.resizeChild){
30993             var el = this.el;
30994             var child = this.resizeChild;
30995             var adj = this.adjustments;
30996             if(el.dom.offsetWidth){
30997                 var b = el.getSize(true);
30998                 child.setSize(b.width+adj[0], b.height+adj[1]);
30999             }
31000             // Second call here for IE
31001             // The first call enables instant resizing and
31002             // the second call corrects scroll bars if they
31003             // exist
31004             if(Roo.isIE){
31005                 setTimeout(function(){
31006                     if(el.dom.offsetWidth){
31007                         var b = el.getSize(true);
31008                         child.setSize(b.width+adj[0], b.height+adj[1]);
31009                     }
31010                 }, 10);
31011             }
31012         }
31013     },
31014
31015     // private
31016     snap : function(value, inc, min){
31017         if(!inc || !value) {
31018             return value;
31019         }
31020         var newValue = value;
31021         var m = value % inc;
31022         if(m > 0){
31023             if(m > (inc/2)){
31024                 newValue = value + (inc-m);
31025             }else{
31026                 newValue = value - m;
31027             }
31028         }
31029         return Math.max(min, newValue);
31030     },
31031
31032     // private
31033     resizeElement : function(){
31034         var box = this.proxy.getBox();
31035         if(this.updateBox){
31036             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31037         }else{
31038             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31039         }
31040         this.updateChildSize();
31041         if(!this.dynamic){
31042             this.proxy.hide();
31043         }
31044         return box;
31045     },
31046
31047     // private
31048     constrain : function(v, diff, m, mx){
31049         if(v - diff < m){
31050             diff = v - m;
31051         }else if(v - diff > mx){
31052             diff = mx - v;
31053         }
31054         return diff;
31055     },
31056
31057     // private
31058     onMouseMove : function(e){
31059         
31060         if(this.enabled){
31061             try{// try catch so if something goes wrong the user doesn't get hung
31062
31063             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31064                 return;
31065             }
31066
31067             //var curXY = this.startPoint;
31068             var curSize = this.curSize || this.startBox;
31069             var x = this.startBox.x, y = this.startBox.y;
31070             var ox = x, oy = y;
31071             var w = curSize.width, h = curSize.height;
31072             var ow = w, oh = h;
31073             var mw = this.minWidth, mh = this.minHeight;
31074             var mxw = this.maxWidth, mxh = this.maxHeight;
31075             var wi = this.widthIncrement;
31076             var hi = this.heightIncrement;
31077
31078             var eventXY = e.getXY();
31079             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31080             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31081
31082             var pos = this.activeHandle.position;
31083
31084             switch(pos){
31085                 case "east":
31086                     w += diffX;
31087                     w = Math.min(Math.max(mw, w), mxw);
31088                     break;
31089              
31090                 case "south":
31091                     h += diffY;
31092                     h = Math.min(Math.max(mh, h), mxh);
31093                     break;
31094                 case "southeast":
31095                     w += diffX;
31096                     h += diffY;
31097                     w = Math.min(Math.max(mw, w), mxw);
31098                     h = Math.min(Math.max(mh, h), mxh);
31099                     break;
31100                 case "north":
31101                     diffY = this.constrain(h, diffY, mh, mxh);
31102                     y += diffY;
31103                     h -= diffY;
31104                     break;
31105                 case "hdrag":
31106                     
31107                     if (wi) {
31108                         var adiffX = Math.abs(diffX);
31109                         var sub = (adiffX % wi); // how much 
31110                         if (sub > (wi/2)) { // far enough to snap
31111                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31112                         } else {
31113                             // remove difference.. 
31114                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31115                         }
31116                     }
31117                     x += diffX;
31118                     x = Math.max(this.minX, x);
31119                     break;
31120                 case "west":
31121                     diffX = this.constrain(w, diffX, mw, mxw);
31122                     x += diffX;
31123                     w -= diffX;
31124                     break;
31125                 case "northeast":
31126                     w += diffX;
31127                     w = Math.min(Math.max(mw, w), mxw);
31128                     diffY = this.constrain(h, diffY, mh, mxh);
31129                     y += diffY;
31130                     h -= diffY;
31131                     break;
31132                 case "northwest":
31133                     diffX = this.constrain(w, diffX, mw, mxw);
31134                     diffY = this.constrain(h, diffY, mh, mxh);
31135                     y += diffY;
31136                     h -= diffY;
31137                     x += diffX;
31138                     w -= diffX;
31139                     break;
31140                case "southwest":
31141                     diffX = this.constrain(w, diffX, mw, mxw);
31142                     h += diffY;
31143                     h = Math.min(Math.max(mh, h), mxh);
31144                     x += diffX;
31145                     w -= diffX;
31146                     break;
31147             }
31148
31149             var sw = this.snap(w, wi, mw);
31150             var sh = this.snap(h, hi, mh);
31151             if(sw != w || sh != h){
31152                 switch(pos){
31153                     case "northeast":
31154                         y -= sh - h;
31155                     break;
31156                     case "north":
31157                         y -= sh - h;
31158                         break;
31159                     case "southwest":
31160                         x -= sw - w;
31161                     break;
31162                     case "west":
31163                         x -= sw - w;
31164                         break;
31165                     case "northwest":
31166                         x -= sw - w;
31167                         y -= sh - h;
31168                     break;
31169                 }
31170                 w = sw;
31171                 h = sh;
31172             }
31173
31174             if(this.preserveRatio){
31175                 switch(pos){
31176                     case "southeast":
31177                     case "east":
31178                         h = oh * (w/ow);
31179                         h = Math.min(Math.max(mh, h), mxh);
31180                         w = ow * (h/oh);
31181                        break;
31182                     case "south":
31183                         w = ow * (h/oh);
31184                         w = Math.min(Math.max(mw, w), mxw);
31185                         h = oh * (w/ow);
31186                         break;
31187                     case "northeast":
31188                         w = ow * (h/oh);
31189                         w = Math.min(Math.max(mw, w), mxw);
31190                         h = oh * (w/ow);
31191                     break;
31192                     case "north":
31193                         var tw = w;
31194                         w = ow * (h/oh);
31195                         w = Math.min(Math.max(mw, w), mxw);
31196                         h = oh * (w/ow);
31197                         x += (tw - w) / 2;
31198                         break;
31199                     case "southwest":
31200                         h = oh * (w/ow);
31201                         h = Math.min(Math.max(mh, h), mxh);
31202                         var tw = w;
31203                         w = ow * (h/oh);
31204                         x += tw - w;
31205                         break;
31206                     case "west":
31207                         var th = h;
31208                         h = oh * (w/ow);
31209                         h = Math.min(Math.max(mh, h), mxh);
31210                         y += (th - h) / 2;
31211                         var tw = w;
31212                         w = ow * (h/oh);
31213                         x += tw - w;
31214                        break;
31215                     case "northwest":
31216                         var tw = w;
31217                         var th = h;
31218                         h = oh * (w/ow);
31219                         h = Math.min(Math.max(mh, h), mxh);
31220                         w = ow * (h/oh);
31221                         y += th - h;
31222                         x += tw - w;
31223                        break;
31224
31225                 }
31226             }
31227             if (pos == 'hdrag') {
31228                 w = ow;
31229             }
31230             this.proxy.setBounds(x, y, w, h);
31231             if(this.dynamic){
31232                 this.resizeElement();
31233             }
31234             }catch(e){}
31235         }
31236         this.fireEvent("resizing", this, x, y, w, h, e);
31237     },
31238
31239     // private
31240     handleOver : function(){
31241         if(this.enabled){
31242             this.el.addClass("x-resizable-over");
31243         }
31244     },
31245
31246     // private
31247     handleOut : function(){
31248         if(!this.resizing){
31249             this.el.removeClass("x-resizable-over");
31250         }
31251     },
31252
31253     /**
31254      * Returns the element this component is bound to.
31255      * @return {Roo.Element}
31256      */
31257     getEl : function(){
31258         return this.el;
31259     },
31260
31261     /**
31262      * Returns the resizeChild element (or null).
31263      * @return {Roo.Element}
31264      */
31265     getResizeChild : function(){
31266         return this.resizeChild;
31267     },
31268     groupHandler : function()
31269     {
31270         
31271     },
31272     /**
31273      * Destroys this resizable. If the element was wrapped and
31274      * removeEl is not true then the element remains.
31275      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31276      */
31277     destroy : function(removeEl){
31278         this.proxy.remove();
31279         if(this.overlay){
31280             this.overlay.removeAllListeners();
31281             this.overlay.remove();
31282         }
31283         var ps = Roo.Resizable.positions;
31284         for(var k in ps){
31285             if(typeof ps[k] != "function" && this[ps[k]]){
31286                 var h = this[ps[k]];
31287                 h.el.removeAllListeners();
31288                 h.el.remove();
31289             }
31290         }
31291         if(removeEl){
31292             this.el.update("");
31293             this.el.remove();
31294         }
31295     }
31296 });
31297
31298 // private
31299 // hash to map config positions to true positions
31300 Roo.Resizable.positions = {
31301     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31302     hd: "hdrag"
31303 };
31304
31305 // private
31306 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31307     if(!this.tpl){
31308         // only initialize the template if resizable is used
31309         var tpl = Roo.DomHelper.createTemplate(
31310             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31311         );
31312         tpl.compile();
31313         Roo.Resizable.Handle.prototype.tpl = tpl;
31314     }
31315     this.position = pos;
31316     this.rz = rz;
31317     // show north drag fro topdra
31318     var handlepos = pos == 'hdrag' ? 'north' : pos;
31319     
31320     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31321     if (pos == 'hdrag') {
31322         this.el.setStyle('cursor', 'pointer');
31323     }
31324     this.el.unselectable();
31325     if(transparent){
31326         this.el.setOpacity(0);
31327     }
31328     this.el.on("mousedown", this.onMouseDown, this);
31329     if(!disableTrackOver){
31330         this.el.on("mouseover", this.onMouseOver, this);
31331         this.el.on("mouseout", this.onMouseOut, this);
31332     }
31333 };
31334
31335 // private
31336 Roo.Resizable.Handle.prototype = {
31337     afterResize : function(rz){
31338         Roo.log('after?');
31339         // do nothing
31340     },
31341     // private
31342     onMouseDown : function(e){
31343         this.rz.onMouseDown(this, e);
31344     },
31345     // private
31346     onMouseOver : function(e){
31347         this.rz.handleOver(this, e);
31348     },
31349     // private
31350     onMouseOut : function(e){
31351         this.rz.handleOut(this, e);
31352     }
31353 };/*
31354  * Based on:
31355  * Ext JS Library 1.1.1
31356  * Copyright(c) 2006-2007, Ext JS, LLC.
31357  *
31358  * Originally Released Under LGPL - original licence link has changed is not relivant.
31359  *
31360  * Fork - LGPL
31361  * <script type="text/javascript">
31362  */
31363
31364 /**
31365  * @class Roo.Editor
31366  * @extends Roo.Component
31367  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31368  * @constructor
31369  * Create a new Editor
31370  * @param {Roo.form.Field} field The Field object (or descendant)
31371  * @param {Object} config The config object
31372  */
31373 Roo.Editor = function(field, config){
31374     Roo.Editor.superclass.constructor.call(this, config);
31375     this.field = field;
31376     this.addEvents({
31377         /**
31378              * @event beforestartedit
31379              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31380              * false from the handler of this event.
31381              * @param {Editor} this
31382              * @param {Roo.Element} boundEl The underlying element bound to this editor
31383              * @param {Mixed} value The field value being set
31384              */
31385         "beforestartedit" : true,
31386         /**
31387              * @event startedit
31388              * Fires when this editor is displayed
31389              * @param {Roo.Element} boundEl The underlying element bound to this editor
31390              * @param {Mixed} value The starting field value
31391              */
31392         "startedit" : true,
31393         /**
31394              * @event beforecomplete
31395              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31396              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31397              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31398              * event will not fire since no edit actually occurred.
31399              * @param {Editor} this
31400              * @param {Mixed} value The current field value
31401              * @param {Mixed} startValue The original field value
31402              */
31403         "beforecomplete" : true,
31404         /**
31405              * @event complete
31406              * Fires after editing is complete and any changed value has been written to the underlying field.
31407              * @param {Editor} this
31408              * @param {Mixed} value The current field value
31409              * @param {Mixed} startValue The original field value
31410              */
31411         "complete" : true,
31412         /**
31413          * @event specialkey
31414          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31415          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31416          * @param {Roo.form.Field} this
31417          * @param {Roo.EventObject} e The event object
31418          */
31419         "specialkey" : true
31420     });
31421 };
31422
31423 Roo.extend(Roo.Editor, Roo.Component, {
31424     /**
31425      * @cfg {Boolean/String} autosize
31426      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31427      * or "height" to adopt the height only (defaults to false)
31428      */
31429     /**
31430      * @cfg {Boolean} revertInvalid
31431      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31432      * validation fails (defaults to true)
31433      */
31434     /**
31435      * @cfg {Boolean} ignoreNoChange
31436      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31437      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31438      * will never be ignored.
31439      */
31440     /**
31441      * @cfg {Boolean} hideEl
31442      * False to keep the bound element visible while the editor is displayed (defaults to true)
31443      */
31444     /**
31445      * @cfg {Mixed} value
31446      * The data value of the underlying field (defaults to "")
31447      */
31448     value : "",
31449     /**
31450      * @cfg {String} alignment
31451      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31452      */
31453     alignment: "c-c?",
31454     /**
31455      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31456      * for bottom-right shadow (defaults to "frame")
31457      */
31458     shadow : "frame",
31459     /**
31460      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31461      */
31462     constrain : false,
31463     /**
31464      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31465      */
31466     completeOnEnter : false,
31467     /**
31468      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31469      */
31470     cancelOnEsc : false,
31471     /**
31472      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31473      */
31474     updateEl : false,
31475
31476     // private
31477     onRender : function(ct, position){
31478         this.el = new Roo.Layer({
31479             shadow: this.shadow,
31480             cls: "x-editor",
31481             parentEl : ct,
31482             shim : this.shim,
31483             shadowOffset:4,
31484             id: this.id,
31485             constrain: this.constrain
31486         });
31487         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31488         if(this.field.msgTarget != 'title'){
31489             this.field.msgTarget = 'qtip';
31490         }
31491         this.field.render(this.el);
31492         if(Roo.isGecko){
31493             this.field.el.dom.setAttribute('autocomplete', 'off');
31494         }
31495         this.field.on("specialkey", this.onSpecialKey, this);
31496         if(this.swallowKeys){
31497             this.field.el.swallowEvent(['keydown','keypress']);
31498         }
31499         this.field.show();
31500         this.field.on("blur", this.onBlur, this);
31501         if(this.field.grow){
31502             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31503         }
31504     },
31505
31506     onSpecialKey : function(field, e)
31507     {
31508         //Roo.log('editor onSpecialKey');
31509         if(this.completeOnEnter && e.getKey() == e.ENTER){
31510             e.stopEvent();
31511             this.completeEdit();
31512             return;
31513         }
31514         // do not fire special key otherwise it might hide close the editor...
31515         if(e.getKey() == e.ENTER){    
31516             return;
31517         }
31518         if(this.cancelOnEsc && e.getKey() == e.ESC){
31519             this.cancelEdit();
31520             return;
31521         } 
31522         this.fireEvent('specialkey', field, e);
31523     
31524     },
31525
31526     /**
31527      * Starts the editing process and shows the editor.
31528      * @param {String/HTMLElement/Element} el The element to edit
31529      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31530       * to the innerHTML of el.
31531      */
31532     startEdit : function(el, value){
31533         if(this.editing){
31534             this.completeEdit();
31535         }
31536         this.boundEl = Roo.get(el);
31537         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31538         if(!this.rendered){
31539             this.render(this.parentEl || document.body);
31540         }
31541         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31542             return;
31543         }
31544         this.startValue = v;
31545         this.field.setValue(v);
31546         if(this.autoSize){
31547             var sz = this.boundEl.getSize();
31548             switch(this.autoSize){
31549                 case "width":
31550                 this.setSize(sz.width,  "");
31551                 break;
31552                 case "height":
31553                 this.setSize("",  sz.height);
31554                 break;
31555                 default:
31556                 this.setSize(sz.width,  sz.height);
31557             }
31558         }
31559         this.el.alignTo(this.boundEl, this.alignment);
31560         this.editing = true;
31561         if(Roo.QuickTips){
31562             Roo.QuickTips.disable();
31563         }
31564         this.show();
31565     },
31566
31567     /**
31568      * Sets the height and width of this editor.
31569      * @param {Number} width The new width
31570      * @param {Number} height The new height
31571      */
31572     setSize : function(w, h){
31573         this.field.setSize(w, h);
31574         if(this.el){
31575             this.el.sync();
31576         }
31577     },
31578
31579     /**
31580      * Realigns the editor to the bound field based on the current alignment config value.
31581      */
31582     realign : function(){
31583         this.el.alignTo(this.boundEl, this.alignment);
31584     },
31585
31586     /**
31587      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31588      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31589      */
31590     completeEdit : function(remainVisible){
31591         if(!this.editing){
31592             return;
31593         }
31594         var v = this.getValue();
31595         if(this.revertInvalid !== false && !this.field.isValid()){
31596             v = this.startValue;
31597             this.cancelEdit(true);
31598         }
31599         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31600             this.editing = false;
31601             this.hide();
31602             return;
31603         }
31604         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31605             this.editing = false;
31606             if(this.updateEl && this.boundEl){
31607                 this.boundEl.update(v);
31608             }
31609             if(remainVisible !== true){
31610                 this.hide();
31611             }
31612             this.fireEvent("complete", this, v, this.startValue);
31613         }
31614     },
31615
31616     // private
31617     onShow : function(){
31618         this.el.show();
31619         if(this.hideEl !== false){
31620             this.boundEl.hide();
31621         }
31622         this.field.show();
31623         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31624             this.fixIEFocus = true;
31625             this.deferredFocus.defer(50, this);
31626         }else{
31627             this.field.focus();
31628         }
31629         this.fireEvent("startedit", this.boundEl, this.startValue);
31630     },
31631
31632     deferredFocus : function(){
31633         if(this.editing){
31634             this.field.focus();
31635         }
31636     },
31637
31638     /**
31639      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31640      * reverted to the original starting value.
31641      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31642      * cancel (defaults to false)
31643      */
31644     cancelEdit : function(remainVisible){
31645         if(this.editing){
31646             this.setValue(this.startValue);
31647             if(remainVisible !== true){
31648                 this.hide();
31649             }
31650         }
31651     },
31652
31653     // private
31654     onBlur : function(){
31655         if(this.allowBlur !== true && this.editing){
31656             this.completeEdit();
31657         }
31658     },
31659
31660     // private
31661     onHide : function(){
31662         if(this.editing){
31663             this.completeEdit();
31664             return;
31665         }
31666         this.field.blur();
31667         if(this.field.collapse){
31668             this.field.collapse();
31669         }
31670         this.el.hide();
31671         if(this.hideEl !== false){
31672             this.boundEl.show();
31673         }
31674         if(Roo.QuickTips){
31675             Roo.QuickTips.enable();
31676         }
31677     },
31678
31679     /**
31680      * Sets the data value of the editor
31681      * @param {Mixed} value Any valid value supported by the underlying field
31682      */
31683     setValue : function(v){
31684         this.field.setValue(v);
31685     },
31686
31687     /**
31688      * Gets the data value of the editor
31689      * @return {Mixed} The data value
31690      */
31691     getValue : function(){
31692         return this.field.getValue();
31693     }
31694 });/*
31695  * Based on:
31696  * Ext JS Library 1.1.1
31697  * Copyright(c) 2006-2007, Ext JS, LLC.
31698  *
31699  * Originally Released Under LGPL - original licence link has changed is not relivant.
31700  *
31701  * Fork - LGPL
31702  * <script type="text/javascript">
31703  */
31704  
31705 /**
31706  * @class Roo.BasicDialog
31707  * @extends Roo.util.Observable
31708  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31709  * <pre><code>
31710 var dlg = new Roo.BasicDialog("my-dlg", {
31711     height: 200,
31712     width: 300,
31713     minHeight: 100,
31714     minWidth: 150,
31715     modal: true,
31716     proxyDrag: true,
31717     shadow: true
31718 });
31719 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31720 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31721 dlg.addButton('Cancel', dlg.hide, dlg);
31722 dlg.show();
31723 </code></pre>
31724   <b>A Dialog should always be a direct child of the body element.</b>
31725  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31726  * @cfg {String} title Default text to display in the title bar (defaults to null)
31727  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31728  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31729  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31730  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31731  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31732  * (defaults to null with no animation)
31733  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31734  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31735  * property for valid values (defaults to 'all')
31736  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31737  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31738  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31739  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31740  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31741  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31742  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31743  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31744  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31745  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31746  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31747  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31748  * draggable = true (defaults to false)
31749  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31750  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31751  * shadow (defaults to false)
31752  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31753  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31754  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31755  * @cfg {Array} buttons Array of buttons
31756  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31757  * @constructor
31758  * Create a new BasicDialog.
31759  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31760  * @param {Object} config Configuration options
31761  */
31762 Roo.BasicDialog = function(el, config){
31763     this.el = Roo.get(el);
31764     var dh = Roo.DomHelper;
31765     if(!this.el && config && config.autoCreate){
31766         if(typeof config.autoCreate == "object"){
31767             if(!config.autoCreate.id){
31768                 config.autoCreate.id = el;
31769             }
31770             this.el = dh.append(document.body,
31771                         config.autoCreate, true);
31772         }else{
31773             this.el = dh.append(document.body,
31774                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31775         }
31776     }
31777     el = this.el;
31778     el.setDisplayed(true);
31779     el.hide = this.hideAction;
31780     this.id = el.id;
31781     el.addClass("x-dlg");
31782
31783     Roo.apply(this, config);
31784
31785     this.proxy = el.createProxy("x-dlg-proxy");
31786     this.proxy.hide = this.hideAction;
31787     this.proxy.setOpacity(.5);
31788     this.proxy.hide();
31789
31790     if(config.width){
31791         el.setWidth(config.width);
31792     }
31793     if(config.height){
31794         el.setHeight(config.height);
31795     }
31796     this.size = el.getSize();
31797     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31798         this.xy = [config.x,config.y];
31799     }else{
31800         this.xy = el.getCenterXY(true);
31801     }
31802     /** The header element @type Roo.Element */
31803     this.header = el.child("> .x-dlg-hd");
31804     /** The body element @type Roo.Element */
31805     this.body = el.child("> .x-dlg-bd");
31806     /** The footer element @type Roo.Element */
31807     this.footer = el.child("> .x-dlg-ft");
31808
31809     if(!this.header){
31810         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31811     }
31812     if(!this.body){
31813         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31814     }
31815
31816     this.header.unselectable();
31817     if(this.title){
31818         this.header.update(this.title);
31819     }
31820     // this element allows the dialog to be focused for keyboard event
31821     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31822     this.focusEl.swallowEvent("click", true);
31823
31824     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31825
31826     // wrap the body and footer for special rendering
31827     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31828     if(this.footer){
31829         this.bwrap.dom.appendChild(this.footer.dom);
31830     }
31831
31832     this.bg = this.el.createChild({
31833         tag: "div", cls:"x-dlg-bg",
31834         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31835     });
31836     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31837
31838
31839     if(this.autoScroll !== false && !this.autoTabs){
31840         this.body.setStyle("overflow", "auto");
31841     }
31842
31843     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31844
31845     if(this.closable !== false){
31846         this.el.addClass("x-dlg-closable");
31847         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31848         this.close.on("click", this.closeClick, this);
31849         this.close.addClassOnOver("x-dlg-close-over");
31850     }
31851     if(this.collapsible !== false){
31852         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31853         this.collapseBtn.on("click", this.collapseClick, this);
31854         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31855         this.header.on("dblclick", this.collapseClick, this);
31856     }
31857     if(this.resizable !== false){
31858         this.el.addClass("x-dlg-resizable");
31859         this.resizer = new Roo.Resizable(el, {
31860             minWidth: this.minWidth || 80,
31861             minHeight:this.minHeight || 80,
31862             handles: this.resizeHandles || "all",
31863             pinned: true
31864         });
31865         this.resizer.on("beforeresize", this.beforeResize, this);
31866         this.resizer.on("resize", this.onResize, this);
31867     }
31868     if(this.draggable !== false){
31869         el.addClass("x-dlg-draggable");
31870         if (!this.proxyDrag) {
31871             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31872         }
31873         else {
31874             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31875         }
31876         dd.setHandleElId(this.header.id);
31877         dd.endDrag = this.endMove.createDelegate(this);
31878         dd.startDrag = this.startMove.createDelegate(this);
31879         dd.onDrag = this.onDrag.createDelegate(this);
31880         dd.scroll = false;
31881         this.dd = dd;
31882     }
31883     if(this.modal){
31884         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31885         this.mask.enableDisplayMode("block");
31886         this.mask.hide();
31887         this.el.addClass("x-dlg-modal");
31888     }
31889     if(this.shadow){
31890         this.shadow = new Roo.Shadow({
31891             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31892             offset : this.shadowOffset
31893         });
31894     }else{
31895         this.shadowOffset = 0;
31896     }
31897     if(Roo.useShims && this.shim !== false){
31898         this.shim = this.el.createShim();
31899         this.shim.hide = this.hideAction;
31900         this.shim.hide();
31901     }else{
31902         this.shim = false;
31903     }
31904     if(this.autoTabs){
31905         this.initTabs();
31906     }
31907     if (this.buttons) { 
31908         var bts= this.buttons;
31909         this.buttons = [];
31910         Roo.each(bts, function(b) {
31911             this.addButton(b);
31912         }, this);
31913     }
31914     
31915     
31916     this.addEvents({
31917         /**
31918          * @event keydown
31919          * Fires when a key is pressed
31920          * @param {Roo.BasicDialog} this
31921          * @param {Roo.EventObject} e
31922          */
31923         "keydown" : true,
31924         /**
31925          * @event move
31926          * Fires when this dialog is moved by the user.
31927          * @param {Roo.BasicDialog} this
31928          * @param {Number} x The new page X
31929          * @param {Number} y The new page Y
31930          */
31931         "move" : true,
31932         /**
31933          * @event resize
31934          * Fires when this dialog is resized by the user.
31935          * @param {Roo.BasicDialog} this
31936          * @param {Number} width The new width
31937          * @param {Number} height The new height
31938          */
31939         "resize" : true,
31940         /**
31941          * @event beforehide
31942          * Fires before this dialog is hidden.
31943          * @param {Roo.BasicDialog} this
31944          */
31945         "beforehide" : true,
31946         /**
31947          * @event hide
31948          * Fires when this dialog is hidden.
31949          * @param {Roo.BasicDialog} this
31950          */
31951         "hide" : true,
31952         /**
31953          * @event beforeshow
31954          * Fires before this dialog is shown.
31955          * @param {Roo.BasicDialog} this
31956          */
31957         "beforeshow" : true,
31958         /**
31959          * @event show
31960          * Fires when this dialog is shown.
31961          * @param {Roo.BasicDialog} this
31962          */
31963         "show" : true
31964     });
31965     el.on("keydown", this.onKeyDown, this);
31966     el.on("mousedown", this.toFront, this);
31967     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31968     this.el.hide();
31969     Roo.DialogManager.register(this);
31970     Roo.BasicDialog.superclass.constructor.call(this);
31971 };
31972
31973 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31974     shadowOffset: Roo.isIE ? 6 : 5,
31975     minHeight: 80,
31976     minWidth: 200,
31977     minButtonWidth: 75,
31978     defaultButton: null,
31979     buttonAlign: "right",
31980     tabTag: 'div',
31981     firstShow: true,
31982
31983     /**
31984      * Sets the dialog title text
31985      * @param {String} text The title text to display
31986      * @return {Roo.BasicDialog} this
31987      */
31988     setTitle : function(text){
31989         this.header.update(text);
31990         return this;
31991     },
31992
31993     // private
31994     closeClick : function(){
31995         this.hide();
31996     },
31997
31998     // private
31999     collapseClick : function(){
32000         this[this.collapsed ? "expand" : "collapse"]();
32001     },
32002
32003     /**
32004      * Collapses the dialog to its minimized state (only the title bar is visible).
32005      * Equivalent to the user clicking the collapse dialog button.
32006      */
32007     collapse : function(){
32008         if(!this.collapsed){
32009             this.collapsed = true;
32010             this.el.addClass("x-dlg-collapsed");
32011             this.restoreHeight = this.el.getHeight();
32012             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32013         }
32014     },
32015
32016     /**
32017      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32018      * clicking the expand dialog button.
32019      */
32020     expand : function(){
32021         if(this.collapsed){
32022             this.collapsed = false;
32023             this.el.removeClass("x-dlg-collapsed");
32024             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32025         }
32026     },
32027
32028     /**
32029      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32030      * @return {Roo.TabPanel} The tabs component
32031      */
32032     initTabs : function(){
32033         var tabs = this.getTabs();
32034         while(tabs.getTab(0)){
32035             tabs.removeTab(0);
32036         }
32037         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32038             var dom = el.dom;
32039             tabs.addTab(Roo.id(dom), dom.title);
32040             dom.title = "";
32041         });
32042         tabs.activate(0);
32043         return tabs;
32044     },
32045
32046     // private
32047     beforeResize : function(){
32048         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32049     },
32050
32051     // private
32052     onResize : function(){
32053         this.refreshSize();
32054         this.syncBodyHeight();
32055         this.adjustAssets();
32056         this.focus();
32057         this.fireEvent("resize", this, this.size.width, this.size.height);
32058     },
32059
32060     // private
32061     onKeyDown : function(e){
32062         if(this.isVisible()){
32063             this.fireEvent("keydown", this, e);
32064         }
32065     },
32066
32067     /**
32068      * Resizes the dialog.
32069      * @param {Number} width
32070      * @param {Number} height
32071      * @return {Roo.BasicDialog} this
32072      */
32073     resizeTo : function(width, height){
32074         this.el.setSize(width, height);
32075         this.size = {width: width, height: height};
32076         this.syncBodyHeight();
32077         if(this.fixedcenter){
32078             this.center();
32079         }
32080         if(this.isVisible()){
32081             this.constrainXY();
32082             this.adjustAssets();
32083         }
32084         this.fireEvent("resize", this, width, height);
32085         return this;
32086     },
32087
32088
32089     /**
32090      * Resizes the dialog to fit the specified content size.
32091      * @param {Number} width
32092      * @param {Number} height
32093      * @return {Roo.BasicDialog} this
32094      */
32095     setContentSize : function(w, h){
32096         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32097         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32098         //if(!this.el.isBorderBox()){
32099             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32100             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32101         //}
32102         if(this.tabs){
32103             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32104             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32105         }
32106         this.resizeTo(w, h);
32107         return this;
32108     },
32109
32110     /**
32111      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32112      * executed in response to a particular key being pressed while the dialog is active.
32113      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32114      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32115      * @param {Function} fn The function to call
32116      * @param {Object} scope (optional) The scope of the function
32117      * @return {Roo.BasicDialog} this
32118      */
32119     addKeyListener : function(key, fn, scope){
32120         var keyCode, shift, ctrl, alt;
32121         if(typeof key == "object" && !(key instanceof Array)){
32122             keyCode = key["key"];
32123             shift = key["shift"];
32124             ctrl = key["ctrl"];
32125             alt = key["alt"];
32126         }else{
32127             keyCode = key;
32128         }
32129         var handler = function(dlg, e){
32130             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32131                 var k = e.getKey();
32132                 if(keyCode instanceof Array){
32133                     for(var i = 0, len = keyCode.length; i < len; i++){
32134                         if(keyCode[i] == k){
32135                           fn.call(scope || window, dlg, k, e);
32136                           return;
32137                         }
32138                     }
32139                 }else{
32140                     if(k == keyCode){
32141                         fn.call(scope || window, dlg, k, e);
32142                     }
32143                 }
32144             }
32145         };
32146         this.on("keydown", handler);
32147         return this;
32148     },
32149
32150     /**
32151      * Returns the TabPanel component (creates it if it doesn't exist).
32152      * Note: If you wish to simply check for the existence of tabs without creating them,
32153      * check for a null 'tabs' property.
32154      * @return {Roo.TabPanel} The tabs component
32155      */
32156     getTabs : function(){
32157         if(!this.tabs){
32158             this.el.addClass("x-dlg-auto-tabs");
32159             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32160             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32161         }
32162         return this.tabs;
32163     },
32164
32165     /**
32166      * Adds a button to the footer section of the dialog.
32167      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32168      * object or a valid Roo.DomHelper element config
32169      * @param {Function} handler The function called when the button is clicked
32170      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32171      * @return {Roo.Button} The new button
32172      */
32173     addButton : function(config, handler, scope){
32174         var dh = Roo.DomHelper;
32175         if(!this.footer){
32176             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32177         }
32178         if(!this.btnContainer){
32179             var tb = this.footer.createChild({
32180
32181                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32182                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32183             }, null, true);
32184             this.btnContainer = tb.firstChild.firstChild.firstChild;
32185         }
32186         var bconfig = {
32187             handler: handler,
32188             scope: scope,
32189             minWidth: this.minButtonWidth,
32190             hideParent:true
32191         };
32192         if(typeof config == "string"){
32193             bconfig.text = config;
32194         }else{
32195             if(config.tag){
32196                 bconfig.dhconfig = config;
32197             }else{
32198                 Roo.apply(bconfig, config);
32199             }
32200         }
32201         var fc = false;
32202         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32203             bconfig.position = Math.max(0, bconfig.position);
32204             fc = this.btnContainer.childNodes[bconfig.position];
32205         }
32206          
32207         var btn = new Roo.Button(
32208             fc ? 
32209                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32210                 : this.btnContainer.appendChild(document.createElement("td")),
32211             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32212             bconfig
32213         );
32214         this.syncBodyHeight();
32215         if(!this.buttons){
32216             /**
32217              * Array of all the buttons that have been added to this dialog via addButton
32218              * @type Array
32219              */
32220             this.buttons = [];
32221         }
32222         this.buttons.push(btn);
32223         return btn;
32224     },
32225
32226     /**
32227      * Sets the default button to be focused when the dialog is displayed.
32228      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32229      * @return {Roo.BasicDialog} this
32230      */
32231     setDefaultButton : function(btn){
32232         this.defaultButton = btn;
32233         return this;
32234     },
32235
32236     // private
32237     getHeaderFooterHeight : function(safe){
32238         var height = 0;
32239         if(this.header){
32240            height += this.header.getHeight();
32241         }
32242         if(this.footer){
32243            var fm = this.footer.getMargins();
32244             height += (this.footer.getHeight()+fm.top+fm.bottom);
32245         }
32246         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32247         height += this.centerBg.getPadding("tb");
32248         return height;
32249     },
32250
32251     // private
32252     syncBodyHeight : function()
32253     {
32254         var bd = this.body, // the text
32255             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32256             bw = this.bwrap;
32257         var height = this.size.height - this.getHeaderFooterHeight(false);
32258         bd.setHeight(height-bd.getMargins("tb"));
32259         var hh = this.header.getHeight();
32260         var h = this.size.height-hh;
32261         cb.setHeight(h);
32262         
32263         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32264         bw.setHeight(h-cb.getPadding("tb"));
32265         
32266         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32267         bd.setWidth(bw.getWidth(true));
32268         if(this.tabs){
32269             this.tabs.syncHeight();
32270             if(Roo.isIE){
32271                 this.tabs.el.repaint();
32272             }
32273         }
32274     },
32275
32276     /**
32277      * Restores the previous state of the dialog if Roo.state is configured.
32278      * @return {Roo.BasicDialog} this
32279      */
32280     restoreState : function(){
32281         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32282         if(box && box.width){
32283             this.xy = [box.x, box.y];
32284             this.resizeTo(box.width, box.height);
32285         }
32286         return this;
32287     },
32288
32289     // private
32290     beforeShow : function(){
32291         this.expand();
32292         if(this.fixedcenter){
32293             this.xy = this.el.getCenterXY(true);
32294         }
32295         if(this.modal){
32296             Roo.get(document.body).addClass("x-body-masked");
32297             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32298             this.mask.show();
32299         }
32300         this.constrainXY();
32301     },
32302
32303     // private
32304     animShow : function(){
32305         var b = Roo.get(this.animateTarget).getBox();
32306         this.proxy.setSize(b.width, b.height);
32307         this.proxy.setLocation(b.x, b.y);
32308         this.proxy.show();
32309         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32310                     true, .35, this.showEl.createDelegate(this));
32311     },
32312
32313     /**
32314      * Shows the dialog.
32315      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32316      * @return {Roo.BasicDialog} this
32317      */
32318     show : function(animateTarget){
32319         if (this.fireEvent("beforeshow", this) === false){
32320             return;
32321         }
32322         if(this.syncHeightBeforeShow){
32323             this.syncBodyHeight();
32324         }else if(this.firstShow){
32325             this.firstShow = false;
32326             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32327         }
32328         this.animateTarget = animateTarget || this.animateTarget;
32329         if(!this.el.isVisible()){
32330             this.beforeShow();
32331             if(this.animateTarget && Roo.get(this.animateTarget)){
32332                 this.animShow();
32333             }else{
32334                 this.showEl();
32335             }
32336         }
32337         return this;
32338     },
32339
32340     // private
32341     showEl : function(){
32342         this.proxy.hide();
32343         this.el.setXY(this.xy);
32344         this.el.show();
32345         this.adjustAssets(true);
32346         this.toFront();
32347         this.focus();
32348         // IE peekaboo bug - fix found by Dave Fenwick
32349         if(Roo.isIE){
32350             this.el.repaint();
32351         }
32352         this.fireEvent("show", this);
32353     },
32354
32355     /**
32356      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32357      * dialog itself will receive focus.
32358      */
32359     focus : function(){
32360         if(this.defaultButton){
32361             this.defaultButton.focus();
32362         }else{
32363             this.focusEl.focus();
32364         }
32365     },
32366
32367     // private
32368     constrainXY : function(){
32369         if(this.constraintoviewport !== false){
32370             if(!this.viewSize){
32371                 if(this.container){
32372                     var s = this.container.getSize();
32373                     this.viewSize = [s.width, s.height];
32374                 }else{
32375                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32376                 }
32377             }
32378             var s = Roo.get(this.container||document).getScroll();
32379
32380             var x = this.xy[0], y = this.xy[1];
32381             var w = this.size.width, h = this.size.height;
32382             var vw = this.viewSize[0], vh = this.viewSize[1];
32383             // only move it if it needs it
32384             var moved = false;
32385             // first validate right/bottom
32386             if(x + w > vw+s.left){
32387                 x = vw - w;
32388                 moved = true;
32389             }
32390             if(y + h > vh+s.top){
32391                 y = vh - h;
32392                 moved = true;
32393             }
32394             // then make sure top/left isn't negative
32395             if(x < s.left){
32396                 x = s.left;
32397                 moved = true;
32398             }
32399             if(y < s.top){
32400                 y = s.top;
32401                 moved = true;
32402             }
32403             if(moved){
32404                 // cache xy
32405                 this.xy = [x, y];
32406                 if(this.isVisible()){
32407                     this.el.setLocation(x, y);
32408                     this.adjustAssets();
32409                 }
32410             }
32411         }
32412     },
32413
32414     // private
32415     onDrag : function(){
32416         if(!this.proxyDrag){
32417             this.xy = this.el.getXY();
32418             this.adjustAssets();
32419         }
32420     },
32421
32422     // private
32423     adjustAssets : function(doShow){
32424         var x = this.xy[0], y = this.xy[1];
32425         var w = this.size.width, h = this.size.height;
32426         if(doShow === true){
32427             if(this.shadow){
32428                 this.shadow.show(this.el);
32429             }
32430             if(this.shim){
32431                 this.shim.show();
32432             }
32433         }
32434         if(this.shadow && this.shadow.isVisible()){
32435             this.shadow.show(this.el);
32436         }
32437         if(this.shim && this.shim.isVisible()){
32438             this.shim.setBounds(x, y, w, h);
32439         }
32440     },
32441
32442     // private
32443     adjustViewport : function(w, h){
32444         if(!w || !h){
32445             w = Roo.lib.Dom.getViewWidth();
32446             h = Roo.lib.Dom.getViewHeight();
32447         }
32448         // cache the size
32449         this.viewSize = [w, h];
32450         if(this.modal && this.mask.isVisible()){
32451             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32452             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32453         }
32454         if(this.isVisible()){
32455             this.constrainXY();
32456         }
32457     },
32458
32459     /**
32460      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32461      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32462      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32463      */
32464     destroy : function(removeEl){
32465         if(this.isVisible()){
32466             this.animateTarget = null;
32467             this.hide();
32468         }
32469         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32470         if(this.tabs){
32471             this.tabs.destroy(removeEl);
32472         }
32473         Roo.destroy(
32474              this.shim,
32475              this.proxy,
32476              this.resizer,
32477              this.close,
32478              this.mask
32479         );
32480         if(this.dd){
32481             this.dd.unreg();
32482         }
32483         if(this.buttons){
32484            for(var i = 0, len = this.buttons.length; i < len; i++){
32485                this.buttons[i].destroy();
32486            }
32487         }
32488         this.el.removeAllListeners();
32489         if(removeEl === true){
32490             this.el.update("");
32491             this.el.remove();
32492         }
32493         Roo.DialogManager.unregister(this);
32494     },
32495
32496     // private
32497     startMove : function(){
32498         if(this.proxyDrag){
32499             this.proxy.show();
32500         }
32501         if(this.constraintoviewport !== false){
32502             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32503         }
32504     },
32505
32506     // private
32507     endMove : function(){
32508         if(!this.proxyDrag){
32509             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32510         }else{
32511             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32512             this.proxy.hide();
32513         }
32514         this.refreshSize();
32515         this.adjustAssets();
32516         this.focus();
32517         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32518     },
32519
32520     /**
32521      * Brings this dialog to the front of any other visible dialogs
32522      * @return {Roo.BasicDialog} this
32523      */
32524     toFront : function(){
32525         Roo.DialogManager.bringToFront(this);
32526         return this;
32527     },
32528
32529     /**
32530      * Sends this dialog to the back (under) of any other visible dialogs
32531      * @return {Roo.BasicDialog} this
32532      */
32533     toBack : function(){
32534         Roo.DialogManager.sendToBack(this);
32535         return this;
32536     },
32537
32538     /**
32539      * Centers this dialog in the viewport
32540      * @return {Roo.BasicDialog} this
32541      */
32542     center : function(){
32543         var xy = this.el.getCenterXY(true);
32544         this.moveTo(xy[0], xy[1]);
32545         return this;
32546     },
32547
32548     /**
32549      * Moves the dialog's top-left corner to the specified point
32550      * @param {Number} x
32551      * @param {Number} y
32552      * @return {Roo.BasicDialog} this
32553      */
32554     moveTo : function(x, y){
32555         this.xy = [x,y];
32556         if(this.isVisible()){
32557             this.el.setXY(this.xy);
32558             this.adjustAssets();
32559         }
32560         return this;
32561     },
32562
32563     /**
32564      * Aligns the dialog to the specified element
32565      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32566      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32567      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32568      * @return {Roo.BasicDialog} this
32569      */
32570     alignTo : function(element, position, offsets){
32571         this.xy = this.el.getAlignToXY(element, position, offsets);
32572         if(this.isVisible()){
32573             this.el.setXY(this.xy);
32574             this.adjustAssets();
32575         }
32576         return this;
32577     },
32578
32579     /**
32580      * Anchors an element to another element and realigns it when the window is resized.
32581      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32582      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32583      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32584      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32585      * is a number, it is used as the buffer delay (defaults to 50ms).
32586      * @return {Roo.BasicDialog} this
32587      */
32588     anchorTo : function(el, alignment, offsets, monitorScroll){
32589         var action = function(){
32590             this.alignTo(el, alignment, offsets);
32591         };
32592         Roo.EventManager.onWindowResize(action, this);
32593         var tm = typeof monitorScroll;
32594         if(tm != 'undefined'){
32595             Roo.EventManager.on(window, 'scroll', action, this,
32596                 {buffer: tm == 'number' ? monitorScroll : 50});
32597         }
32598         action.call(this);
32599         return this;
32600     },
32601
32602     /**
32603      * Returns true if the dialog is visible
32604      * @return {Boolean}
32605      */
32606     isVisible : function(){
32607         return this.el.isVisible();
32608     },
32609
32610     // private
32611     animHide : function(callback){
32612         var b = Roo.get(this.animateTarget).getBox();
32613         this.proxy.show();
32614         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32615         this.el.hide();
32616         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32617                     this.hideEl.createDelegate(this, [callback]));
32618     },
32619
32620     /**
32621      * Hides the dialog.
32622      * @param {Function} callback (optional) Function to call when the dialog is hidden
32623      * @return {Roo.BasicDialog} this
32624      */
32625     hide : function(callback){
32626         if (this.fireEvent("beforehide", this) === false){
32627             return;
32628         }
32629         if(this.shadow){
32630             this.shadow.hide();
32631         }
32632         if(this.shim) {
32633           this.shim.hide();
32634         }
32635         // sometimes animateTarget seems to get set.. causing problems...
32636         // this just double checks..
32637         if(this.animateTarget && Roo.get(this.animateTarget)) {
32638            this.animHide(callback);
32639         }else{
32640             this.el.hide();
32641             this.hideEl(callback);
32642         }
32643         return this;
32644     },
32645
32646     // private
32647     hideEl : function(callback){
32648         this.proxy.hide();
32649         if(this.modal){
32650             this.mask.hide();
32651             Roo.get(document.body).removeClass("x-body-masked");
32652         }
32653         this.fireEvent("hide", this);
32654         if(typeof callback == "function"){
32655             callback();
32656         }
32657     },
32658
32659     // private
32660     hideAction : function(){
32661         this.setLeft("-10000px");
32662         this.setTop("-10000px");
32663         this.setStyle("visibility", "hidden");
32664     },
32665
32666     // private
32667     refreshSize : function(){
32668         this.size = this.el.getSize();
32669         this.xy = this.el.getXY();
32670         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32671     },
32672
32673     // private
32674     // z-index is managed by the DialogManager and may be overwritten at any time
32675     setZIndex : function(index){
32676         if(this.modal){
32677             this.mask.setStyle("z-index", index);
32678         }
32679         if(this.shim){
32680             this.shim.setStyle("z-index", ++index);
32681         }
32682         if(this.shadow){
32683             this.shadow.setZIndex(++index);
32684         }
32685         this.el.setStyle("z-index", ++index);
32686         if(this.proxy){
32687             this.proxy.setStyle("z-index", ++index);
32688         }
32689         if(this.resizer){
32690             this.resizer.proxy.setStyle("z-index", ++index);
32691         }
32692
32693         this.lastZIndex = index;
32694     },
32695
32696     /**
32697      * Returns the element for this dialog
32698      * @return {Roo.Element} The underlying dialog Element
32699      */
32700     getEl : function(){
32701         return this.el;
32702     }
32703 });
32704
32705 /**
32706  * @class Roo.DialogManager
32707  * Provides global access to BasicDialogs that have been created and
32708  * support for z-indexing (layering) multiple open dialogs.
32709  */
32710 Roo.DialogManager = function(){
32711     var list = {};
32712     var accessList = [];
32713     var front = null;
32714
32715     // private
32716     var sortDialogs = function(d1, d2){
32717         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32718     };
32719
32720     // private
32721     var orderDialogs = function(){
32722         accessList.sort(sortDialogs);
32723         var seed = Roo.DialogManager.zseed;
32724         for(var i = 0, len = accessList.length; i < len; i++){
32725             var dlg = accessList[i];
32726             if(dlg){
32727                 dlg.setZIndex(seed + (i*10));
32728             }
32729         }
32730     };
32731
32732     return {
32733         /**
32734          * The starting z-index for BasicDialogs (defaults to 9000)
32735          * @type Number The z-index value
32736          */
32737         zseed : 9000,
32738
32739         // private
32740         register : function(dlg){
32741             list[dlg.id] = dlg;
32742             accessList.push(dlg);
32743         },
32744
32745         // private
32746         unregister : function(dlg){
32747             delete list[dlg.id];
32748             var i=0;
32749             var len=0;
32750             if(!accessList.indexOf){
32751                 for(  i = 0, len = accessList.length; i < len; i++){
32752                     if(accessList[i] == dlg){
32753                         accessList.splice(i, 1);
32754                         return;
32755                     }
32756                 }
32757             }else{
32758                  i = accessList.indexOf(dlg);
32759                 if(i != -1){
32760                     accessList.splice(i, 1);
32761                 }
32762             }
32763         },
32764
32765         /**
32766          * Gets a registered dialog by id
32767          * @param {String/Object} id The id of the dialog or a dialog
32768          * @return {Roo.BasicDialog} this
32769          */
32770         get : function(id){
32771             return typeof id == "object" ? id : list[id];
32772         },
32773
32774         /**
32775          * Brings the specified dialog to the front
32776          * @param {String/Object} dlg The id of the dialog or a dialog
32777          * @return {Roo.BasicDialog} this
32778          */
32779         bringToFront : function(dlg){
32780             dlg = this.get(dlg);
32781             if(dlg != front){
32782                 front = dlg;
32783                 dlg._lastAccess = new Date().getTime();
32784                 orderDialogs();
32785             }
32786             return dlg;
32787         },
32788
32789         /**
32790          * Sends the specified dialog to the back
32791          * @param {String/Object} dlg The id of the dialog or a dialog
32792          * @return {Roo.BasicDialog} this
32793          */
32794         sendToBack : function(dlg){
32795             dlg = this.get(dlg);
32796             dlg._lastAccess = -(new Date().getTime());
32797             orderDialogs();
32798             return dlg;
32799         },
32800
32801         /**
32802          * Hides all dialogs
32803          */
32804         hideAll : function(){
32805             for(var id in list){
32806                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32807                     list[id].hide();
32808                 }
32809             }
32810         }
32811     };
32812 }();
32813
32814 /**
32815  * @class Roo.LayoutDialog
32816  * @extends Roo.BasicDialog
32817  * Dialog which provides adjustments for working with a layout in a Dialog.
32818  * Add your necessary layout config options to the dialog's config.<br>
32819  * Example usage (including a nested layout):
32820  * <pre><code>
32821 if(!dialog){
32822     dialog = new Roo.LayoutDialog("download-dlg", {
32823         modal: true,
32824         width:600,
32825         height:450,
32826         shadow:true,
32827         minWidth:500,
32828         minHeight:350,
32829         autoTabs:true,
32830         proxyDrag:true,
32831         // layout config merges with the dialog config
32832         center:{
32833             tabPosition: "top",
32834             alwaysShowTabs: true
32835         }
32836     });
32837     dialog.addKeyListener(27, dialog.hide, dialog);
32838     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32839     dialog.addButton("Build It!", this.getDownload, this);
32840
32841     // we can even add nested layouts
32842     var innerLayout = new Roo.BorderLayout("dl-inner", {
32843         east: {
32844             initialSize: 200,
32845             autoScroll:true,
32846             split:true
32847         },
32848         center: {
32849             autoScroll:true
32850         }
32851     });
32852     innerLayout.beginUpdate();
32853     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32854     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32855     innerLayout.endUpdate(true);
32856
32857     var layout = dialog.getLayout();
32858     layout.beginUpdate();
32859     layout.add("center", new Roo.ContentPanel("standard-panel",
32860                         {title: "Download the Source", fitToFrame:true}));
32861     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32862                {title: "Build your own roo.js"}));
32863     layout.getRegion("center").showPanel(sp);
32864     layout.endUpdate();
32865 }
32866 </code></pre>
32867     * @constructor
32868     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32869     * @param {Object} config configuration options
32870   */
32871 Roo.LayoutDialog = function(el, cfg){
32872     
32873     var config=  cfg;
32874     if (typeof(cfg) == 'undefined') {
32875         config = Roo.apply({}, el);
32876         // not sure why we use documentElement here.. - it should always be body.
32877         // IE7 borks horribly if we use documentElement.
32878         // webkit also does not like documentElement - it creates a body element...
32879         el = Roo.get( document.body || document.documentElement ).createChild();
32880         //config.autoCreate = true;
32881     }
32882     
32883     
32884     config.autoTabs = false;
32885     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32886     this.body.setStyle({overflow:"hidden", position:"relative"});
32887     this.layout = new Roo.BorderLayout(this.body.dom, config);
32888     this.layout.monitorWindowResize = false;
32889     this.el.addClass("x-dlg-auto-layout");
32890     // fix case when center region overwrites center function
32891     this.center = Roo.BasicDialog.prototype.center;
32892     this.on("show", this.layout.layout, this.layout, true);
32893     if (config.items) {
32894         var xitems = config.items;
32895         delete config.items;
32896         Roo.each(xitems, this.addxtype, this);
32897     }
32898     
32899     
32900 };
32901 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32902     /**
32903      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32904      * @deprecated
32905      */
32906     endUpdate : function(){
32907         this.layout.endUpdate();
32908     },
32909
32910     /**
32911      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32912      *  @deprecated
32913      */
32914     beginUpdate : function(){
32915         this.layout.beginUpdate();
32916     },
32917
32918     /**
32919      * Get the BorderLayout for this dialog
32920      * @return {Roo.BorderLayout}
32921      */
32922     getLayout : function(){
32923         return this.layout;
32924     },
32925
32926     showEl : function(){
32927         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32928         if(Roo.isIE7){
32929             this.layout.layout();
32930         }
32931     },
32932
32933     // private
32934     // Use the syncHeightBeforeShow config option to control this automatically
32935     syncBodyHeight : function(){
32936         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32937         if(this.layout){this.layout.layout();}
32938     },
32939     
32940       /**
32941      * Add an xtype element (actually adds to the layout.)
32942      * @return {Object} xdata xtype object data.
32943      */
32944     
32945     addxtype : function(c) {
32946         return this.layout.addxtype(c);
32947     }
32948 });/*
32949  * Based on:
32950  * Ext JS Library 1.1.1
32951  * Copyright(c) 2006-2007, Ext JS, LLC.
32952  *
32953  * Originally Released Under LGPL - original licence link has changed is not relivant.
32954  *
32955  * Fork - LGPL
32956  * <script type="text/javascript">
32957  */
32958  
32959 /**
32960  * @class Roo.MessageBox
32961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32962  * Example usage:
32963  *<pre><code>
32964 // Basic alert:
32965 Roo.Msg.alert('Status', 'Changes saved successfully.');
32966
32967 // Prompt for user data:
32968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32969     if (btn == 'ok'){
32970         // process text value...
32971     }
32972 });
32973
32974 // Show a dialog using config options:
32975 Roo.Msg.show({
32976    title:'Save Changes?',
32977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32978    buttons: Roo.Msg.YESNOCANCEL,
32979    fn: processResult,
32980    animEl: 'elId'
32981 });
32982 </code></pre>
32983  * @singleton
32984  */
32985 Roo.MessageBox = function(){
32986     var dlg, opt, mask, waitTimer;
32987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32988     var buttons, activeTextEl, bwidth;
32989
32990     // private
32991     var handleButton = function(button){
32992         dlg.hide();
32993         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32994     };
32995
32996     // private
32997     var handleHide = function(){
32998         if(opt && opt.cls){
32999             dlg.el.removeClass(opt.cls);
33000         }
33001         if(waitTimer){
33002             Roo.TaskMgr.stop(waitTimer);
33003             waitTimer = null;
33004         }
33005     };
33006
33007     // private
33008     var updateButtons = function(b){
33009         var width = 0;
33010         if(!b){
33011             buttons["ok"].hide();
33012             buttons["cancel"].hide();
33013             buttons["yes"].hide();
33014             buttons["no"].hide();
33015             dlg.footer.dom.style.display = 'none';
33016             return width;
33017         }
33018         dlg.footer.dom.style.display = '';
33019         for(var k in buttons){
33020             if(typeof buttons[k] != "function"){
33021                 if(b[k]){
33022                     buttons[k].show();
33023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33024                     width += buttons[k].el.getWidth()+15;
33025                 }else{
33026                     buttons[k].hide();
33027                 }
33028             }
33029         }
33030         return width;
33031     };
33032
33033     // private
33034     var handleEsc = function(d, k, e){
33035         if(opt && opt.closable !== false){
33036             dlg.hide();
33037         }
33038         if(e){
33039             e.stopEvent();
33040         }
33041     };
33042
33043     return {
33044         /**
33045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33046          * @return {Roo.BasicDialog} The BasicDialog element
33047          */
33048         getDialog : function(){
33049            if(!dlg){
33050                 dlg = new Roo.BasicDialog("x-msg-box", {
33051                     autoCreate : true,
33052                     shadow: true,
33053                     draggable: true,
33054                     resizable:false,
33055                     constraintoviewport:false,
33056                     fixedcenter:true,
33057                     collapsible : false,
33058                     shim:true,
33059                     modal: true,
33060                     width:400, height:100,
33061                     buttonAlign:"center",
33062                     closeClick : function(){
33063                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33064                             handleButton("no");
33065                         }else{
33066                             handleButton("cancel");
33067                         }
33068                     }
33069                 });
33070                 dlg.on("hide", handleHide);
33071                 mask = dlg.mask;
33072                 dlg.addKeyListener(27, handleEsc);
33073                 buttons = {};
33074                 var bt = this.buttonText;
33075                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33076                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33077                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33078                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33079                 bodyEl = dlg.body.createChild({
33080
33081                     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>'
33082                 });
33083                 msgEl = bodyEl.dom.firstChild;
33084                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33085                 textboxEl.enableDisplayMode();
33086                 textboxEl.addKeyListener([10,13], function(){
33087                     if(dlg.isVisible() && opt && opt.buttons){
33088                         if(opt.buttons.ok){
33089                             handleButton("ok");
33090                         }else if(opt.buttons.yes){
33091                             handleButton("yes");
33092                         }
33093                     }
33094                 });
33095                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33096                 textareaEl.enableDisplayMode();
33097                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33098                 progressEl.enableDisplayMode();
33099                 var pf = progressEl.dom.firstChild;
33100                 if (pf) {
33101                     pp = Roo.get(pf.firstChild);
33102                     pp.setHeight(pf.offsetHeight);
33103                 }
33104                 
33105             }
33106             return dlg;
33107         },
33108
33109         /**
33110          * Updates the message box body text
33111          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33112          * the XHTML-compliant non-breaking space character '&amp;#160;')
33113          * @return {Roo.MessageBox} This message box
33114          */
33115         updateText : function(text){
33116             if(!dlg.isVisible() && !opt.width){
33117                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33118             }
33119             msgEl.innerHTML = text || '&#160;';
33120       
33121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33123             var w = Math.max(
33124                     Math.min(opt.width || cw , this.maxWidth), 
33125                     Math.max(opt.minWidth || this.minWidth, bwidth)
33126             );
33127             if(opt.prompt){
33128                 activeTextEl.setWidth(w);
33129             }
33130             if(dlg.isVisible()){
33131                 dlg.fixedcenter = false;
33132             }
33133             // to big, make it scroll. = But as usual stupid IE does not support
33134             // !important..
33135             
33136             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33137                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33138                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33139             } else {
33140                 bodyEl.dom.style.height = '';
33141                 bodyEl.dom.style.overflowY = '';
33142             }
33143             if (cw > w) {
33144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33145             } else {
33146                 bodyEl.dom.style.overflowX = '';
33147             }
33148             
33149             dlg.setContentSize(w, bodyEl.getHeight());
33150             if(dlg.isVisible()){
33151                 dlg.fixedcenter = true;
33152             }
33153             return this;
33154         },
33155
33156         /**
33157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33161          * @return {Roo.MessageBox} This message box
33162          */
33163         updateProgress : function(value, text){
33164             if(text){
33165                 this.updateText(text);
33166             }
33167             if (pp) { // weird bug on my firefox - for some reason this is not defined
33168                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33169             }
33170             return this;
33171         },        
33172
33173         /**
33174          * Returns true if the message box is currently displayed
33175          * @return {Boolean} True if the message box is visible, else false
33176          */
33177         isVisible : function(){
33178             return dlg && dlg.isVisible();  
33179         },
33180
33181         /**
33182          * Hides the message box if it is displayed
33183          */
33184         hide : function(){
33185             if(this.isVisible()){
33186                 dlg.hide();
33187             }  
33188         },
33189
33190         /**
33191          * Displays a new message box, or reinitializes an existing message box, based on the config options
33192          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33193          * The following config object properties are supported:
33194          * <pre>
33195 Property    Type             Description
33196 ----------  ---------------  ------------------------------------------------------------------------------------
33197 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33198                                    closes (defaults to undefined)
33199 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33200                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33201 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33202                                    progress and wait dialogs will ignore this property and always hide the
33203                                    close button as they can only be closed programmatically.
33204 cls               String           A custom CSS class to apply to the message box element
33205 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33206                                    displayed (defaults to 75)
33207 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33208                                    function will be btn (the name of the button that was clicked, if applicable,
33209                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33210                                    Progress and wait dialogs will ignore this option since they do not respond to
33211                                    user actions and can only be closed programmatically, so any required function
33212                                    should be called by the same code after it closes the dialog.
33213 icon              String           A CSS class that provides a background image to be used as an icon for
33214                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33215 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33216 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33217 modal             Boolean          False to allow user interaction with the page while the message box is
33218                                    displayed (defaults to true)
33219 msg               String           A string that will replace the existing message box body text (defaults
33220                                    to the XHTML-compliant non-breaking space character '&#160;')
33221 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33222 progress          Boolean          True to display a progress bar (defaults to false)
33223 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33224 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33225 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33226 title             String           The title text
33227 value             String           The string value to set into the active textbox element if displayed
33228 wait              Boolean          True to display a progress bar (defaults to false)
33229 width             Number           The width of the dialog in pixels
33230 </pre>
33231          *
33232          * Example usage:
33233          * <pre><code>
33234 Roo.Msg.show({
33235    title: 'Address',
33236    msg: 'Please enter your address:',
33237    width: 300,
33238    buttons: Roo.MessageBox.OKCANCEL,
33239    multiline: true,
33240    fn: saveAddress,
33241    animEl: 'addAddressBtn'
33242 });
33243 </code></pre>
33244          * @param {Object} config Configuration options
33245          * @return {Roo.MessageBox} This message box
33246          */
33247         show : function(options)
33248         {
33249             
33250             // this causes nightmares if you show one dialog after another
33251             // especially on callbacks..
33252              
33253             if(this.isVisible()){
33254                 
33255                 this.hide();
33256                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33257                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33258                 Roo.log("New Dialog Message:" +  options.msg )
33259                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33260                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33261                 
33262             }
33263             var d = this.getDialog();
33264             opt = options;
33265             d.setTitle(opt.title || "&#160;");
33266             d.close.setDisplayed(opt.closable !== false);
33267             activeTextEl = textboxEl;
33268             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33269             if(opt.prompt){
33270                 if(opt.multiline){
33271                     textboxEl.hide();
33272                     textareaEl.show();
33273                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33274                         opt.multiline : this.defaultTextHeight);
33275                     activeTextEl = textareaEl;
33276                 }else{
33277                     textboxEl.show();
33278                     textareaEl.hide();
33279                 }
33280             }else{
33281                 textboxEl.hide();
33282                 textareaEl.hide();
33283             }
33284             progressEl.setDisplayed(opt.progress === true);
33285             this.updateProgress(0);
33286             activeTextEl.dom.value = opt.value || "";
33287             if(opt.prompt){
33288                 dlg.setDefaultButton(activeTextEl);
33289             }else{
33290                 var bs = opt.buttons;
33291                 var db = null;
33292                 if(bs && bs.ok){
33293                     db = buttons["ok"];
33294                 }else if(bs && bs.yes){
33295                     db = buttons["yes"];
33296                 }
33297                 dlg.setDefaultButton(db);
33298             }
33299             bwidth = updateButtons(opt.buttons);
33300             this.updateText(opt.msg);
33301             if(opt.cls){
33302                 d.el.addClass(opt.cls);
33303             }
33304             d.proxyDrag = opt.proxyDrag === true;
33305             d.modal = opt.modal !== false;
33306             d.mask = opt.modal !== false ? mask : false;
33307             if(!d.isVisible()){
33308                 // force it to the end of the z-index stack so it gets a cursor in FF
33309                 document.body.appendChild(dlg.el.dom);
33310                 d.animateTarget = null;
33311                 d.show(options.animEl);
33312             }
33313             return this;
33314         },
33315
33316         /**
33317          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33318          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33319          * and closing the message box when the process is complete.
33320          * @param {String} title The title bar text
33321          * @param {String} msg The message box body text
33322          * @return {Roo.MessageBox} This message box
33323          */
33324         progress : function(title, msg){
33325             this.show({
33326                 title : title,
33327                 msg : msg,
33328                 buttons: false,
33329                 progress:true,
33330                 closable:false,
33331                 minWidth: this.minProgressWidth,
33332                 modal : true
33333             });
33334             return this;
33335         },
33336
33337         /**
33338          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33339          * If a callback function is passed it will be called after the user clicks the button, and the
33340          * id of the button that was clicked will be passed as the only parameter to the callback
33341          * (could also be the top-right close button).
33342          * @param {String} title The title bar text
33343          * @param {String} msg The message box body text
33344          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33345          * @param {Object} scope (optional) The scope of the callback function
33346          * @return {Roo.MessageBox} This message box
33347          */
33348         alert : function(title, msg, fn, scope){
33349             this.show({
33350                 title : title,
33351                 msg : msg,
33352                 buttons: this.OK,
33353                 fn: fn,
33354                 scope : scope,
33355                 modal : true
33356             });
33357             return this;
33358         },
33359
33360         /**
33361          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33362          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33363          * You are responsible for closing the message box when the process is complete.
33364          * @param {String} msg The message box body text
33365          * @param {String} title (optional) The title bar text
33366          * @return {Roo.MessageBox} This message box
33367          */
33368         wait : function(msg, title){
33369             this.show({
33370                 title : title,
33371                 msg : msg,
33372                 buttons: false,
33373                 closable:false,
33374                 progress:true,
33375                 modal:true,
33376                 width:300,
33377                 wait:true
33378             });
33379             waitTimer = Roo.TaskMgr.start({
33380                 run: function(i){
33381                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33382                 },
33383                 interval: 1000
33384             });
33385             return this;
33386         },
33387
33388         /**
33389          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33390          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33391          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33392          * @param {String} title The title bar text
33393          * @param {String} msg The message box body text
33394          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33395          * @param {Object} scope (optional) The scope of the callback function
33396          * @return {Roo.MessageBox} This message box
33397          */
33398         confirm : function(title, msg, fn, scope){
33399             this.show({
33400                 title : title,
33401                 msg : msg,
33402                 buttons: this.YESNO,
33403                 fn: fn,
33404                 scope : scope,
33405                 modal : true
33406             });
33407             return this;
33408         },
33409
33410         /**
33411          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33412          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33413          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33414          * (could also be the top-right close button) and the text that was entered will be passed as the two
33415          * parameters to the callback.
33416          * @param {String} title The title bar text
33417          * @param {String} msg The message box body text
33418          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33419          * @param {Object} scope (optional) The scope of the callback function
33420          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33421          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33422          * @return {Roo.MessageBox} This message box
33423          */
33424         prompt : function(title, msg, fn, scope, multiline){
33425             this.show({
33426                 title : title,
33427                 msg : msg,
33428                 buttons: this.OKCANCEL,
33429                 fn: fn,
33430                 minWidth:250,
33431                 scope : scope,
33432                 prompt:true,
33433                 multiline: multiline,
33434                 modal : true
33435             });
33436             return this;
33437         },
33438
33439         /**
33440          * Button config that displays a single OK button
33441          * @type Object
33442          */
33443         OK : {ok:true},
33444         /**
33445          * Button config that displays Yes and No buttons
33446          * @type Object
33447          */
33448         YESNO : {yes:true, no:true},
33449         /**
33450          * Button config that displays OK and Cancel buttons
33451          * @type Object
33452          */
33453         OKCANCEL : {ok:true, cancel:true},
33454         /**
33455          * Button config that displays Yes, No and Cancel buttons
33456          * @type Object
33457          */
33458         YESNOCANCEL : {yes:true, no:true, cancel:true},
33459
33460         /**
33461          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33462          * @type Number
33463          */
33464         defaultTextHeight : 75,
33465         /**
33466          * The maximum width in pixels of the message box (defaults to 600)
33467          * @type Number
33468          */
33469         maxWidth : 600,
33470         /**
33471          * The minimum width in pixels of the message box (defaults to 100)
33472          * @type Number
33473          */
33474         minWidth : 100,
33475         /**
33476          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33477          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33478          * @type Number
33479          */
33480         minProgressWidth : 250,
33481         /**
33482          * An object containing the default button text strings that can be overriden for localized language support.
33483          * Supported properties are: ok, cancel, yes and no.
33484          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33485          * @type Object
33486          */
33487         buttonText : {
33488             ok : "OK",
33489             cancel : "Cancel",
33490             yes : "Yes",
33491             no : "No"
33492         }
33493     };
33494 }();
33495
33496 /**
33497  * Shorthand for {@link Roo.MessageBox}
33498  */
33499 Roo.Msg = Roo.MessageBox;/*
33500  * Based on:
33501  * Ext JS Library 1.1.1
33502  * Copyright(c) 2006-2007, Ext JS, LLC.
33503  *
33504  * Originally Released Under LGPL - original licence link has changed is not relivant.
33505  *
33506  * Fork - LGPL
33507  * <script type="text/javascript">
33508  */
33509 /**
33510  * @class Roo.QuickTips
33511  * Provides attractive and customizable tooltips for any element.
33512  * @singleton
33513  */
33514 Roo.QuickTips = function(){
33515     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33516     var ce, bd, xy, dd;
33517     var visible = false, disabled = true, inited = false;
33518     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33519     
33520     var onOver = function(e){
33521         if(disabled){
33522             return;
33523         }
33524         var t = e.getTarget();
33525         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33526             return;
33527         }
33528         if(ce && t == ce.el){
33529             clearTimeout(hideProc);
33530             return;
33531         }
33532         if(t && tagEls[t.id]){
33533             tagEls[t.id].el = t;
33534             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33535             return;
33536         }
33537         var ttp, et = Roo.fly(t);
33538         var ns = cfg.namespace;
33539         if(tm.interceptTitles && t.title){
33540             ttp = t.title;
33541             t.qtip = ttp;
33542             t.removeAttribute("title");
33543             e.preventDefault();
33544         }else{
33545             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33546         }
33547         if(ttp){
33548             showProc = show.defer(tm.showDelay, tm, [{
33549                 el: t, 
33550                 text: ttp, 
33551                 width: et.getAttributeNS(ns, cfg.width),
33552                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33553                 title: et.getAttributeNS(ns, cfg.title),
33554                     cls: et.getAttributeNS(ns, cfg.cls)
33555             }]);
33556         }
33557     };
33558     
33559     var onOut = function(e){
33560         clearTimeout(showProc);
33561         var t = e.getTarget();
33562         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33563             hideProc = setTimeout(hide, tm.hideDelay);
33564         }
33565     };
33566     
33567     var onMove = function(e){
33568         if(disabled){
33569             return;
33570         }
33571         xy = e.getXY();
33572         xy[1] += 18;
33573         if(tm.trackMouse && ce){
33574             el.setXY(xy);
33575         }
33576     };
33577     
33578     var onDown = function(e){
33579         clearTimeout(showProc);
33580         clearTimeout(hideProc);
33581         if(!e.within(el)){
33582             if(tm.hideOnClick){
33583                 hide();
33584                 tm.disable();
33585                 tm.enable.defer(100, tm);
33586             }
33587         }
33588     };
33589     
33590     var getPad = function(){
33591         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33592     };
33593
33594     var show = function(o){
33595         if(disabled){
33596             return;
33597         }
33598         clearTimeout(dismissProc);
33599         ce = o;
33600         if(removeCls){ // in case manually hidden
33601             el.removeClass(removeCls);
33602             removeCls = null;
33603         }
33604         if(ce.cls){
33605             el.addClass(ce.cls);
33606             removeCls = ce.cls;
33607         }
33608         if(ce.title){
33609             tipTitle.update(ce.title);
33610             tipTitle.show();
33611         }else{
33612             tipTitle.update('');
33613             tipTitle.hide();
33614         }
33615         el.dom.style.width  = tm.maxWidth+'px';
33616         //tipBody.dom.style.width = '';
33617         tipBodyText.update(o.text);
33618         var p = getPad(), w = ce.width;
33619         if(!w){
33620             var td = tipBodyText.dom;
33621             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33622             if(aw > tm.maxWidth){
33623                 w = tm.maxWidth;
33624             }else if(aw < tm.minWidth){
33625                 w = tm.minWidth;
33626             }else{
33627                 w = aw;
33628             }
33629         }
33630         //tipBody.setWidth(w);
33631         el.setWidth(parseInt(w, 10) + p);
33632         if(ce.autoHide === false){
33633             close.setDisplayed(true);
33634             if(dd){
33635                 dd.unlock();
33636             }
33637         }else{
33638             close.setDisplayed(false);
33639             if(dd){
33640                 dd.lock();
33641             }
33642         }
33643         if(xy){
33644             el.avoidY = xy[1]-18;
33645             el.setXY(xy);
33646         }
33647         if(tm.animate){
33648             el.setOpacity(.1);
33649             el.setStyle("visibility", "visible");
33650             el.fadeIn({callback: afterShow});
33651         }else{
33652             afterShow();
33653         }
33654     };
33655     
33656     var afterShow = function(){
33657         if(ce){
33658             el.show();
33659             esc.enable();
33660             if(tm.autoDismiss && ce.autoHide !== false){
33661                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33662             }
33663         }
33664     };
33665     
33666     var hide = function(noanim){
33667         clearTimeout(dismissProc);
33668         clearTimeout(hideProc);
33669         ce = null;
33670         if(el.isVisible()){
33671             esc.disable();
33672             if(noanim !== true && tm.animate){
33673                 el.fadeOut({callback: afterHide});
33674             }else{
33675                 afterHide();
33676             } 
33677         }
33678     };
33679     
33680     var afterHide = function(){
33681         el.hide();
33682         if(removeCls){
33683             el.removeClass(removeCls);
33684             removeCls = null;
33685         }
33686     };
33687     
33688     return {
33689         /**
33690         * @cfg {Number} minWidth
33691         * The minimum width of the quick tip (defaults to 40)
33692         */
33693        minWidth : 40,
33694         /**
33695         * @cfg {Number} maxWidth
33696         * The maximum width of the quick tip (defaults to 300)
33697         */
33698        maxWidth : 300,
33699         /**
33700         * @cfg {Boolean} interceptTitles
33701         * True to automatically use the element's DOM title value if available (defaults to false)
33702         */
33703        interceptTitles : false,
33704         /**
33705         * @cfg {Boolean} trackMouse
33706         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33707         */
33708        trackMouse : false,
33709         /**
33710         * @cfg {Boolean} hideOnClick
33711         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33712         */
33713        hideOnClick : true,
33714         /**
33715         * @cfg {Number} showDelay
33716         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33717         */
33718        showDelay : 500,
33719         /**
33720         * @cfg {Number} hideDelay
33721         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33722         */
33723        hideDelay : 200,
33724         /**
33725         * @cfg {Boolean} autoHide
33726         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33727         * Used in conjunction with hideDelay.
33728         */
33729        autoHide : true,
33730         /**
33731         * @cfg {Boolean}
33732         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33733         * (defaults to true).  Used in conjunction with autoDismissDelay.
33734         */
33735        autoDismiss : true,
33736         /**
33737         * @cfg {Number}
33738         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33739         */
33740        autoDismissDelay : 5000,
33741        /**
33742         * @cfg {Boolean} animate
33743         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33744         */
33745        animate : false,
33746
33747        /**
33748         * @cfg {String} title
33749         * Title text to display (defaults to '').  This can be any valid HTML markup.
33750         */
33751         title: '',
33752        /**
33753         * @cfg {String} text
33754         * Body text to display (defaults to '').  This can be any valid HTML markup.
33755         */
33756         text : '',
33757        /**
33758         * @cfg {String} cls
33759         * A CSS class to apply to the base quick tip element (defaults to '').
33760         */
33761         cls : '',
33762        /**
33763         * @cfg {Number} width
33764         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33765         * minWidth or maxWidth.
33766         */
33767         width : null,
33768
33769     /**
33770      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33771      * or display QuickTips in a page.
33772      */
33773        init : function(){
33774           tm = Roo.QuickTips;
33775           cfg = tm.tagConfig;
33776           if(!inited){
33777               if(!Roo.isReady){ // allow calling of init() before onReady
33778                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33779                   return;
33780               }
33781               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33782               el.fxDefaults = {stopFx: true};
33783               // maximum custom styling
33784               //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>');
33785               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>');              
33786               tipTitle = el.child('h3');
33787               tipTitle.enableDisplayMode("block");
33788               tipBody = el.child('div.x-tip-bd');
33789               tipBodyText = el.child('div.x-tip-bd-inner');
33790               //bdLeft = el.child('div.x-tip-bd-left');
33791               //bdRight = el.child('div.x-tip-bd-right');
33792               close = el.child('div.x-tip-close');
33793               close.enableDisplayMode("block");
33794               close.on("click", hide);
33795               var d = Roo.get(document);
33796               d.on("mousedown", onDown);
33797               d.on("mouseover", onOver);
33798               d.on("mouseout", onOut);
33799               d.on("mousemove", onMove);
33800               esc = d.addKeyListener(27, hide);
33801               esc.disable();
33802               if(Roo.dd.DD){
33803                   dd = el.initDD("default", null, {
33804                       onDrag : function(){
33805                           el.sync();  
33806                       }
33807                   });
33808                   dd.setHandleElId(tipTitle.id);
33809                   dd.lock();
33810               }
33811               inited = true;
33812           }
33813           this.enable(); 
33814        },
33815
33816     /**
33817      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33818      * are supported:
33819      * <pre>
33820 Property    Type                   Description
33821 ----------  ---------------------  ------------------------------------------------------------------------
33822 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33823      * </ul>
33824      * @param {Object} config The config object
33825      */
33826        register : function(config){
33827            var cs = config instanceof Array ? config : arguments;
33828            for(var i = 0, len = cs.length; i < len; i++) {
33829                var c = cs[i];
33830                var target = c.target;
33831                if(target){
33832                    if(target instanceof Array){
33833                        for(var j = 0, jlen = target.length; j < jlen; j++){
33834                            tagEls[target[j]] = c;
33835                        }
33836                    }else{
33837                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33838                    }
33839                }
33840            }
33841        },
33842
33843     /**
33844      * Removes this quick tip from its element and destroys it.
33845      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33846      */
33847        unregister : function(el){
33848            delete tagEls[Roo.id(el)];
33849        },
33850
33851     /**
33852      * Enable this quick tip.
33853      */
33854        enable : function(){
33855            if(inited && disabled){
33856                locks.pop();
33857                if(locks.length < 1){
33858                    disabled = false;
33859                }
33860            }
33861        },
33862
33863     /**
33864      * Disable this quick tip.
33865      */
33866        disable : function(){
33867           disabled = true;
33868           clearTimeout(showProc);
33869           clearTimeout(hideProc);
33870           clearTimeout(dismissProc);
33871           if(ce){
33872               hide(true);
33873           }
33874           locks.push(1);
33875        },
33876
33877     /**
33878      * Returns true if the quick tip is enabled, else false.
33879      */
33880        isEnabled : function(){
33881             return !disabled;
33882        },
33883
33884         // private
33885        tagConfig : {
33886            namespace : "roo", // was ext?? this may break..
33887            alt_namespace : "ext",
33888            attribute : "qtip",
33889            width : "width",
33890            target : "target",
33891            title : "qtitle",
33892            hide : "hide",
33893            cls : "qclass"
33894        }
33895    };
33896 }();
33897
33898 // backwards compat
33899 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33900  * Based on:
33901  * Ext JS Library 1.1.1
33902  * Copyright(c) 2006-2007, Ext JS, LLC.
33903  *
33904  * Originally Released Under LGPL - original licence link has changed is not relivant.
33905  *
33906  * Fork - LGPL
33907  * <script type="text/javascript">
33908  */
33909  
33910
33911 /**
33912  * @class Roo.tree.TreePanel
33913  * @extends Roo.data.Tree
33914
33915  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33916  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33917  * @cfg {Boolean} enableDD true to enable drag and drop
33918  * @cfg {Boolean} enableDrag true to enable just drag
33919  * @cfg {Boolean} enableDrop true to enable just drop
33920  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33921  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33922  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33923  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33924  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33925  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33926  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33927  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33928  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33929  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33930  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33931  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33932  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33933  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33934  * @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>
33935  * @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>
33936  * 
33937  * @constructor
33938  * @param {String/HTMLElement/Element} el The container element
33939  * @param {Object} config
33940  */
33941 Roo.tree.TreePanel = function(el, config){
33942     var root = false;
33943     var loader = false;
33944     if (config.root) {
33945         root = config.root;
33946         delete config.root;
33947     }
33948     if (config.loader) {
33949         loader = config.loader;
33950         delete config.loader;
33951     }
33952     
33953     Roo.apply(this, config);
33954     Roo.tree.TreePanel.superclass.constructor.call(this);
33955     this.el = Roo.get(el);
33956     this.el.addClass('x-tree');
33957     //console.log(root);
33958     if (root) {
33959         this.setRootNode( Roo.factory(root, Roo.tree));
33960     }
33961     if (loader) {
33962         this.loader = Roo.factory(loader, Roo.tree);
33963     }
33964    /**
33965     * Read-only. The id of the container element becomes this TreePanel's id.
33966     */
33967     this.id = this.el.id;
33968     this.addEvents({
33969         /**
33970         * @event beforeload
33971         * Fires before a node is loaded, return false to cancel
33972         * @param {Node} node The node being loaded
33973         */
33974         "beforeload" : true,
33975         /**
33976         * @event load
33977         * Fires when a node is loaded
33978         * @param {Node} node The node that was loaded
33979         */
33980         "load" : true,
33981         /**
33982         * @event textchange
33983         * Fires when the text for a node is changed
33984         * @param {Node} node The node
33985         * @param {String} text The new text
33986         * @param {String} oldText The old text
33987         */
33988         "textchange" : true,
33989         /**
33990         * @event beforeexpand
33991         * Fires before a node is expanded, return false to cancel.
33992         * @param {Node} node The node
33993         * @param {Boolean} deep
33994         * @param {Boolean} anim
33995         */
33996         "beforeexpand" : true,
33997         /**
33998         * @event beforecollapse
33999         * Fires before a node is collapsed, return false to cancel.
34000         * @param {Node} node The node
34001         * @param {Boolean} deep
34002         * @param {Boolean} anim
34003         */
34004         "beforecollapse" : true,
34005         /**
34006         * @event expand
34007         * Fires when a node is expanded
34008         * @param {Node} node The node
34009         */
34010         "expand" : true,
34011         /**
34012         * @event disabledchange
34013         * Fires when the disabled status of a node changes
34014         * @param {Node} node The node
34015         * @param {Boolean} disabled
34016         */
34017         "disabledchange" : true,
34018         /**
34019         * @event collapse
34020         * Fires when a node is collapsed
34021         * @param {Node} node The node
34022         */
34023         "collapse" : true,
34024         /**
34025         * @event beforeclick
34026         * Fires before click processing on a node. Return false to cancel the default action.
34027         * @param {Node} node The node
34028         * @param {Roo.EventObject} e The event object
34029         */
34030         "beforeclick":true,
34031         /**
34032         * @event checkchange
34033         * Fires when a node with a checkbox's checked property changes
34034         * @param {Node} this This node
34035         * @param {Boolean} checked
34036         */
34037         "checkchange":true,
34038         /**
34039         * @event click
34040         * Fires when a node is clicked
34041         * @param {Node} node The node
34042         * @param {Roo.EventObject} e The event object
34043         */
34044         "click":true,
34045         /**
34046         * @event dblclick
34047         * Fires when a node is double clicked
34048         * @param {Node} node The node
34049         * @param {Roo.EventObject} e The event object
34050         */
34051         "dblclick":true,
34052         /**
34053         * @event contextmenu
34054         * Fires when a node is right clicked
34055         * @param {Node} node The node
34056         * @param {Roo.EventObject} e The event object
34057         */
34058         "contextmenu":true,
34059         /**
34060         * @event beforechildrenrendered
34061         * Fires right before the child nodes for a node are rendered
34062         * @param {Node} node The node
34063         */
34064         "beforechildrenrendered":true,
34065         /**
34066         * @event startdrag
34067         * Fires when a node starts being dragged
34068         * @param {Roo.tree.TreePanel} this
34069         * @param {Roo.tree.TreeNode} node
34070         * @param {event} e The raw browser event
34071         */ 
34072        "startdrag" : true,
34073        /**
34074         * @event enddrag
34075         * Fires when a drag operation is complete
34076         * @param {Roo.tree.TreePanel} this
34077         * @param {Roo.tree.TreeNode} node
34078         * @param {event} e The raw browser event
34079         */
34080        "enddrag" : true,
34081        /**
34082         * @event dragdrop
34083         * Fires when a dragged node is dropped on a valid DD target
34084         * @param {Roo.tree.TreePanel} this
34085         * @param {Roo.tree.TreeNode} node
34086         * @param {DD} dd The dd it was dropped on
34087         * @param {event} e The raw browser event
34088         */
34089        "dragdrop" : true,
34090        /**
34091         * @event beforenodedrop
34092         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34093         * passed to handlers has the following properties:<br />
34094         * <ul style="padding:5px;padding-left:16px;">
34095         * <li>tree - The TreePanel</li>
34096         * <li>target - The node being targeted for the drop</li>
34097         * <li>data - The drag data from the drag source</li>
34098         * <li>point - The point of the drop - append, above or below</li>
34099         * <li>source - The drag source</li>
34100         * <li>rawEvent - Raw mouse event</li>
34101         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34102         * to be inserted by setting them on this object.</li>
34103         * <li>cancel - Set this to true to cancel the drop.</li>
34104         * </ul>
34105         * @param {Object} dropEvent
34106         */
34107        "beforenodedrop" : true,
34108        /**
34109         * @event nodedrop
34110         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34111         * passed to handlers has the following properties:<br />
34112         * <ul style="padding:5px;padding-left:16px;">
34113         * <li>tree - The TreePanel</li>
34114         * <li>target - The node being targeted for the drop</li>
34115         * <li>data - The drag data from the drag source</li>
34116         * <li>point - The point of the drop - append, above or below</li>
34117         * <li>source - The drag source</li>
34118         * <li>rawEvent - Raw mouse event</li>
34119         * <li>dropNode - Dropped node(s).</li>
34120         * </ul>
34121         * @param {Object} dropEvent
34122         */
34123        "nodedrop" : true,
34124         /**
34125         * @event nodedragover
34126         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34127         * passed to handlers has the following properties:<br />
34128         * <ul style="padding:5px;padding-left:16px;">
34129         * <li>tree - The TreePanel</li>
34130         * <li>target - The node being targeted for the drop</li>
34131         * <li>data - The drag data from the drag source</li>
34132         * <li>point - The point of the drop - append, above or below</li>
34133         * <li>source - The drag source</li>
34134         * <li>rawEvent - Raw mouse event</li>
34135         * <li>dropNode - Drop node(s) provided by the source.</li>
34136         * <li>cancel - Set this to true to signal drop not allowed.</li>
34137         * </ul>
34138         * @param {Object} dragOverEvent
34139         */
34140        "nodedragover" : true
34141         
34142     });
34143     if(this.singleExpand){
34144        this.on("beforeexpand", this.restrictExpand, this);
34145     }
34146     if (this.editor) {
34147         this.editor.tree = this;
34148         this.editor = Roo.factory(this.editor, Roo.tree);
34149     }
34150     
34151     if (this.selModel) {
34152         this.selModel = Roo.factory(this.selModel, Roo.tree);
34153     }
34154    
34155 };
34156 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34157     rootVisible : true,
34158     animate: Roo.enableFx,
34159     lines : true,
34160     enableDD : false,
34161     hlDrop : Roo.enableFx,
34162   
34163     renderer: false,
34164     
34165     rendererTip: false,
34166     // private
34167     restrictExpand : function(node){
34168         var p = node.parentNode;
34169         if(p){
34170             if(p.expandedChild && p.expandedChild.parentNode == p){
34171                 p.expandedChild.collapse();
34172             }
34173             p.expandedChild = node;
34174         }
34175     },
34176
34177     // private override
34178     setRootNode : function(node){
34179         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34180         if(!this.rootVisible){
34181             node.ui = new Roo.tree.RootTreeNodeUI(node);
34182         }
34183         return node;
34184     },
34185
34186     /**
34187      * Returns the container element for this TreePanel
34188      */
34189     getEl : function(){
34190         return this.el;
34191     },
34192
34193     /**
34194      * Returns the default TreeLoader for this TreePanel
34195      */
34196     getLoader : function(){
34197         return this.loader;
34198     },
34199
34200     /**
34201      * Expand all nodes
34202      */
34203     expandAll : function(){
34204         this.root.expand(true);
34205     },
34206
34207     /**
34208      * Collapse all nodes
34209      */
34210     collapseAll : function(){
34211         this.root.collapse(true);
34212     },
34213
34214     /**
34215      * Returns the selection model used by this TreePanel
34216      */
34217     getSelectionModel : function(){
34218         if(!this.selModel){
34219             this.selModel = new Roo.tree.DefaultSelectionModel();
34220         }
34221         return this.selModel;
34222     },
34223
34224     /**
34225      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34226      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34227      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34228      * @return {Array}
34229      */
34230     getChecked : function(a, startNode){
34231         startNode = startNode || this.root;
34232         var r = [];
34233         var f = function(){
34234             if(this.attributes.checked){
34235                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34236             }
34237         }
34238         startNode.cascade(f);
34239         return r;
34240     },
34241
34242     /**
34243      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34244      * @param {String} path
34245      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34246      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34247      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34248      */
34249     expandPath : function(path, attr, callback){
34250         attr = attr || "id";
34251         var keys = path.split(this.pathSeparator);
34252         var curNode = this.root;
34253         if(curNode.attributes[attr] != keys[1]){ // invalid root
34254             if(callback){
34255                 callback(false, null);
34256             }
34257             return;
34258         }
34259         var index = 1;
34260         var f = function(){
34261             if(++index == keys.length){
34262                 if(callback){
34263                     callback(true, curNode);
34264                 }
34265                 return;
34266             }
34267             var c = curNode.findChild(attr, keys[index]);
34268             if(!c){
34269                 if(callback){
34270                     callback(false, curNode);
34271                 }
34272                 return;
34273             }
34274             curNode = c;
34275             c.expand(false, false, f);
34276         };
34277         curNode.expand(false, false, f);
34278     },
34279
34280     /**
34281      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34282      * @param {String} path
34283      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34284      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34285      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34286      */
34287     selectPath : function(path, attr, callback){
34288         attr = attr || "id";
34289         var keys = path.split(this.pathSeparator);
34290         var v = keys.pop();
34291         if(keys.length > 0){
34292             var f = function(success, node){
34293                 if(success && node){
34294                     var n = node.findChild(attr, v);
34295                     if(n){
34296                         n.select();
34297                         if(callback){
34298                             callback(true, n);
34299                         }
34300                     }else if(callback){
34301                         callback(false, n);
34302                     }
34303                 }else{
34304                     if(callback){
34305                         callback(false, n);
34306                     }
34307                 }
34308             };
34309             this.expandPath(keys.join(this.pathSeparator), attr, f);
34310         }else{
34311             this.root.select();
34312             if(callback){
34313                 callback(true, this.root);
34314             }
34315         }
34316     },
34317
34318     getTreeEl : function(){
34319         return this.el;
34320     },
34321
34322     /**
34323      * Trigger rendering of this TreePanel
34324      */
34325     render : function(){
34326         if (this.innerCt) {
34327             return this; // stop it rendering more than once!!
34328         }
34329         
34330         this.innerCt = this.el.createChild({tag:"ul",
34331                cls:"x-tree-root-ct " +
34332                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34333
34334         if(this.containerScroll){
34335             Roo.dd.ScrollManager.register(this.el);
34336         }
34337         if((this.enableDD || this.enableDrop) && !this.dropZone){
34338            /**
34339             * The dropZone used by this tree if drop is enabled
34340             * @type Roo.tree.TreeDropZone
34341             */
34342              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34343                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34344            });
34345         }
34346         if((this.enableDD || this.enableDrag) && !this.dragZone){
34347            /**
34348             * The dragZone used by this tree if drag is enabled
34349             * @type Roo.tree.TreeDragZone
34350             */
34351             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34352                ddGroup: this.ddGroup || "TreeDD",
34353                scroll: this.ddScroll
34354            });
34355         }
34356         this.getSelectionModel().init(this);
34357         if (!this.root) {
34358             Roo.log("ROOT not set in tree");
34359             return this;
34360         }
34361         this.root.render();
34362         if(!this.rootVisible){
34363             this.root.renderChildren();
34364         }
34365         return this;
34366     }
34367 });/*
34368  * Based on:
34369  * Ext JS Library 1.1.1
34370  * Copyright(c) 2006-2007, Ext JS, LLC.
34371  *
34372  * Originally Released Under LGPL - original licence link has changed is not relivant.
34373  *
34374  * Fork - LGPL
34375  * <script type="text/javascript">
34376  */
34377  
34378
34379 /**
34380  * @class Roo.tree.DefaultSelectionModel
34381  * @extends Roo.util.Observable
34382  * The default single selection for a TreePanel.
34383  * @param {Object} cfg Configuration
34384  */
34385 Roo.tree.DefaultSelectionModel = function(cfg){
34386    this.selNode = null;
34387    
34388    
34389    
34390    this.addEvents({
34391        /**
34392         * @event selectionchange
34393         * Fires when the selected node changes
34394         * @param {DefaultSelectionModel} this
34395         * @param {TreeNode} node the new selection
34396         */
34397        "selectionchange" : true,
34398
34399        /**
34400         * @event beforeselect
34401         * Fires before the selected node changes, return false to cancel the change
34402         * @param {DefaultSelectionModel} this
34403         * @param {TreeNode} node the new selection
34404         * @param {TreeNode} node the old selection
34405         */
34406        "beforeselect" : true
34407    });
34408    
34409     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34410 };
34411
34412 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34413     init : function(tree){
34414         this.tree = tree;
34415         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34416         tree.on("click", this.onNodeClick, this);
34417     },
34418     
34419     onNodeClick : function(node, e){
34420         if (e.ctrlKey && this.selNode == node)  {
34421             this.unselect(node);
34422             return;
34423         }
34424         this.select(node);
34425     },
34426     
34427     /**
34428      * Select a node.
34429      * @param {TreeNode} node The node to select
34430      * @return {TreeNode} The selected node
34431      */
34432     select : function(node){
34433         var last = this.selNode;
34434         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34435             if(last){
34436                 last.ui.onSelectedChange(false);
34437             }
34438             this.selNode = node;
34439             node.ui.onSelectedChange(true);
34440             this.fireEvent("selectionchange", this, node, last);
34441         }
34442         return node;
34443     },
34444     
34445     /**
34446      * Deselect a node.
34447      * @param {TreeNode} node The node to unselect
34448      */
34449     unselect : function(node){
34450         if(this.selNode == node){
34451             this.clearSelections();
34452         }    
34453     },
34454     
34455     /**
34456      * Clear all selections
34457      */
34458     clearSelections : function(){
34459         var n = this.selNode;
34460         if(n){
34461             n.ui.onSelectedChange(false);
34462             this.selNode = null;
34463             this.fireEvent("selectionchange", this, null);
34464         }
34465         return n;
34466     },
34467     
34468     /**
34469      * Get the selected node
34470      * @return {TreeNode} The selected node
34471      */
34472     getSelectedNode : function(){
34473         return this.selNode;    
34474     },
34475     
34476     /**
34477      * Returns true if the node is selected
34478      * @param {TreeNode} node The node to check
34479      * @return {Boolean}
34480      */
34481     isSelected : function(node){
34482         return this.selNode == node;  
34483     },
34484
34485     /**
34486      * Selects the node above the selected node in the tree, intelligently walking the nodes
34487      * @return TreeNode The new selection
34488      */
34489     selectPrevious : function(){
34490         var s = this.selNode || this.lastSelNode;
34491         if(!s){
34492             return null;
34493         }
34494         var ps = s.previousSibling;
34495         if(ps){
34496             if(!ps.isExpanded() || ps.childNodes.length < 1){
34497                 return this.select(ps);
34498             } else{
34499                 var lc = ps.lastChild;
34500                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34501                     lc = lc.lastChild;
34502                 }
34503                 return this.select(lc);
34504             }
34505         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34506             return this.select(s.parentNode);
34507         }
34508         return null;
34509     },
34510
34511     /**
34512      * Selects the node above the selected node in the tree, intelligently walking the nodes
34513      * @return TreeNode The new selection
34514      */
34515     selectNext : function(){
34516         var s = this.selNode || this.lastSelNode;
34517         if(!s){
34518             return null;
34519         }
34520         if(s.firstChild && s.isExpanded()){
34521              return this.select(s.firstChild);
34522          }else if(s.nextSibling){
34523              return this.select(s.nextSibling);
34524          }else if(s.parentNode){
34525             var newS = null;
34526             s.parentNode.bubble(function(){
34527                 if(this.nextSibling){
34528                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34529                     return false;
34530                 }
34531             });
34532             return newS;
34533          }
34534         return null;
34535     },
34536
34537     onKeyDown : function(e){
34538         var s = this.selNode || this.lastSelNode;
34539         // undesirable, but required
34540         var sm = this;
34541         if(!s){
34542             return;
34543         }
34544         var k = e.getKey();
34545         switch(k){
34546              case e.DOWN:
34547                  e.stopEvent();
34548                  this.selectNext();
34549              break;
34550              case e.UP:
34551                  e.stopEvent();
34552                  this.selectPrevious();
34553              break;
34554              case e.RIGHT:
34555                  e.preventDefault();
34556                  if(s.hasChildNodes()){
34557                      if(!s.isExpanded()){
34558                          s.expand();
34559                      }else if(s.firstChild){
34560                          this.select(s.firstChild, e);
34561                      }
34562                  }
34563              break;
34564              case e.LEFT:
34565                  e.preventDefault();
34566                  if(s.hasChildNodes() && s.isExpanded()){
34567                      s.collapse();
34568                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34569                      this.select(s.parentNode, e);
34570                  }
34571              break;
34572         };
34573     }
34574 });
34575
34576 /**
34577  * @class Roo.tree.MultiSelectionModel
34578  * @extends Roo.util.Observable
34579  * Multi selection for a TreePanel.
34580  * @param {Object} cfg Configuration
34581  */
34582 Roo.tree.MultiSelectionModel = function(){
34583    this.selNodes = [];
34584    this.selMap = {};
34585    this.addEvents({
34586        /**
34587         * @event selectionchange
34588         * Fires when the selected nodes change
34589         * @param {MultiSelectionModel} this
34590         * @param {Array} nodes Array of the selected nodes
34591         */
34592        "selectionchange" : true
34593    });
34594    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34595    
34596 };
34597
34598 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34599     init : function(tree){
34600         this.tree = tree;
34601         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34602         tree.on("click", this.onNodeClick, this);
34603     },
34604     
34605     onNodeClick : function(node, e){
34606         this.select(node, e, e.ctrlKey);
34607     },
34608     
34609     /**
34610      * Select a node.
34611      * @param {TreeNode} node The node to select
34612      * @param {EventObject} e (optional) An event associated with the selection
34613      * @param {Boolean} keepExisting True to retain existing selections
34614      * @return {TreeNode} The selected node
34615      */
34616     select : function(node, e, keepExisting){
34617         if(keepExisting !== true){
34618             this.clearSelections(true);
34619         }
34620         if(this.isSelected(node)){
34621             this.lastSelNode = node;
34622             return node;
34623         }
34624         this.selNodes.push(node);
34625         this.selMap[node.id] = node;
34626         this.lastSelNode = node;
34627         node.ui.onSelectedChange(true);
34628         this.fireEvent("selectionchange", this, this.selNodes);
34629         return node;
34630     },
34631     
34632     /**
34633      * Deselect a node.
34634      * @param {TreeNode} node The node to unselect
34635      */
34636     unselect : function(node){
34637         if(this.selMap[node.id]){
34638             node.ui.onSelectedChange(false);
34639             var sn = this.selNodes;
34640             var index = -1;
34641             if(sn.indexOf){
34642                 index = sn.indexOf(node);
34643             }else{
34644                 for(var i = 0, len = sn.length; i < len; i++){
34645                     if(sn[i] == node){
34646                         index = i;
34647                         break;
34648                     }
34649                 }
34650             }
34651             if(index != -1){
34652                 this.selNodes.splice(index, 1);
34653             }
34654             delete this.selMap[node.id];
34655             this.fireEvent("selectionchange", this, this.selNodes);
34656         }
34657     },
34658     
34659     /**
34660      * Clear all selections
34661      */
34662     clearSelections : function(suppressEvent){
34663         var sn = this.selNodes;
34664         if(sn.length > 0){
34665             for(var i = 0, len = sn.length; i < len; i++){
34666                 sn[i].ui.onSelectedChange(false);
34667             }
34668             this.selNodes = [];
34669             this.selMap = {};
34670             if(suppressEvent !== true){
34671                 this.fireEvent("selectionchange", this, this.selNodes);
34672             }
34673         }
34674     },
34675     
34676     /**
34677      * Returns true if the node is selected
34678      * @param {TreeNode} node The node to check
34679      * @return {Boolean}
34680      */
34681     isSelected : function(node){
34682         return this.selMap[node.id] ? true : false;  
34683     },
34684     
34685     /**
34686      * Returns an array of the selected nodes
34687      * @return {Array}
34688      */
34689     getSelectedNodes : function(){
34690         return this.selNodes;    
34691     },
34692
34693     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34694
34695     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34696
34697     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34698 });/*
34699  * Based on:
34700  * Ext JS Library 1.1.1
34701  * Copyright(c) 2006-2007, Ext JS, LLC.
34702  *
34703  * Originally Released Under LGPL - original licence link has changed is not relivant.
34704  *
34705  * Fork - LGPL
34706  * <script type="text/javascript">
34707  */
34708  
34709 /**
34710  * @class Roo.tree.TreeNode
34711  * @extends Roo.data.Node
34712  * @cfg {String} text The text for this node
34713  * @cfg {Boolean} expanded true to start the node expanded
34714  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34715  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34716  * @cfg {Boolean} disabled true to start the node disabled
34717  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34718  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34719  * @cfg {String} cls A css class to be added to the node
34720  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34721  * @cfg {String} href URL of the link used for the node (defaults to #)
34722  * @cfg {String} hrefTarget target frame for the link
34723  * @cfg {String} qtip An Ext QuickTip for the node
34724  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34725  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34726  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34727  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34728  * (defaults to undefined with no checkbox rendered)
34729  * @constructor
34730  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34731  */
34732 Roo.tree.TreeNode = function(attributes){
34733     attributes = attributes || {};
34734     if(typeof attributes == "string"){
34735         attributes = {text: attributes};
34736     }
34737     this.childrenRendered = false;
34738     this.rendered = false;
34739     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34740     this.expanded = attributes.expanded === true;
34741     this.isTarget = attributes.isTarget !== false;
34742     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34743     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34744
34745     /**
34746      * Read-only. The text for this node. To change it use setText().
34747      * @type String
34748      */
34749     this.text = attributes.text;
34750     /**
34751      * True if this node is disabled.
34752      * @type Boolean
34753      */
34754     this.disabled = attributes.disabled === true;
34755
34756     this.addEvents({
34757         /**
34758         * @event textchange
34759         * Fires when the text for this node is changed
34760         * @param {Node} this This node
34761         * @param {String} text The new text
34762         * @param {String} oldText The old text
34763         */
34764         "textchange" : true,
34765         /**
34766         * @event beforeexpand
34767         * Fires before this node is expanded, return false to cancel.
34768         * @param {Node} this This node
34769         * @param {Boolean} deep
34770         * @param {Boolean} anim
34771         */
34772         "beforeexpand" : true,
34773         /**
34774         * @event beforecollapse
34775         * Fires before this node is collapsed, return false to cancel.
34776         * @param {Node} this This node
34777         * @param {Boolean} deep
34778         * @param {Boolean} anim
34779         */
34780         "beforecollapse" : true,
34781         /**
34782         * @event expand
34783         * Fires when this node is expanded
34784         * @param {Node} this This node
34785         */
34786         "expand" : true,
34787         /**
34788         * @event disabledchange
34789         * Fires when the disabled status of this node changes
34790         * @param {Node} this This node
34791         * @param {Boolean} disabled
34792         */
34793         "disabledchange" : true,
34794         /**
34795         * @event collapse
34796         * Fires when this node is collapsed
34797         * @param {Node} this This node
34798         */
34799         "collapse" : true,
34800         /**
34801         * @event beforeclick
34802         * Fires before click processing. Return false to cancel the default action.
34803         * @param {Node} this This node
34804         * @param {Roo.EventObject} e The event object
34805         */
34806         "beforeclick":true,
34807         /**
34808         * @event checkchange
34809         * Fires when a node with a checkbox's checked property changes
34810         * @param {Node} this This node
34811         * @param {Boolean} checked
34812         */
34813         "checkchange":true,
34814         /**
34815         * @event click
34816         * Fires when this node is clicked
34817         * @param {Node} this This node
34818         * @param {Roo.EventObject} e The event object
34819         */
34820         "click":true,
34821         /**
34822         * @event dblclick
34823         * Fires when this node is double clicked
34824         * @param {Node} this This node
34825         * @param {Roo.EventObject} e The event object
34826         */
34827         "dblclick":true,
34828         /**
34829         * @event contextmenu
34830         * Fires when this node is right clicked
34831         * @param {Node} this This node
34832         * @param {Roo.EventObject} e The event object
34833         */
34834         "contextmenu":true,
34835         /**
34836         * @event beforechildrenrendered
34837         * Fires right before the child nodes for this node are rendered
34838         * @param {Node} this This node
34839         */
34840         "beforechildrenrendered":true
34841     });
34842
34843     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34844
34845     /**
34846      * Read-only. The UI for this node
34847      * @type TreeNodeUI
34848      */
34849     this.ui = new uiClass(this);
34850     
34851     // finally support items[]
34852     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34853         return;
34854     }
34855     
34856     
34857     Roo.each(this.attributes.items, function(c) {
34858         this.appendChild(Roo.factory(c,Roo.Tree));
34859     }, this);
34860     delete this.attributes.items;
34861     
34862     
34863     
34864 };
34865 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34866     preventHScroll: true,
34867     /**
34868      * Returns true if this node is expanded
34869      * @return {Boolean}
34870      */
34871     isExpanded : function(){
34872         return this.expanded;
34873     },
34874
34875     /**
34876      * Returns the UI object for this node
34877      * @return {TreeNodeUI}
34878      */
34879     getUI : function(){
34880         return this.ui;
34881     },
34882
34883     // private override
34884     setFirstChild : function(node){
34885         var of = this.firstChild;
34886         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34887         if(this.childrenRendered && of && node != of){
34888             of.renderIndent(true, true);
34889         }
34890         if(this.rendered){
34891             this.renderIndent(true, true);
34892         }
34893     },
34894
34895     // private override
34896     setLastChild : function(node){
34897         var ol = this.lastChild;
34898         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34899         if(this.childrenRendered && ol && node != ol){
34900             ol.renderIndent(true, true);
34901         }
34902         if(this.rendered){
34903             this.renderIndent(true, true);
34904         }
34905     },
34906
34907     // these methods are overridden to provide lazy rendering support
34908     // private override
34909     appendChild : function()
34910     {
34911         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34912         if(node && this.childrenRendered){
34913             node.render();
34914         }
34915         this.ui.updateExpandIcon();
34916         return node;
34917     },
34918
34919     // private override
34920     removeChild : function(node){
34921         this.ownerTree.getSelectionModel().unselect(node);
34922         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34923         // if it's been rendered remove dom node
34924         if(this.childrenRendered){
34925             node.ui.remove();
34926         }
34927         if(this.childNodes.length < 1){
34928             this.collapse(false, false);
34929         }else{
34930             this.ui.updateExpandIcon();
34931         }
34932         if(!this.firstChild) {
34933             this.childrenRendered = false;
34934         }
34935         return node;
34936     },
34937
34938     // private override
34939     insertBefore : function(node, refNode){
34940         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34941         if(newNode && refNode && this.childrenRendered){
34942             node.render();
34943         }
34944         this.ui.updateExpandIcon();
34945         return newNode;
34946     },
34947
34948     /**
34949      * Sets the text for this node
34950      * @param {String} text
34951      */
34952     setText : function(text){
34953         var oldText = this.text;
34954         this.text = text;
34955         this.attributes.text = text;
34956         if(this.rendered){ // event without subscribing
34957             this.ui.onTextChange(this, text, oldText);
34958         }
34959         this.fireEvent("textchange", this, text, oldText);
34960     },
34961
34962     /**
34963      * Triggers selection of this node
34964      */
34965     select : function(){
34966         this.getOwnerTree().getSelectionModel().select(this);
34967     },
34968
34969     /**
34970      * Triggers deselection of this node
34971      */
34972     unselect : function(){
34973         this.getOwnerTree().getSelectionModel().unselect(this);
34974     },
34975
34976     /**
34977      * Returns true if this node is selected
34978      * @return {Boolean}
34979      */
34980     isSelected : function(){
34981         return this.getOwnerTree().getSelectionModel().isSelected(this);
34982     },
34983
34984     /**
34985      * Expand this node.
34986      * @param {Boolean} deep (optional) True to expand all children as well
34987      * @param {Boolean} anim (optional) false to cancel the default animation
34988      * @param {Function} callback (optional) A callback to be called when
34989      * expanding this node completes (does not wait for deep expand to complete).
34990      * Called with 1 parameter, this node.
34991      */
34992     expand : function(deep, anim, callback){
34993         if(!this.expanded){
34994             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34995                 return;
34996             }
34997             if(!this.childrenRendered){
34998                 this.renderChildren();
34999             }
35000             this.expanded = true;
35001             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35002                 this.ui.animExpand(function(){
35003                     this.fireEvent("expand", this);
35004                     if(typeof callback == "function"){
35005                         callback(this);
35006                     }
35007                     if(deep === true){
35008                         this.expandChildNodes(true);
35009                     }
35010                 }.createDelegate(this));
35011                 return;
35012             }else{
35013                 this.ui.expand();
35014                 this.fireEvent("expand", this);
35015                 if(typeof callback == "function"){
35016                     callback(this);
35017                 }
35018             }
35019         }else{
35020            if(typeof callback == "function"){
35021                callback(this);
35022            }
35023         }
35024         if(deep === true){
35025             this.expandChildNodes(true);
35026         }
35027     },
35028
35029     isHiddenRoot : function(){
35030         return this.isRoot && !this.getOwnerTree().rootVisible;
35031     },
35032
35033     /**
35034      * Collapse this node.
35035      * @param {Boolean} deep (optional) True to collapse all children as well
35036      * @param {Boolean} anim (optional) false to cancel the default animation
35037      */
35038     collapse : function(deep, anim){
35039         if(this.expanded && !this.isHiddenRoot()){
35040             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35041                 return;
35042             }
35043             this.expanded = false;
35044             if((this.getOwnerTree().animate && anim !== false) || anim){
35045                 this.ui.animCollapse(function(){
35046                     this.fireEvent("collapse", this);
35047                     if(deep === true){
35048                         this.collapseChildNodes(true);
35049                     }
35050                 }.createDelegate(this));
35051                 return;
35052             }else{
35053                 this.ui.collapse();
35054                 this.fireEvent("collapse", this);
35055             }
35056         }
35057         if(deep === true){
35058             var cs = this.childNodes;
35059             for(var i = 0, len = cs.length; i < len; i++) {
35060                 cs[i].collapse(true, false);
35061             }
35062         }
35063     },
35064
35065     // private
35066     delayedExpand : function(delay){
35067         if(!this.expandProcId){
35068             this.expandProcId = this.expand.defer(delay, this);
35069         }
35070     },
35071
35072     // private
35073     cancelExpand : function(){
35074         if(this.expandProcId){
35075             clearTimeout(this.expandProcId);
35076         }
35077         this.expandProcId = false;
35078     },
35079
35080     /**
35081      * Toggles expanded/collapsed state of the node
35082      */
35083     toggle : function(){
35084         if(this.expanded){
35085             this.collapse();
35086         }else{
35087             this.expand();
35088         }
35089     },
35090
35091     /**
35092      * Ensures all parent nodes are expanded
35093      */
35094     ensureVisible : function(callback){
35095         var tree = this.getOwnerTree();
35096         tree.expandPath(this.parentNode.getPath(), false, function(){
35097             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35098             Roo.callback(callback);
35099         }.createDelegate(this));
35100     },
35101
35102     /**
35103      * Expand all child nodes
35104      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35105      */
35106     expandChildNodes : function(deep){
35107         var cs = this.childNodes;
35108         for(var i = 0, len = cs.length; i < len; i++) {
35109                 cs[i].expand(deep);
35110         }
35111     },
35112
35113     /**
35114      * Collapse all child nodes
35115      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35116      */
35117     collapseChildNodes : function(deep){
35118         var cs = this.childNodes;
35119         for(var i = 0, len = cs.length; i < len; i++) {
35120                 cs[i].collapse(deep);
35121         }
35122     },
35123
35124     /**
35125      * Disables this node
35126      */
35127     disable : function(){
35128         this.disabled = true;
35129         this.unselect();
35130         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35131             this.ui.onDisableChange(this, true);
35132         }
35133         this.fireEvent("disabledchange", this, true);
35134     },
35135
35136     /**
35137      * Enables this node
35138      */
35139     enable : function(){
35140         this.disabled = false;
35141         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35142             this.ui.onDisableChange(this, false);
35143         }
35144         this.fireEvent("disabledchange", this, false);
35145     },
35146
35147     // private
35148     renderChildren : function(suppressEvent){
35149         if(suppressEvent !== false){
35150             this.fireEvent("beforechildrenrendered", this);
35151         }
35152         var cs = this.childNodes;
35153         for(var i = 0, len = cs.length; i < len; i++){
35154             cs[i].render(true);
35155         }
35156         this.childrenRendered = true;
35157     },
35158
35159     // private
35160     sort : function(fn, scope){
35161         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35162         if(this.childrenRendered){
35163             var cs = this.childNodes;
35164             for(var i = 0, len = cs.length; i < len; i++){
35165                 cs[i].render(true);
35166             }
35167         }
35168     },
35169
35170     // private
35171     render : function(bulkRender){
35172         this.ui.render(bulkRender);
35173         if(!this.rendered){
35174             this.rendered = true;
35175             if(this.expanded){
35176                 this.expanded = false;
35177                 this.expand(false, false);
35178             }
35179         }
35180     },
35181
35182     // private
35183     renderIndent : function(deep, refresh){
35184         if(refresh){
35185             this.ui.childIndent = null;
35186         }
35187         this.ui.renderIndent();
35188         if(deep === true && this.childrenRendered){
35189             var cs = this.childNodes;
35190             for(var i = 0, len = cs.length; i < len; i++){
35191                 cs[i].renderIndent(true, refresh);
35192             }
35193         }
35194     }
35195 });/*
35196  * Based on:
35197  * Ext JS Library 1.1.1
35198  * Copyright(c) 2006-2007, Ext JS, LLC.
35199  *
35200  * Originally Released Under LGPL - original licence link has changed is not relivant.
35201  *
35202  * Fork - LGPL
35203  * <script type="text/javascript">
35204  */
35205  
35206 /**
35207  * @class Roo.tree.AsyncTreeNode
35208  * @extends Roo.tree.TreeNode
35209  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35210  * @constructor
35211  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35212  */
35213  Roo.tree.AsyncTreeNode = function(config){
35214     this.loaded = false;
35215     this.loading = false;
35216     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35217     /**
35218     * @event beforeload
35219     * Fires before this node is loaded, return false to cancel
35220     * @param {Node} this This node
35221     */
35222     this.addEvents({'beforeload':true, 'load': true});
35223     /**
35224     * @event load
35225     * Fires when this node is loaded
35226     * @param {Node} this This node
35227     */
35228     /**
35229      * The loader used by this node (defaults to using the tree's defined loader)
35230      * @type TreeLoader
35231      * @property loader
35232      */
35233 };
35234 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35235     expand : function(deep, anim, callback){
35236         if(this.loading){ // if an async load is already running, waiting til it's done
35237             var timer;
35238             var f = function(){
35239                 if(!this.loading){ // done loading
35240                     clearInterval(timer);
35241                     this.expand(deep, anim, callback);
35242                 }
35243             }.createDelegate(this);
35244             timer = setInterval(f, 200);
35245             return;
35246         }
35247         if(!this.loaded){
35248             if(this.fireEvent("beforeload", this) === false){
35249                 return;
35250             }
35251             this.loading = true;
35252             this.ui.beforeLoad(this);
35253             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35254             if(loader){
35255                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35256                 return;
35257             }
35258         }
35259         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35260     },
35261     
35262     /**
35263      * Returns true if this node is currently loading
35264      * @return {Boolean}
35265      */
35266     isLoading : function(){
35267         return this.loading;  
35268     },
35269     
35270     loadComplete : function(deep, anim, callback){
35271         this.loading = false;
35272         this.loaded = true;
35273         this.ui.afterLoad(this);
35274         this.fireEvent("load", this);
35275         this.expand(deep, anim, callback);
35276     },
35277     
35278     /**
35279      * Returns true if this node has been loaded
35280      * @return {Boolean}
35281      */
35282     isLoaded : function(){
35283         return this.loaded;
35284     },
35285     
35286     hasChildNodes : function(){
35287         if(!this.isLeaf() && !this.loaded){
35288             return true;
35289         }else{
35290             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35291         }
35292     },
35293
35294     /**
35295      * Trigger a reload for this node
35296      * @param {Function} callback
35297      */
35298     reload : function(callback){
35299         this.collapse(false, false);
35300         while(this.firstChild){
35301             this.removeChild(this.firstChild);
35302         }
35303         this.childrenRendered = false;
35304         this.loaded = false;
35305         if(this.isHiddenRoot()){
35306             this.expanded = false;
35307         }
35308         this.expand(false, false, callback);
35309     }
35310 });/*
35311  * Based on:
35312  * Ext JS Library 1.1.1
35313  * Copyright(c) 2006-2007, Ext JS, LLC.
35314  *
35315  * Originally Released Under LGPL - original licence link has changed is not relivant.
35316  *
35317  * Fork - LGPL
35318  * <script type="text/javascript">
35319  */
35320  
35321 /**
35322  * @class Roo.tree.TreeNodeUI
35323  * @constructor
35324  * @param {Object} node The node to render
35325  * The TreeNode UI implementation is separate from the
35326  * tree implementation. Unless you are customizing the tree UI,
35327  * you should never have to use this directly.
35328  */
35329 Roo.tree.TreeNodeUI = function(node){
35330     this.node = node;
35331     this.rendered = false;
35332     this.animating = false;
35333     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35334 };
35335
35336 Roo.tree.TreeNodeUI.prototype = {
35337     removeChild : function(node){
35338         if(this.rendered){
35339             this.ctNode.removeChild(node.ui.getEl());
35340         }
35341     },
35342
35343     beforeLoad : function(){
35344          this.addClass("x-tree-node-loading");
35345     },
35346
35347     afterLoad : function(){
35348          this.removeClass("x-tree-node-loading");
35349     },
35350
35351     onTextChange : function(node, text, oldText){
35352         if(this.rendered){
35353             this.textNode.innerHTML = text;
35354         }
35355     },
35356
35357     onDisableChange : function(node, state){
35358         this.disabled = state;
35359         if(state){
35360             this.addClass("x-tree-node-disabled");
35361         }else{
35362             this.removeClass("x-tree-node-disabled");
35363         }
35364     },
35365
35366     onSelectedChange : function(state){
35367         if(state){
35368             this.focus();
35369             this.addClass("x-tree-selected");
35370         }else{
35371             //this.blur();
35372             this.removeClass("x-tree-selected");
35373         }
35374     },
35375
35376     onMove : function(tree, node, oldParent, newParent, index, refNode){
35377         this.childIndent = null;
35378         if(this.rendered){
35379             var targetNode = newParent.ui.getContainer();
35380             if(!targetNode){//target not rendered
35381                 this.holder = document.createElement("div");
35382                 this.holder.appendChild(this.wrap);
35383                 return;
35384             }
35385             var insertBefore = refNode ? refNode.ui.getEl() : null;
35386             if(insertBefore){
35387                 targetNode.insertBefore(this.wrap, insertBefore);
35388             }else{
35389                 targetNode.appendChild(this.wrap);
35390             }
35391             this.node.renderIndent(true);
35392         }
35393     },
35394
35395     addClass : function(cls){
35396         if(this.elNode){
35397             Roo.fly(this.elNode).addClass(cls);
35398         }
35399     },
35400
35401     removeClass : function(cls){
35402         if(this.elNode){
35403             Roo.fly(this.elNode).removeClass(cls);
35404         }
35405     },
35406
35407     remove : function(){
35408         if(this.rendered){
35409             this.holder = document.createElement("div");
35410             this.holder.appendChild(this.wrap);
35411         }
35412     },
35413
35414     fireEvent : function(){
35415         return this.node.fireEvent.apply(this.node, arguments);
35416     },
35417
35418     initEvents : function(){
35419         this.node.on("move", this.onMove, this);
35420         var E = Roo.EventManager;
35421         var a = this.anchor;
35422
35423         var el = Roo.fly(a, '_treeui');
35424
35425         if(Roo.isOpera){ // opera render bug ignores the CSS
35426             el.setStyle("text-decoration", "none");
35427         }
35428
35429         el.on("click", this.onClick, this);
35430         el.on("dblclick", this.onDblClick, this);
35431
35432         if(this.checkbox){
35433             Roo.EventManager.on(this.checkbox,
35434                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35435         }
35436
35437         el.on("contextmenu", this.onContextMenu, this);
35438
35439         var icon = Roo.fly(this.iconNode);
35440         icon.on("click", this.onClick, this);
35441         icon.on("dblclick", this.onDblClick, this);
35442         icon.on("contextmenu", this.onContextMenu, this);
35443         E.on(this.ecNode, "click", this.ecClick, this, true);
35444
35445         if(this.node.disabled){
35446             this.addClass("x-tree-node-disabled");
35447         }
35448         if(this.node.hidden){
35449             this.addClass("x-tree-node-disabled");
35450         }
35451         var ot = this.node.getOwnerTree();
35452         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35453         if(dd && (!this.node.isRoot || ot.rootVisible)){
35454             Roo.dd.Registry.register(this.elNode, {
35455                 node: this.node,
35456                 handles: this.getDDHandles(),
35457                 isHandle: false
35458             });
35459         }
35460     },
35461
35462     getDDHandles : function(){
35463         return [this.iconNode, this.textNode];
35464     },
35465
35466     hide : function(){
35467         if(this.rendered){
35468             this.wrap.style.display = "none";
35469         }
35470     },
35471
35472     show : function(){
35473         if(this.rendered){
35474             this.wrap.style.display = "";
35475         }
35476     },
35477
35478     onContextMenu : function(e){
35479         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35480             e.preventDefault();
35481             this.focus();
35482             this.fireEvent("contextmenu", this.node, e);
35483         }
35484     },
35485
35486     onClick : function(e){
35487         if(this.dropping){
35488             e.stopEvent();
35489             return;
35490         }
35491         if(this.fireEvent("beforeclick", this.node, e) !== false){
35492             if(!this.disabled && this.node.attributes.href){
35493                 this.fireEvent("click", this.node, e);
35494                 return;
35495             }
35496             e.preventDefault();
35497             if(this.disabled){
35498                 return;
35499             }
35500
35501             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35502                 this.node.toggle();
35503             }
35504
35505             this.fireEvent("click", this.node, e);
35506         }else{
35507             e.stopEvent();
35508         }
35509     },
35510
35511     onDblClick : function(e){
35512         e.preventDefault();
35513         if(this.disabled){
35514             return;
35515         }
35516         if(this.checkbox){
35517             this.toggleCheck();
35518         }
35519         if(!this.animating && this.node.hasChildNodes()){
35520             this.node.toggle();
35521         }
35522         this.fireEvent("dblclick", this.node, e);
35523     },
35524
35525     onCheckChange : function(){
35526         var checked = this.checkbox.checked;
35527         this.node.attributes.checked = checked;
35528         this.fireEvent('checkchange', this.node, checked);
35529     },
35530
35531     ecClick : function(e){
35532         if(!this.animating && this.node.hasChildNodes()){
35533             this.node.toggle();
35534         }
35535     },
35536
35537     startDrop : function(){
35538         this.dropping = true;
35539     },
35540
35541     // delayed drop so the click event doesn't get fired on a drop
35542     endDrop : function(){
35543        setTimeout(function(){
35544            this.dropping = false;
35545        }.createDelegate(this), 50);
35546     },
35547
35548     expand : function(){
35549         this.updateExpandIcon();
35550         this.ctNode.style.display = "";
35551     },
35552
35553     focus : function(){
35554         if(!this.node.preventHScroll){
35555             try{this.anchor.focus();
35556             }catch(e){}
35557         }else if(!Roo.isIE){
35558             try{
35559                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35560                 var l = noscroll.scrollLeft;
35561                 this.anchor.focus();
35562                 noscroll.scrollLeft = l;
35563             }catch(e){}
35564         }
35565     },
35566
35567     toggleCheck : function(value){
35568         var cb = this.checkbox;
35569         if(cb){
35570             cb.checked = (value === undefined ? !cb.checked : value);
35571         }
35572     },
35573
35574     blur : function(){
35575         try{
35576             this.anchor.blur();
35577         }catch(e){}
35578     },
35579
35580     animExpand : function(callback){
35581         var ct = Roo.get(this.ctNode);
35582         ct.stopFx();
35583         if(!this.node.hasChildNodes()){
35584             this.updateExpandIcon();
35585             this.ctNode.style.display = "";
35586             Roo.callback(callback);
35587             return;
35588         }
35589         this.animating = true;
35590         this.updateExpandIcon();
35591
35592         ct.slideIn('t', {
35593            callback : function(){
35594                this.animating = false;
35595                Roo.callback(callback);
35596             },
35597             scope: this,
35598             duration: this.node.ownerTree.duration || .25
35599         });
35600     },
35601
35602     highlight : function(){
35603         var tree = this.node.getOwnerTree();
35604         Roo.fly(this.wrap).highlight(
35605             tree.hlColor || "C3DAF9",
35606             {endColor: tree.hlBaseColor}
35607         );
35608     },
35609
35610     collapse : function(){
35611         this.updateExpandIcon();
35612         this.ctNode.style.display = "none";
35613     },
35614
35615     animCollapse : function(callback){
35616         var ct = Roo.get(this.ctNode);
35617         ct.enableDisplayMode('block');
35618         ct.stopFx();
35619
35620         this.animating = true;
35621         this.updateExpandIcon();
35622
35623         ct.slideOut('t', {
35624             callback : function(){
35625                this.animating = false;
35626                Roo.callback(callback);
35627             },
35628             scope: this,
35629             duration: this.node.ownerTree.duration || .25
35630         });
35631     },
35632
35633     getContainer : function(){
35634         return this.ctNode;
35635     },
35636
35637     getEl : function(){
35638         return this.wrap;
35639     },
35640
35641     appendDDGhost : function(ghostNode){
35642         ghostNode.appendChild(this.elNode.cloneNode(true));
35643     },
35644
35645     getDDRepairXY : function(){
35646         return Roo.lib.Dom.getXY(this.iconNode);
35647     },
35648
35649     onRender : function(){
35650         this.render();
35651     },
35652
35653     render : function(bulkRender){
35654         var n = this.node, a = n.attributes;
35655         var targetNode = n.parentNode ?
35656               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35657
35658         if(!this.rendered){
35659             this.rendered = true;
35660
35661             this.renderElements(n, a, targetNode, bulkRender);
35662
35663             if(a.qtip){
35664                if(this.textNode.setAttributeNS){
35665                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35666                    if(a.qtipTitle){
35667                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35668                    }
35669                }else{
35670                    this.textNode.setAttribute("ext:qtip", a.qtip);
35671                    if(a.qtipTitle){
35672                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35673                    }
35674                }
35675             }else if(a.qtipCfg){
35676                 a.qtipCfg.target = Roo.id(this.textNode);
35677                 Roo.QuickTips.register(a.qtipCfg);
35678             }
35679             this.initEvents();
35680             if(!this.node.expanded){
35681                 this.updateExpandIcon();
35682             }
35683         }else{
35684             if(bulkRender === true) {
35685                 targetNode.appendChild(this.wrap);
35686             }
35687         }
35688     },
35689
35690     renderElements : function(n, a, targetNode, bulkRender)
35691     {
35692         // add some indent caching, this helps performance when rendering a large tree
35693         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35694         var t = n.getOwnerTree();
35695         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35696         if (typeof(n.attributes.html) != 'undefined') {
35697             txt = n.attributes.html;
35698         }
35699         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35700         var cb = typeof a.checked == 'boolean';
35701         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35702         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35703             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35704             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35705             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35706             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35707             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35708              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35709                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35710             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35711             "</li>"];
35712
35713         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35714             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35715                                 n.nextSibling.ui.getEl(), buf.join(""));
35716         }else{
35717             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35718         }
35719
35720         this.elNode = this.wrap.childNodes[0];
35721         this.ctNode = this.wrap.childNodes[1];
35722         var cs = this.elNode.childNodes;
35723         this.indentNode = cs[0];
35724         this.ecNode = cs[1];
35725         this.iconNode = cs[2];
35726         var index = 3;
35727         if(cb){
35728             this.checkbox = cs[3];
35729             index++;
35730         }
35731         this.anchor = cs[index];
35732         this.textNode = cs[index].firstChild;
35733     },
35734
35735     getAnchor : function(){
35736         return this.anchor;
35737     },
35738
35739     getTextEl : function(){
35740         return this.textNode;
35741     },
35742
35743     getIconEl : function(){
35744         return this.iconNode;
35745     },
35746
35747     isChecked : function(){
35748         return this.checkbox ? this.checkbox.checked : false;
35749     },
35750
35751     updateExpandIcon : function(){
35752         if(this.rendered){
35753             var n = this.node, c1, c2;
35754             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35755             var hasChild = n.hasChildNodes();
35756             if(hasChild){
35757                 if(n.expanded){
35758                     cls += "-minus";
35759                     c1 = "x-tree-node-collapsed";
35760                     c2 = "x-tree-node-expanded";
35761                 }else{
35762                     cls += "-plus";
35763                     c1 = "x-tree-node-expanded";
35764                     c2 = "x-tree-node-collapsed";
35765                 }
35766                 if(this.wasLeaf){
35767                     this.removeClass("x-tree-node-leaf");
35768                     this.wasLeaf = false;
35769                 }
35770                 if(this.c1 != c1 || this.c2 != c2){
35771                     Roo.fly(this.elNode).replaceClass(c1, c2);
35772                     this.c1 = c1; this.c2 = c2;
35773                 }
35774             }else{
35775                 // this changes non-leafs into leafs if they have no children.
35776                 // it's not very rational behaviour..
35777                 
35778                 if(!this.wasLeaf && this.node.leaf){
35779                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35780                     delete this.c1;
35781                     delete this.c2;
35782                     this.wasLeaf = true;
35783                 }
35784             }
35785             var ecc = "x-tree-ec-icon "+cls;
35786             if(this.ecc != ecc){
35787                 this.ecNode.className = ecc;
35788                 this.ecc = ecc;
35789             }
35790         }
35791     },
35792
35793     getChildIndent : function(){
35794         if(!this.childIndent){
35795             var buf = [];
35796             var p = this.node;
35797             while(p){
35798                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35799                     if(!p.isLast()) {
35800                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35801                     } else {
35802                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35803                     }
35804                 }
35805                 p = p.parentNode;
35806             }
35807             this.childIndent = buf.join("");
35808         }
35809         return this.childIndent;
35810     },
35811
35812     renderIndent : function(){
35813         if(this.rendered){
35814             var indent = "";
35815             var p = this.node.parentNode;
35816             if(p){
35817                 indent = p.ui.getChildIndent();
35818             }
35819             if(this.indentMarkup != indent){ // don't rerender if not required
35820                 this.indentNode.innerHTML = indent;
35821                 this.indentMarkup = indent;
35822             }
35823             this.updateExpandIcon();
35824         }
35825     }
35826 };
35827
35828 Roo.tree.RootTreeNodeUI = function(){
35829     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35830 };
35831 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35832     render : function(){
35833         if(!this.rendered){
35834             var targetNode = this.node.ownerTree.innerCt.dom;
35835             this.node.expanded = true;
35836             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35837             this.wrap = this.ctNode = targetNode.firstChild;
35838         }
35839     },
35840     collapse : function(){
35841     },
35842     expand : function(){
35843     }
35844 });/*
35845  * Based on:
35846  * Ext JS Library 1.1.1
35847  * Copyright(c) 2006-2007, Ext JS, LLC.
35848  *
35849  * Originally Released Under LGPL - original licence link has changed is not relivant.
35850  *
35851  * Fork - LGPL
35852  * <script type="text/javascript">
35853  */
35854 /**
35855  * @class Roo.tree.TreeLoader
35856  * @extends Roo.util.Observable
35857  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35858  * nodes from a specified URL. The response must be a javascript Array definition
35859  * who's elements are node definition objects. eg:
35860  * <pre><code>
35861 {  success : true,
35862    data :      [
35863    
35864     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35865     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35866     ]
35867 }
35868
35869
35870 </code></pre>
35871  * <br><br>
35872  * The old style respose with just an array is still supported, but not recommended.
35873  * <br><br>
35874  *
35875  * A server request is sent, and child nodes are loaded only when a node is expanded.
35876  * The loading node's id is passed to the server under the parameter name "node" to
35877  * enable the server to produce the correct child nodes.
35878  * <br><br>
35879  * To pass extra parameters, an event handler may be attached to the "beforeload"
35880  * event, and the parameters specified in the TreeLoader's baseParams property:
35881  * <pre><code>
35882     myTreeLoader.on("beforeload", function(treeLoader, node) {
35883         this.baseParams.category = node.attributes.category;
35884     }, this);
35885 </code></pre><
35886  * This would pass an HTTP parameter called "category" to the server containing
35887  * the value of the Node's "category" attribute.
35888  * @constructor
35889  * Creates a new Treeloader.
35890  * @param {Object} config A config object containing config properties.
35891  */
35892 Roo.tree.TreeLoader = function(config){
35893     this.baseParams = {};
35894     this.requestMethod = "POST";
35895     Roo.apply(this, config);
35896
35897     this.addEvents({
35898     
35899         /**
35900          * @event beforeload
35901          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35902          * @param {Object} This TreeLoader object.
35903          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35904          * @param {Object} callback The callback function specified in the {@link #load} call.
35905          */
35906         beforeload : true,
35907         /**
35908          * @event load
35909          * Fires when the node has been successfuly loaded.
35910          * @param {Object} This TreeLoader object.
35911          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35912          * @param {Object} response The response object containing the data from the server.
35913          */
35914         load : true,
35915         /**
35916          * @event loadexception
35917          * Fires if the network request failed.
35918          * @param {Object} This TreeLoader object.
35919          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35920          * @param {Object} response The response object containing the data from the server.
35921          */
35922         loadexception : true,
35923         /**
35924          * @event create
35925          * Fires before a node is created, enabling you to return custom Node types 
35926          * @param {Object} This TreeLoader object.
35927          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35928          */
35929         create : true
35930     });
35931
35932     Roo.tree.TreeLoader.superclass.constructor.call(this);
35933 };
35934
35935 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35936     /**
35937     * @cfg {String} dataUrl The URL from which to request a Json string which
35938     * specifies an array of node definition object representing the child nodes
35939     * to be loaded.
35940     */
35941     /**
35942     * @cfg {String} requestMethod either GET or POST
35943     * defaults to POST (due to BC)
35944     * to be loaded.
35945     */
35946     /**
35947     * @cfg {Object} baseParams (optional) An object containing properties which
35948     * specify HTTP parameters to be passed to each request for child nodes.
35949     */
35950     /**
35951     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35952     * created by this loader. If the attributes sent by the server have an attribute in this object,
35953     * they take priority.
35954     */
35955     /**
35956     * @cfg {Object} uiProviders (optional) An object containing properties which
35957     * 
35958     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35959     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35960     * <i>uiProvider</i> attribute of a returned child node is a string rather
35961     * than a reference to a TreeNodeUI implementation, this that string value
35962     * is used as a property name in the uiProviders object. You can define the provider named
35963     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35964     */
35965     uiProviders : {},
35966
35967     /**
35968     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35969     * child nodes before loading.
35970     */
35971     clearOnLoad : true,
35972
35973     /**
35974     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35975     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35976     * Grid query { data : [ .....] }
35977     */
35978     
35979     root : false,
35980      /**
35981     * @cfg {String} queryParam (optional) 
35982     * Name of the query as it will be passed on the querystring (defaults to 'node')
35983     * eg. the request will be ?node=[id]
35984     */
35985     
35986     
35987     queryParam: false,
35988     
35989     /**
35990      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35991      * This is called automatically when a node is expanded, but may be used to reload
35992      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35993      * @param {Roo.tree.TreeNode} node
35994      * @param {Function} callback
35995      */
35996     load : function(node, callback){
35997         if(this.clearOnLoad){
35998             while(node.firstChild){
35999                 node.removeChild(node.firstChild);
36000             }
36001         }
36002         if(node.attributes.children){ // preloaded json children
36003             var cs = node.attributes.children;
36004             for(var i = 0, len = cs.length; i < len; i++){
36005                 node.appendChild(this.createNode(cs[i]));
36006             }
36007             if(typeof callback == "function"){
36008                 callback();
36009             }
36010         }else if(this.dataUrl){
36011             this.requestData(node, callback);
36012         }
36013     },
36014
36015     getParams: function(node){
36016         var buf = [], bp = this.baseParams;
36017         for(var key in bp){
36018             if(typeof bp[key] != "function"){
36019                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36020             }
36021         }
36022         var n = this.queryParam === false ? 'node' : this.queryParam;
36023         buf.push(n + "=", encodeURIComponent(node.id));
36024         return buf.join("");
36025     },
36026
36027     requestData : function(node, callback){
36028         if(this.fireEvent("beforeload", this, node, callback) !== false){
36029             this.transId = Roo.Ajax.request({
36030                 method:this.requestMethod,
36031                 url: this.dataUrl||this.url,
36032                 success: this.handleResponse,
36033                 failure: this.handleFailure,
36034                 scope: this,
36035                 argument: {callback: callback, node: node},
36036                 params: this.getParams(node)
36037             });
36038         }else{
36039             // if the load is cancelled, make sure we notify
36040             // the node that we are done
36041             if(typeof callback == "function"){
36042                 callback();
36043             }
36044         }
36045     },
36046
36047     isLoading : function(){
36048         return this.transId ? true : false;
36049     },
36050
36051     abort : function(){
36052         if(this.isLoading()){
36053             Roo.Ajax.abort(this.transId);
36054         }
36055     },
36056
36057     // private
36058     createNode : function(attr)
36059     {
36060         // apply baseAttrs, nice idea Corey!
36061         if(this.baseAttrs){
36062             Roo.applyIf(attr, this.baseAttrs);
36063         }
36064         if(this.applyLoader !== false){
36065             attr.loader = this;
36066         }
36067         // uiProvider = depreciated..
36068         
36069         if(typeof(attr.uiProvider) == 'string'){
36070            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36071                 /**  eval:var:attr */ eval(attr.uiProvider);
36072         }
36073         if(typeof(this.uiProviders['default']) != 'undefined') {
36074             attr.uiProvider = this.uiProviders['default'];
36075         }
36076         
36077         this.fireEvent('create', this, attr);
36078         
36079         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36080         return(attr.leaf ?
36081                         new Roo.tree.TreeNode(attr) :
36082                         new Roo.tree.AsyncTreeNode(attr));
36083     },
36084
36085     processResponse : function(response, node, callback)
36086     {
36087         var json = response.responseText;
36088         try {
36089             
36090             var o = Roo.decode(json);
36091             
36092             if (this.root === false && typeof(o.success) != undefined) {
36093                 this.root = 'data'; // the default behaviour for list like data..
36094                 }
36095                 
36096             if (this.root !== false &&  !o.success) {
36097                 // it's a failure condition.
36098                 var a = response.argument;
36099                 this.fireEvent("loadexception", this, a.node, response);
36100                 Roo.log("Load failed - should have a handler really");
36101                 return;
36102             }
36103             
36104             
36105             
36106             if (this.root !== false) {
36107                  o = o[this.root];
36108             }
36109             
36110             for(var i = 0, len = o.length; i < len; i++){
36111                 var n = this.createNode(o[i]);
36112                 if(n){
36113                     node.appendChild(n);
36114                 }
36115             }
36116             if(typeof callback == "function"){
36117                 callback(this, node);
36118             }
36119         }catch(e){
36120             this.handleFailure(response);
36121         }
36122     },
36123
36124     handleResponse : function(response){
36125         this.transId = false;
36126         var a = response.argument;
36127         this.processResponse(response, a.node, a.callback);
36128         this.fireEvent("load", this, a.node, response);
36129     },
36130
36131     handleFailure : function(response)
36132     {
36133         // should handle failure better..
36134         this.transId = false;
36135         var a = response.argument;
36136         this.fireEvent("loadexception", this, a.node, response);
36137         if(typeof a.callback == "function"){
36138             a.callback(this, a.node);
36139         }
36140     }
36141 });/*
36142  * Based on:
36143  * Ext JS Library 1.1.1
36144  * Copyright(c) 2006-2007, Ext JS, LLC.
36145  *
36146  * Originally Released Under LGPL - original licence link has changed is not relivant.
36147  *
36148  * Fork - LGPL
36149  * <script type="text/javascript">
36150  */
36151
36152 /**
36153 * @class Roo.tree.TreeFilter
36154 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36155 * @param {TreePanel} tree
36156 * @param {Object} config (optional)
36157  */
36158 Roo.tree.TreeFilter = function(tree, config){
36159     this.tree = tree;
36160     this.filtered = {};
36161     Roo.apply(this, config);
36162 };
36163
36164 Roo.tree.TreeFilter.prototype = {
36165     clearBlank:false,
36166     reverse:false,
36167     autoClear:false,
36168     remove:false,
36169
36170      /**
36171      * Filter the data by a specific attribute.
36172      * @param {String/RegExp} value Either string that the attribute value
36173      * should start with or a RegExp to test against the attribute
36174      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36175      * @param {TreeNode} startNode (optional) The node to start the filter at.
36176      */
36177     filter : function(value, attr, startNode){
36178         attr = attr || "text";
36179         var f;
36180         if(typeof value == "string"){
36181             var vlen = value.length;
36182             // auto clear empty filter
36183             if(vlen == 0 && this.clearBlank){
36184                 this.clear();
36185                 return;
36186             }
36187             value = value.toLowerCase();
36188             f = function(n){
36189                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36190             };
36191         }else if(value.exec){ // regex?
36192             f = function(n){
36193                 return value.test(n.attributes[attr]);
36194             };
36195         }else{
36196             throw 'Illegal filter type, must be string or regex';
36197         }
36198         this.filterBy(f, null, startNode);
36199         },
36200
36201     /**
36202      * Filter by a function. The passed function will be called with each
36203      * node in the tree (or from the startNode). If the function returns true, the node is kept
36204      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36205      * @param {Function} fn The filter function
36206      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36207      */
36208     filterBy : function(fn, scope, startNode){
36209         startNode = startNode || this.tree.root;
36210         if(this.autoClear){
36211             this.clear();
36212         }
36213         var af = this.filtered, rv = this.reverse;
36214         var f = function(n){
36215             if(n == startNode){
36216                 return true;
36217             }
36218             if(af[n.id]){
36219                 return false;
36220             }
36221             var m = fn.call(scope || n, n);
36222             if(!m || rv){
36223                 af[n.id] = n;
36224                 n.ui.hide();
36225                 return false;
36226             }
36227             return true;
36228         };
36229         startNode.cascade(f);
36230         if(this.remove){
36231            for(var id in af){
36232                if(typeof id != "function"){
36233                    var n = af[id];
36234                    if(n && n.parentNode){
36235                        n.parentNode.removeChild(n);
36236                    }
36237                }
36238            }
36239         }
36240     },
36241
36242     /**
36243      * Clears the current filter. Note: with the "remove" option
36244      * set a filter cannot be cleared.
36245      */
36246     clear : function(){
36247         var t = this.tree;
36248         var af = this.filtered;
36249         for(var id in af){
36250             if(typeof id != "function"){
36251                 var n = af[id];
36252                 if(n){
36253                     n.ui.show();
36254                 }
36255             }
36256         }
36257         this.filtered = {};
36258     }
36259 };
36260 /*
36261  * Based on:
36262  * Ext JS Library 1.1.1
36263  * Copyright(c) 2006-2007, Ext JS, LLC.
36264  *
36265  * Originally Released Under LGPL - original licence link has changed is not relivant.
36266  *
36267  * Fork - LGPL
36268  * <script type="text/javascript">
36269  */
36270  
36271
36272 /**
36273  * @class Roo.tree.TreeSorter
36274  * Provides sorting of nodes in a TreePanel
36275  * 
36276  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36277  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36278  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36279  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36280  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36281  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36282  * @constructor
36283  * @param {TreePanel} tree
36284  * @param {Object} config
36285  */
36286 Roo.tree.TreeSorter = function(tree, config){
36287     Roo.apply(this, config);
36288     tree.on("beforechildrenrendered", this.doSort, this);
36289     tree.on("append", this.updateSort, this);
36290     tree.on("insert", this.updateSort, this);
36291     
36292     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36293     var p = this.property || "text";
36294     var sortType = this.sortType;
36295     var fs = this.folderSort;
36296     var cs = this.caseSensitive === true;
36297     var leafAttr = this.leafAttr || 'leaf';
36298
36299     this.sortFn = function(n1, n2){
36300         if(fs){
36301             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36302                 return 1;
36303             }
36304             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36305                 return -1;
36306             }
36307         }
36308         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36309         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36310         if(v1 < v2){
36311                         return dsc ? +1 : -1;
36312                 }else if(v1 > v2){
36313                         return dsc ? -1 : +1;
36314         }else{
36315                 return 0;
36316         }
36317     };
36318 };
36319
36320 Roo.tree.TreeSorter.prototype = {
36321     doSort : function(node){
36322         node.sort(this.sortFn);
36323     },
36324     
36325     compareNodes : function(n1, n2){
36326         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36327     },
36328     
36329     updateSort : function(tree, node){
36330         if(node.childrenRendered){
36331             this.doSort.defer(1, this, [node]);
36332         }
36333     }
36334 };/*
36335  * Based on:
36336  * Ext JS Library 1.1.1
36337  * Copyright(c) 2006-2007, Ext JS, LLC.
36338  *
36339  * Originally Released Under LGPL - original licence link has changed is not relivant.
36340  *
36341  * Fork - LGPL
36342  * <script type="text/javascript">
36343  */
36344
36345 if(Roo.dd.DropZone){
36346     
36347 Roo.tree.TreeDropZone = function(tree, config){
36348     this.allowParentInsert = false;
36349     this.allowContainerDrop = false;
36350     this.appendOnly = false;
36351     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36352     this.tree = tree;
36353     this.lastInsertClass = "x-tree-no-status";
36354     this.dragOverData = {};
36355 };
36356
36357 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36358     ddGroup : "TreeDD",
36359     scroll:  true,
36360     
36361     expandDelay : 1000,
36362     
36363     expandNode : function(node){
36364         if(node.hasChildNodes() && !node.isExpanded()){
36365             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36366         }
36367     },
36368     
36369     queueExpand : function(node){
36370         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36371     },
36372     
36373     cancelExpand : function(){
36374         if(this.expandProcId){
36375             clearTimeout(this.expandProcId);
36376             this.expandProcId = false;
36377         }
36378     },
36379     
36380     isValidDropPoint : function(n, pt, dd, e, data){
36381         if(!n || !data){ return false; }
36382         var targetNode = n.node;
36383         var dropNode = data.node;
36384         // default drop rules
36385         if(!(targetNode && targetNode.isTarget && pt)){
36386             return false;
36387         }
36388         if(pt == "append" && targetNode.allowChildren === false){
36389             return false;
36390         }
36391         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36392             return false;
36393         }
36394         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36395             return false;
36396         }
36397         // reuse the object
36398         var overEvent = this.dragOverData;
36399         overEvent.tree = this.tree;
36400         overEvent.target = targetNode;
36401         overEvent.data = data;
36402         overEvent.point = pt;
36403         overEvent.source = dd;
36404         overEvent.rawEvent = e;
36405         overEvent.dropNode = dropNode;
36406         overEvent.cancel = false;  
36407         var result = this.tree.fireEvent("nodedragover", overEvent);
36408         return overEvent.cancel === false && result !== false;
36409     },
36410     
36411     getDropPoint : function(e, n, dd)
36412     {
36413         var tn = n.node;
36414         if(tn.isRoot){
36415             return tn.allowChildren !== false ? "append" : false; // always append for root
36416         }
36417         var dragEl = n.ddel;
36418         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36419         var y = Roo.lib.Event.getPageY(e);
36420         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36421         
36422         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36423         var noAppend = tn.allowChildren === false;
36424         if(this.appendOnly || tn.parentNode.allowChildren === false){
36425             return noAppend ? false : "append";
36426         }
36427         var noBelow = false;
36428         if(!this.allowParentInsert){
36429             noBelow = tn.hasChildNodes() && tn.isExpanded();
36430         }
36431         var q = (b - t) / (noAppend ? 2 : 3);
36432         if(y >= t && y < (t + q)){
36433             return "above";
36434         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36435             return "below";
36436         }else{
36437             return "append";
36438         }
36439     },
36440     
36441     onNodeEnter : function(n, dd, e, data)
36442     {
36443         this.cancelExpand();
36444     },
36445     
36446     onNodeOver : function(n, dd, e, data)
36447     {
36448        
36449         var pt = this.getDropPoint(e, n, dd);
36450         var node = n.node;
36451         
36452         // auto node expand check
36453         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36454             this.queueExpand(node);
36455         }else if(pt != "append"){
36456             this.cancelExpand();
36457         }
36458         
36459         // set the insert point style on the target node
36460         var returnCls = this.dropNotAllowed;
36461         if(this.isValidDropPoint(n, pt, dd, e, data)){
36462            if(pt){
36463                var el = n.ddel;
36464                var cls;
36465                if(pt == "above"){
36466                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36467                    cls = "x-tree-drag-insert-above";
36468                }else if(pt == "below"){
36469                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36470                    cls = "x-tree-drag-insert-below";
36471                }else{
36472                    returnCls = "x-tree-drop-ok-append";
36473                    cls = "x-tree-drag-append";
36474                }
36475                if(this.lastInsertClass != cls){
36476                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36477                    this.lastInsertClass = cls;
36478                }
36479            }
36480        }
36481        return returnCls;
36482     },
36483     
36484     onNodeOut : function(n, dd, e, data){
36485         
36486         this.cancelExpand();
36487         this.removeDropIndicators(n);
36488     },
36489     
36490     onNodeDrop : function(n, dd, e, data){
36491         var point = this.getDropPoint(e, n, dd);
36492         var targetNode = n.node;
36493         targetNode.ui.startDrop();
36494         if(!this.isValidDropPoint(n, point, dd, e, data)){
36495             targetNode.ui.endDrop();
36496             return false;
36497         }
36498         // first try to find the drop node
36499         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36500         var dropEvent = {
36501             tree : this.tree,
36502             target: targetNode,
36503             data: data,
36504             point: point,
36505             source: dd,
36506             rawEvent: e,
36507             dropNode: dropNode,
36508             cancel: !dropNode   
36509         };
36510         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36511         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36512             targetNode.ui.endDrop();
36513             return false;
36514         }
36515         // allow target changing
36516         targetNode = dropEvent.target;
36517         if(point == "append" && !targetNode.isExpanded()){
36518             targetNode.expand(false, null, function(){
36519                 this.completeDrop(dropEvent);
36520             }.createDelegate(this));
36521         }else{
36522             this.completeDrop(dropEvent);
36523         }
36524         return true;
36525     },
36526     
36527     completeDrop : function(de){
36528         var ns = de.dropNode, p = de.point, t = de.target;
36529         if(!(ns instanceof Array)){
36530             ns = [ns];
36531         }
36532         var n;
36533         for(var i = 0, len = ns.length; i < len; i++){
36534             n = ns[i];
36535             if(p == "above"){
36536                 t.parentNode.insertBefore(n, t);
36537             }else if(p == "below"){
36538                 t.parentNode.insertBefore(n, t.nextSibling);
36539             }else{
36540                 t.appendChild(n);
36541             }
36542         }
36543         n.ui.focus();
36544         if(this.tree.hlDrop){
36545             n.ui.highlight();
36546         }
36547         t.ui.endDrop();
36548         this.tree.fireEvent("nodedrop", de);
36549     },
36550     
36551     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36552         if(this.tree.hlDrop){
36553             dropNode.ui.focus();
36554             dropNode.ui.highlight();
36555         }
36556         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36557     },
36558     
36559     getTree : function(){
36560         return this.tree;
36561     },
36562     
36563     removeDropIndicators : function(n){
36564         if(n && n.ddel){
36565             var el = n.ddel;
36566             Roo.fly(el).removeClass([
36567                     "x-tree-drag-insert-above",
36568                     "x-tree-drag-insert-below",
36569                     "x-tree-drag-append"]);
36570             this.lastInsertClass = "_noclass";
36571         }
36572     },
36573     
36574     beforeDragDrop : function(target, e, id){
36575         this.cancelExpand();
36576         return true;
36577     },
36578     
36579     afterRepair : function(data){
36580         if(data && Roo.enableFx){
36581             data.node.ui.highlight();
36582         }
36583         this.hideProxy();
36584     } 
36585     
36586 });
36587
36588 }
36589 /*
36590  * Based on:
36591  * Ext JS Library 1.1.1
36592  * Copyright(c) 2006-2007, Ext JS, LLC.
36593  *
36594  * Originally Released Under LGPL - original licence link has changed is not relivant.
36595  *
36596  * Fork - LGPL
36597  * <script type="text/javascript">
36598  */
36599  
36600
36601 if(Roo.dd.DragZone){
36602 Roo.tree.TreeDragZone = function(tree, config){
36603     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36604     this.tree = tree;
36605 };
36606
36607 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36608     ddGroup : "TreeDD",
36609    
36610     onBeforeDrag : function(data, e){
36611         var n = data.node;
36612         return n && n.draggable && !n.disabled;
36613     },
36614      
36615     
36616     onInitDrag : function(e){
36617         var data = this.dragData;
36618         this.tree.getSelectionModel().select(data.node);
36619         this.proxy.update("");
36620         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36621         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36622     },
36623     
36624     getRepairXY : function(e, data){
36625         return data.node.ui.getDDRepairXY();
36626     },
36627     
36628     onEndDrag : function(data, e){
36629         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36630         
36631         
36632     },
36633     
36634     onValidDrop : function(dd, e, id){
36635         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36636         this.hideProxy();
36637     },
36638     
36639     beforeInvalidDrop : function(e, id){
36640         // this scrolls the original position back into view
36641         var sm = this.tree.getSelectionModel();
36642         sm.clearSelections();
36643         sm.select(this.dragData.node);
36644     }
36645 });
36646 }/*
36647  * Based on:
36648  * Ext JS Library 1.1.1
36649  * Copyright(c) 2006-2007, Ext JS, LLC.
36650  *
36651  * Originally Released Under LGPL - original licence link has changed is not relivant.
36652  *
36653  * Fork - LGPL
36654  * <script type="text/javascript">
36655  */
36656 /**
36657  * @class Roo.tree.TreeEditor
36658  * @extends Roo.Editor
36659  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36660  * as the editor field.
36661  * @constructor
36662  * @param {Object} config (used to be the tree panel.)
36663  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36664  * 
36665  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36666  * @cfg {Roo.form.TextField|Object} field The field configuration
36667  *
36668  * 
36669  */
36670 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36671     var tree = config;
36672     var field;
36673     if (oldconfig) { // old style..
36674         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36675     } else {
36676         // new style..
36677         tree = config.tree;
36678         config.field = config.field  || {};
36679         config.field.xtype = 'TextField';
36680         field = Roo.factory(config.field, Roo.form);
36681     }
36682     config = config || {};
36683     
36684     
36685     this.addEvents({
36686         /**
36687          * @event beforenodeedit
36688          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36689          * false from the handler of this event.
36690          * @param {Editor} this
36691          * @param {Roo.tree.Node} node 
36692          */
36693         "beforenodeedit" : true
36694     });
36695     
36696     //Roo.log(config);
36697     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36698
36699     this.tree = tree;
36700
36701     tree.on('beforeclick', this.beforeNodeClick, this);
36702     tree.getTreeEl().on('mousedown', this.hide, this);
36703     this.on('complete', this.updateNode, this);
36704     this.on('beforestartedit', this.fitToTree, this);
36705     this.on('startedit', this.bindScroll, this, {delay:10});
36706     this.on('specialkey', this.onSpecialKey, this);
36707 };
36708
36709 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36710     /**
36711      * @cfg {String} alignment
36712      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36713      */
36714     alignment: "l-l",
36715     // inherit
36716     autoSize: false,
36717     /**
36718      * @cfg {Boolean} hideEl
36719      * True to hide the bound element while the editor is displayed (defaults to false)
36720      */
36721     hideEl : false,
36722     /**
36723      * @cfg {String} cls
36724      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36725      */
36726     cls: "x-small-editor x-tree-editor",
36727     /**
36728      * @cfg {Boolean} shim
36729      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36730      */
36731     shim:false,
36732     // inherit
36733     shadow:"frame",
36734     /**
36735      * @cfg {Number} maxWidth
36736      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36737      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36738      * scroll and client offsets into account prior to each edit.
36739      */
36740     maxWidth: 250,
36741
36742     editDelay : 350,
36743
36744     // private
36745     fitToTree : function(ed, el){
36746         var td = this.tree.getTreeEl().dom, nd = el.dom;
36747         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36748             td.scrollLeft = nd.offsetLeft;
36749         }
36750         var w = Math.min(
36751                 this.maxWidth,
36752                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36753         this.setSize(w, '');
36754         
36755         return this.fireEvent('beforenodeedit', this, this.editNode);
36756         
36757     },
36758
36759     // private
36760     triggerEdit : function(node){
36761         this.completeEdit();
36762         this.editNode = node;
36763         this.startEdit(node.ui.textNode, node.text);
36764     },
36765
36766     // private
36767     bindScroll : function(){
36768         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36769     },
36770
36771     // private
36772     beforeNodeClick : function(node, e){
36773         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36774         this.lastClick = new Date();
36775         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36776             e.stopEvent();
36777             this.triggerEdit(node);
36778             return false;
36779         }
36780         return true;
36781     },
36782
36783     // private
36784     updateNode : function(ed, value){
36785         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36786         this.editNode.setText(value);
36787     },
36788
36789     // private
36790     onHide : function(){
36791         Roo.tree.TreeEditor.superclass.onHide.call(this);
36792         if(this.editNode){
36793             this.editNode.ui.focus();
36794         }
36795     },
36796
36797     // private
36798     onSpecialKey : function(field, e){
36799         var k = e.getKey();
36800         if(k == e.ESC){
36801             e.stopEvent();
36802             this.cancelEdit();
36803         }else if(k == e.ENTER && !e.hasModifier()){
36804             e.stopEvent();
36805             this.completeEdit();
36806         }
36807     }
36808 });//<Script type="text/javascript">
36809 /*
36810  * Based on:
36811  * Ext JS Library 1.1.1
36812  * Copyright(c) 2006-2007, Ext JS, LLC.
36813  *
36814  * Originally Released Under LGPL - original licence link has changed is not relivant.
36815  *
36816  * Fork - LGPL
36817  * <script type="text/javascript">
36818  */
36819  
36820 /**
36821  * Not documented??? - probably should be...
36822  */
36823
36824 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36825     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36826     
36827     renderElements : function(n, a, targetNode, bulkRender){
36828         //consel.log("renderElements?");
36829         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36830
36831         var t = n.getOwnerTree();
36832         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36833         
36834         var cols = t.columns;
36835         var bw = t.borderWidth;
36836         var c = cols[0];
36837         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36838          var cb = typeof a.checked == "boolean";
36839         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36840         var colcls = 'x-t-' + tid + '-c0';
36841         var buf = [
36842             '<li class="x-tree-node">',
36843             
36844                 
36845                 '<div class="x-tree-node-el ', a.cls,'">',
36846                     // extran...
36847                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36848                 
36849                 
36850                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36851                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36852                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36853                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36854                            (a.iconCls ? ' '+a.iconCls : ''),
36855                            '" unselectable="on" />',
36856                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36857                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36858                              
36859                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36860                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36861                             '<span unselectable="on" qtip="' + tx + '">',
36862                              tx,
36863                              '</span></a>' ,
36864                     '</div>',
36865                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36866                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36867                  ];
36868         for(var i = 1, len = cols.length; i < len; i++){
36869             c = cols[i];
36870             colcls = 'x-t-' + tid + '-c' +i;
36871             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36872             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36873                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36874                       "</div>");
36875          }
36876          
36877          buf.push(
36878             '</a>',
36879             '<div class="x-clear"></div></div>',
36880             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36881             "</li>");
36882         
36883         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36884             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36885                                 n.nextSibling.ui.getEl(), buf.join(""));
36886         }else{
36887             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36888         }
36889         var el = this.wrap.firstChild;
36890         this.elRow = el;
36891         this.elNode = el.firstChild;
36892         this.ranchor = el.childNodes[1];
36893         this.ctNode = this.wrap.childNodes[1];
36894         var cs = el.firstChild.childNodes;
36895         this.indentNode = cs[0];
36896         this.ecNode = cs[1];
36897         this.iconNode = cs[2];
36898         var index = 3;
36899         if(cb){
36900             this.checkbox = cs[3];
36901             index++;
36902         }
36903         this.anchor = cs[index];
36904         
36905         this.textNode = cs[index].firstChild;
36906         
36907         //el.on("click", this.onClick, this);
36908         //el.on("dblclick", this.onDblClick, this);
36909         
36910         
36911        // console.log(this);
36912     },
36913     initEvents : function(){
36914         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36915         
36916             
36917         var a = this.ranchor;
36918
36919         var el = Roo.get(a);
36920
36921         if(Roo.isOpera){ // opera render bug ignores the CSS
36922             el.setStyle("text-decoration", "none");
36923         }
36924
36925         el.on("click", this.onClick, this);
36926         el.on("dblclick", this.onDblClick, this);
36927         el.on("contextmenu", this.onContextMenu, this);
36928         
36929     },
36930     
36931     /*onSelectedChange : function(state){
36932         if(state){
36933             this.focus();
36934             this.addClass("x-tree-selected");
36935         }else{
36936             //this.blur();
36937             this.removeClass("x-tree-selected");
36938         }
36939     },*/
36940     addClass : function(cls){
36941         if(this.elRow){
36942             Roo.fly(this.elRow).addClass(cls);
36943         }
36944         
36945     },
36946     
36947     
36948     removeClass : function(cls){
36949         if(this.elRow){
36950             Roo.fly(this.elRow).removeClass(cls);
36951         }
36952     }
36953
36954     
36955     
36956 });//<Script type="text/javascript">
36957
36958 /*
36959  * Based on:
36960  * Ext JS Library 1.1.1
36961  * Copyright(c) 2006-2007, Ext JS, LLC.
36962  *
36963  * Originally Released Under LGPL - original licence link has changed is not relivant.
36964  *
36965  * Fork - LGPL
36966  * <script type="text/javascript">
36967  */
36968  
36969
36970 /**
36971  * @class Roo.tree.ColumnTree
36972  * @extends Roo.data.TreePanel
36973  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36974  * @cfg {int} borderWidth  compined right/left border allowance
36975  * @constructor
36976  * @param {String/HTMLElement/Element} el The container element
36977  * @param {Object} config
36978  */
36979 Roo.tree.ColumnTree =  function(el, config)
36980 {
36981    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36982    this.addEvents({
36983         /**
36984         * @event resize
36985         * Fire this event on a container when it resizes
36986         * @param {int} w Width
36987         * @param {int} h Height
36988         */
36989        "resize" : true
36990     });
36991     this.on('resize', this.onResize, this);
36992 };
36993
36994 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36995     //lines:false,
36996     
36997     
36998     borderWidth: Roo.isBorderBox ? 0 : 2, 
36999     headEls : false,
37000     
37001     render : function(){
37002         // add the header.....
37003        
37004         Roo.tree.ColumnTree.superclass.render.apply(this);
37005         
37006         this.el.addClass('x-column-tree');
37007         
37008         this.headers = this.el.createChild(
37009             {cls:'x-tree-headers'},this.innerCt.dom);
37010    
37011         var cols = this.columns, c;
37012         var totalWidth = 0;
37013         this.headEls = [];
37014         var  len = cols.length;
37015         for(var i = 0; i < len; i++){
37016              c = cols[i];
37017              totalWidth += c.width;
37018             this.headEls.push(this.headers.createChild({
37019                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37020                  cn: {
37021                      cls:'x-tree-hd-text',
37022                      html: c.header
37023                  },
37024                  style:'width:'+(c.width-this.borderWidth)+'px;'
37025              }));
37026         }
37027         this.headers.createChild({cls:'x-clear'});
37028         // prevent floats from wrapping when clipped
37029         this.headers.setWidth(totalWidth);
37030         //this.innerCt.setWidth(totalWidth);
37031         this.innerCt.setStyle({ overflow: 'auto' });
37032         this.onResize(this.width, this.height);
37033              
37034         
37035     },
37036     onResize : function(w,h)
37037     {
37038         this.height = h;
37039         this.width = w;
37040         // resize cols..
37041         this.innerCt.setWidth(this.width);
37042         this.innerCt.setHeight(this.height-20);
37043         
37044         // headers...
37045         var cols = this.columns, c;
37046         var totalWidth = 0;
37047         var expEl = false;
37048         var len = cols.length;
37049         for(var i = 0; i < len; i++){
37050             c = cols[i];
37051             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37052                 // it's the expander..
37053                 expEl  = this.headEls[i];
37054                 continue;
37055             }
37056             totalWidth += c.width;
37057             
37058         }
37059         if (expEl) {
37060             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37061         }
37062         this.headers.setWidth(w-20);
37063
37064         
37065         
37066         
37067     }
37068 });
37069 /*
37070  * Based on:
37071  * Ext JS Library 1.1.1
37072  * Copyright(c) 2006-2007, Ext JS, LLC.
37073  *
37074  * Originally Released Under LGPL - original licence link has changed is not relivant.
37075  *
37076  * Fork - LGPL
37077  * <script type="text/javascript">
37078  */
37079  
37080 /**
37081  * @class Roo.menu.Menu
37082  * @extends Roo.util.Observable
37083  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37084  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37085  * @constructor
37086  * Creates a new Menu
37087  * @param {Object} config Configuration options
37088  */
37089 Roo.menu.Menu = function(config){
37090     Roo.apply(this, config);
37091     this.id = this.id || Roo.id();
37092     this.addEvents({
37093         /**
37094          * @event beforeshow
37095          * Fires before this menu is displayed
37096          * @param {Roo.menu.Menu} this
37097          */
37098         beforeshow : true,
37099         /**
37100          * @event beforehide
37101          * Fires before this menu is hidden
37102          * @param {Roo.menu.Menu} this
37103          */
37104         beforehide : true,
37105         /**
37106          * @event show
37107          * Fires after this menu is displayed
37108          * @param {Roo.menu.Menu} this
37109          */
37110         show : true,
37111         /**
37112          * @event hide
37113          * Fires after this menu is hidden
37114          * @param {Roo.menu.Menu} this
37115          */
37116         hide : true,
37117         /**
37118          * @event click
37119          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37120          * @param {Roo.menu.Menu} this
37121          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37122          * @param {Roo.EventObject} e
37123          */
37124         click : true,
37125         /**
37126          * @event mouseover
37127          * Fires when the mouse is hovering over this menu
37128          * @param {Roo.menu.Menu} this
37129          * @param {Roo.EventObject} e
37130          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37131          */
37132         mouseover : true,
37133         /**
37134          * @event mouseout
37135          * Fires when the mouse exits this menu
37136          * @param {Roo.menu.Menu} this
37137          * @param {Roo.EventObject} e
37138          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37139          */
37140         mouseout : true,
37141         /**
37142          * @event itemclick
37143          * Fires when a menu item contained in this menu is clicked
37144          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37145          * @param {Roo.EventObject} e
37146          */
37147         itemclick: true
37148     });
37149     if (this.registerMenu) {
37150         Roo.menu.MenuMgr.register(this);
37151     }
37152     
37153     var mis = this.items;
37154     this.items = new Roo.util.MixedCollection();
37155     if(mis){
37156         this.add.apply(this, mis);
37157     }
37158 };
37159
37160 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37161     /**
37162      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37163      */
37164     minWidth : 120,
37165     /**
37166      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37167      * for bottom-right shadow (defaults to "sides")
37168      */
37169     shadow : "sides",
37170     /**
37171      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37172      * this menu (defaults to "tl-tr?")
37173      */
37174     subMenuAlign : "tl-tr?",
37175     /**
37176      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37177      * relative to its element of origin (defaults to "tl-bl?")
37178      */
37179     defaultAlign : "tl-bl?",
37180     /**
37181      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37182      */
37183     allowOtherMenus : false,
37184     /**
37185      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37186      */
37187     registerMenu : true,
37188
37189     hidden:true,
37190
37191     // private
37192     render : function(){
37193         if(this.el){
37194             return;
37195         }
37196         var el = this.el = new Roo.Layer({
37197             cls: "x-menu",
37198             shadow:this.shadow,
37199             constrain: false,
37200             parentEl: this.parentEl || document.body,
37201             zindex:15000
37202         });
37203
37204         this.keyNav = new Roo.menu.MenuNav(this);
37205
37206         if(this.plain){
37207             el.addClass("x-menu-plain");
37208         }
37209         if(this.cls){
37210             el.addClass(this.cls);
37211         }
37212         // generic focus element
37213         this.focusEl = el.createChild({
37214             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37215         });
37216         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37217         //disabling touch- as it's causing issues ..
37218         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37219         ul.on('click'   , this.onClick, this);
37220         
37221         
37222         ul.on("mouseover", this.onMouseOver, this);
37223         ul.on("mouseout", this.onMouseOut, this);
37224         this.items.each(function(item){
37225             if (item.hidden) {
37226                 return;
37227             }
37228             
37229             var li = document.createElement("li");
37230             li.className = "x-menu-list-item";
37231             ul.dom.appendChild(li);
37232             item.render(li, this);
37233         }, this);
37234         this.ul = ul;
37235         this.autoWidth();
37236     },
37237
37238     // private
37239     autoWidth : function(){
37240         var el = this.el, ul = this.ul;
37241         if(!el){
37242             return;
37243         }
37244         var w = this.width;
37245         if(w){
37246             el.setWidth(w);
37247         }else if(Roo.isIE){
37248             el.setWidth(this.minWidth);
37249             var t = el.dom.offsetWidth; // force recalc
37250             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37251         }
37252     },
37253
37254     // private
37255     delayAutoWidth : function(){
37256         if(this.rendered){
37257             if(!this.awTask){
37258                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37259             }
37260             this.awTask.delay(20);
37261         }
37262     },
37263
37264     // private
37265     findTargetItem : function(e){
37266         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37267         if(t && t.menuItemId){
37268             return this.items.get(t.menuItemId);
37269         }
37270     },
37271
37272     // private
37273     onClick : function(e){
37274         Roo.log("menu.onClick");
37275         var t = this.findTargetItem(e);
37276         if(!t){
37277             return;
37278         }
37279         Roo.log(e);
37280         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37281             if(t == this.activeItem && t.shouldDeactivate(e)){
37282                 this.activeItem.deactivate();
37283                 delete this.activeItem;
37284                 return;
37285             }
37286             if(t.canActivate){
37287                 this.setActiveItem(t, true);
37288             }
37289             return;
37290             
37291             
37292         }
37293         
37294         t.onClick(e);
37295         this.fireEvent("click", this, t, e);
37296     },
37297
37298     // private
37299     setActiveItem : function(item, autoExpand){
37300         if(item != this.activeItem){
37301             if(this.activeItem){
37302                 this.activeItem.deactivate();
37303             }
37304             this.activeItem = item;
37305             item.activate(autoExpand);
37306         }else if(autoExpand){
37307             item.expandMenu();
37308         }
37309     },
37310
37311     // private
37312     tryActivate : function(start, step){
37313         var items = this.items;
37314         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37315             var item = items.get(i);
37316             if(!item.disabled && item.canActivate){
37317                 this.setActiveItem(item, false);
37318                 return item;
37319             }
37320         }
37321         return false;
37322     },
37323
37324     // private
37325     onMouseOver : function(e){
37326         var t;
37327         if(t = this.findTargetItem(e)){
37328             if(t.canActivate && !t.disabled){
37329                 this.setActiveItem(t, true);
37330             }
37331         }
37332         this.fireEvent("mouseover", this, e, t);
37333     },
37334
37335     // private
37336     onMouseOut : function(e){
37337         var t;
37338         if(t = this.findTargetItem(e)){
37339             if(t == this.activeItem && t.shouldDeactivate(e)){
37340                 this.activeItem.deactivate();
37341                 delete this.activeItem;
37342             }
37343         }
37344         this.fireEvent("mouseout", this, e, t);
37345     },
37346
37347     /**
37348      * Read-only.  Returns true if the menu is currently displayed, else false.
37349      * @type Boolean
37350      */
37351     isVisible : function(){
37352         return this.el && !this.hidden;
37353     },
37354
37355     /**
37356      * Displays this menu relative to another element
37357      * @param {String/HTMLElement/Roo.Element} element The element to align to
37358      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37359      * the element (defaults to this.defaultAlign)
37360      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37361      */
37362     show : function(el, pos, parentMenu){
37363         this.parentMenu = parentMenu;
37364         if(!this.el){
37365             this.render();
37366         }
37367         this.fireEvent("beforeshow", this);
37368         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37369     },
37370
37371     /**
37372      * Displays this menu at a specific xy position
37373      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37374      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37375      */
37376     showAt : function(xy, parentMenu, /* private: */_e){
37377         this.parentMenu = parentMenu;
37378         if(!this.el){
37379             this.render();
37380         }
37381         if(_e !== false){
37382             this.fireEvent("beforeshow", this);
37383             xy = this.el.adjustForConstraints(xy);
37384         }
37385         this.el.setXY(xy);
37386         this.el.show();
37387         this.hidden = false;
37388         this.focus();
37389         this.fireEvent("show", this);
37390     },
37391
37392     focus : function(){
37393         if(!this.hidden){
37394             this.doFocus.defer(50, this);
37395         }
37396     },
37397
37398     doFocus : function(){
37399         if(!this.hidden){
37400             this.focusEl.focus();
37401         }
37402     },
37403
37404     /**
37405      * Hides this menu and optionally all parent menus
37406      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37407      */
37408     hide : function(deep){
37409         if(this.el && this.isVisible()){
37410             this.fireEvent("beforehide", this);
37411             if(this.activeItem){
37412                 this.activeItem.deactivate();
37413                 this.activeItem = null;
37414             }
37415             this.el.hide();
37416             this.hidden = true;
37417             this.fireEvent("hide", this);
37418         }
37419         if(deep === true && this.parentMenu){
37420             this.parentMenu.hide(true);
37421         }
37422     },
37423
37424     /**
37425      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37426      * Any of the following are valid:
37427      * <ul>
37428      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37429      * <li>An HTMLElement object which will be converted to a menu item</li>
37430      * <li>A menu item config object that will be created as a new menu item</li>
37431      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37432      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37433      * </ul>
37434      * Usage:
37435      * <pre><code>
37436 // Create the menu
37437 var menu = new Roo.menu.Menu();
37438
37439 // Create a menu item to add by reference
37440 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37441
37442 // Add a bunch of items at once using different methods.
37443 // Only the last item added will be returned.
37444 var item = menu.add(
37445     menuItem,                // add existing item by ref
37446     'Dynamic Item',          // new TextItem
37447     '-',                     // new separator
37448     { text: 'Config Item' }  // new item by config
37449 );
37450 </code></pre>
37451      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37452      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37453      */
37454     add : function(){
37455         var a = arguments, l = a.length, item;
37456         for(var i = 0; i < l; i++){
37457             var el = a[i];
37458             if ((typeof(el) == "object") && el.xtype && el.xns) {
37459                 el = Roo.factory(el, Roo.menu);
37460             }
37461             
37462             if(el.render){ // some kind of Item
37463                 item = this.addItem(el);
37464             }else if(typeof el == "string"){ // string
37465                 if(el == "separator" || el == "-"){
37466                     item = this.addSeparator();
37467                 }else{
37468                     item = this.addText(el);
37469                 }
37470             }else if(el.tagName || el.el){ // element
37471                 item = this.addElement(el);
37472             }else if(typeof el == "object"){ // must be menu item config?
37473                 item = this.addMenuItem(el);
37474             }
37475         }
37476         return item;
37477     },
37478
37479     /**
37480      * Returns this menu's underlying {@link Roo.Element} object
37481      * @return {Roo.Element} The element
37482      */
37483     getEl : function(){
37484         if(!this.el){
37485             this.render();
37486         }
37487         return this.el;
37488     },
37489
37490     /**
37491      * Adds a separator bar to the menu
37492      * @return {Roo.menu.Item} The menu item that was added
37493      */
37494     addSeparator : function(){
37495         return this.addItem(new Roo.menu.Separator());
37496     },
37497
37498     /**
37499      * Adds an {@link Roo.Element} object to the menu
37500      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37501      * @return {Roo.menu.Item} The menu item that was added
37502      */
37503     addElement : function(el){
37504         return this.addItem(new Roo.menu.BaseItem(el));
37505     },
37506
37507     /**
37508      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37509      * @param {Roo.menu.Item} item The menu item to add
37510      * @return {Roo.menu.Item} The menu item that was added
37511      */
37512     addItem : function(item){
37513         this.items.add(item);
37514         if(this.ul){
37515             var li = document.createElement("li");
37516             li.className = "x-menu-list-item";
37517             this.ul.dom.appendChild(li);
37518             item.render(li, this);
37519             this.delayAutoWidth();
37520         }
37521         return item;
37522     },
37523
37524     /**
37525      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37526      * @param {Object} config A MenuItem config object
37527      * @return {Roo.menu.Item} The menu item that was added
37528      */
37529     addMenuItem : function(config){
37530         if(!(config instanceof Roo.menu.Item)){
37531             if(typeof config.checked == "boolean"){ // must be check menu item config?
37532                 config = new Roo.menu.CheckItem(config);
37533             }else{
37534                 config = new Roo.menu.Item(config);
37535             }
37536         }
37537         return this.addItem(config);
37538     },
37539
37540     /**
37541      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37542      * @param {String} text The text to display in the menu item
37543      * @return {Roo.menu.Item} The menu item that was added
37544      */
37545     addText : function(text){
37546         return this.addItem(new Roo.menu.TextItem({ text : text }));
37547     },
37548
37549     /**
37550      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37551      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37552      * @param {Roo.menu.Item} item The menu item to add
37553      * @return {Roo.menu.Item} The menu item that was added
37554      */
37555     insert : function(index, item){
37556         this.items.insert(index, item);
37557         if(this.ul){
37558             var li = document.createElement("li");
37559             li.className = "x-menu-list-item";
37560             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37561             item.render(li, this);
37562             this.delayAutoWidth();
37563         }
37564         return item;
37565     },
37566
37567     /**
37568      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37569      * @param {Roo.menu.Item} item The menu item to remove
37570      */
37571     remove : function(item){
37572         this.items.removeKey(item.id);
37573         item.destroy();
37574     },
37575
37576     /**
37577      * Removes and destroys all items in the menu
37578      */
37579     removeAll : function(){
37580         var f;
37581         while(f = this.items.first()){
37582             this.remove(f);
37583         }
37584     }
37585 });
37586
37587 // MenuNav is a private utility class used internally by the Menu
37588 Roo.menu.MenuNav = function(menu){
37589     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37590     this.scope = this.menu = menu;
37591 };
37592
37593 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37594     doRelay : function(e, h){
37595         var k = e.getKey();
37596         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37597             this.menu.tryActivate(0, 1);
37598             return false;
37599         }
37600         return h.call(this.scope || this, e, this.menu);
37601     },
37602
37603     up : function(e, m){
37604         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37605             m.tryActivate(m.items.length-1, -1);
37606         }
37607     },
37608
37609     down : function(e, m){
37610         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37611             m.tryActivate(0, 1);
37612         }
37613     },
37614
37615     right : function(e, m){
37616         if(m.activeItem){
37617             m.activeItem.expandMenu(true);
37618         }
37619     },
37620
37621     left : function(e, m){
37622         m.hide();
37623         if(m.parentMenu && m.parentMenu.activeItem){
37624             m.parentMenu.activeItem.activate();
37625         }
37626     },
37627
37628     enter : function(e, m){
37629         if(m.activeItem){
37630             e.stopPropagation();
37631             m.activeItem.onClick(e);
37632             m.fireEvent("click", this, m.activeItem);
37633             return true;
37634         }
37635     }
37636 });/*
37637  * Based on:
37638  * Ext JS Library 1.1.1
37639  * Copyright(c) 2006-2007, Ext JS, LLC.
37640  *
37641  * Originally Released Under LGPL - original licence link has changed is not relivant.
37642  *
37643  * Fork - LGPL
37644  * <script type="text/javascript">
37645  */
37646  
37647 /**
37648  * @class Roo.menu.MenuMgr
37649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37650  * @singleton
37651  */
37652 Roo.menu.MenuMgr = function(){
37653    var menus, active, groups = {}, attached = false, lastShow = new Date();
37654
37655    // private - called when first menu is created
37656    function init(){
37657        menus = {};
37658        active = new Roo.util.MixedCollection();
37659        Roo.get(document).addKeyListener(27, function(){
37660            if(active.length > 0){
37661                hideAll();
37662            }
37663        });
37664    }
37665
37666    // private
37667    function hideAll(){
37668        if(active && active.length > 0){
37669            var c = active.clone();
37670            c.each(function(m){
37671                m.hide();
37672            });
37673        }
37674    }
37675
37676    // private
37677    function onHide(m){
37678        active.remove(m);
37679        if(active.length < 1){
37680            Roo.get(document).un("mousedown", onMouseDown);
37681            attached = false;
37682        }
37683    }
37684
37685    // private
37686    function onShow(m){
37687        var last = active.last();
37688        lastShow = new Date();
37689        active.add(m);
37690        if(!attached){
37691            Roo.get(document).on("mousedown", onMouseDown);
37692            attached = true;
37693        }
37694        if(m.parentMenu){
37695           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37696           m.parentMenu.activeChild = m;
37697        }else if(last && last.isVisible()){
37698           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37699        }
37700    }
37701
37702    // private
37703    function onBeforeHide(m){
37704        if(m.activeChild){
37705            m.activeChild.hide();
37706        }
37707        if(m.autoHideTimer){
37708            clearTimeout(m.autoHideTimer);
37709            delete m.autoHideTimer;
37710        }
37711    }
37712
37713    // private
37714    function onBeforeShow(m){
37715        var pm = m.parentMenu;
37716        if(!pm && !m.allowOtherMenus){
37717            hideAll();
37718        }else if(pm && pm.activeChild && active != m){
37719            pm.activeChild.hide();
37720        }
37721    }
37722
37723    // private
37724    function onMouseDown(e){
37725        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37726            hideAll();
37727        }
37728    }
37729
37730    // private
37731    function onBeforeCheck(mi, state){
37732        if(state){
37733            var g = groups[mi.group];
37734            for(var i = 0, l = g.length; i < l; i++){
37735                if(g[i] != mi){
37736                    g[i].setChecked(false);
37737                }
37738            }
37739        }
37740    }
37741
37742    return {
37743
37744        /**
37745         * Hides all menus that are currently visible
37746         */
37747        hideAll : function(){
37748             hideAll();  
37749        },
37750
37751        // private
37752        register : function(menu){
37753            if(!menus){
37754                init();
37755            }
37756            menus[menu.id] = menu;
37757            menu.on("beforehide", onBeforeHide);
37758            menu.on("hide", onHide);
37759            menu.on("beforeshow", onBeforeShow);
37760            menu.on("show", onShow);
37761            var g = menu.group;
37762            if(g && menu.events["checkchange"]){
37763                if(!groups[g]){
37764                    groups[g] = [];
37765                }
37766                groups[g].push(menu);
37767                menu.on("checkchange", onCheck);
37768            }
37769        },
37770
37771         /**
37772          * Returns a {@link Roo.menu.Menu} object
37773          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37774          * be used to generate and return a new Menu instance.
37775          */
37776        get : function(menu){
37777            if(typeof menu == "string"){ // menu id
37778                return menus[menu];
37779            }else if(menu.events){  // menu instance
37780                return menu;
37781            }else if(typeof menu.length == 'number'){ // array of menu items?
37782                return new Roo.menu.Menu({items:menu});
37783            }else{ // otherwise, must be a config
37784                return new Roo.menu.Menu(menu);
37785            }
37786        },
37787
37788        // private
37789        unregister : function(menu){
37790            delete menus[menu.id];
37791            menu.un("beforehide", onBeforeHide);
37792            menu.un("hide", onHide);
37793            menu.un("beforeshow", onBeforeShow);
37794            menu.un("show", onShow);
37795            var g = menu.group;
37796            if(g && menu.events["checkchange"]){
37797                groups[g].remove(menu);
37798                menu.un("checkchange", onCheck);
37799            }
37800        },
37801
37802        // private
37803        registerCheckable : function(menuItem){
37804            var g = menuItem.group;
37805            if(g){
37806                if(!groups[g]){
37807                    groups[g] = [];
37808                }
37809                groups[g].push(menuItem);
37810                menuItem.on("beforecheckchange", onBeforeCheck);
37811            }
37812        },
37813
37814        // private
37815        unregisterCheckable : function(menuItem){
37816            var g = menuItem.group;
37817            if(g){
37818                groups[g].remove(menuItem);
37819                menuItem.un("beforecheckchange", onBeforeCheck);
37820            }
37821        }
37822    };
37823 }();/*
37824  * Based on:
37825  * Ext JS Library 1.1.1
37826  * Copyright(c) 2006-2007, Ext JS, LLC.
37827  *
37828  * Originally Released Under LGPL - original licence link has changed is not relivant.
37829  *
37830  * Fork - LGPL
37831  * <script type="text/javascript">
37832  */
37833  
37834
37835 /**
37836  * @class Roo.menu.BaseItem
37837  * @extends Roo.Component
37838  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37839  * management and base configuration options shared by all menu components.
37840  * @constructor
37841  * Creates a new BaseItem
37842  * @param {Object} config Configuration options
37843  */
37844 Roo.menu.BaseItem = function(config){
37845     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37846
37847     this.addEvents({
37848         /**
37849          * @event click
37850          * Fires when this item is clicked
37851          * @param {Roo.menu.BaseItem} this
37852          * @param {Roo.EventObject} e
37853          */
37854         click: true,
37855         /**
37856          * @event activate
37857          * Fires when this item is activated
37858          * @param {Roo.menu.BaseItem} this
37859          */
37860         activate : true,
37861         /**
37862          * @event deactivate
37863          * Fires when this item is deactivated
37864          * @param {Roo.menu.BaseItem} this
37865          */
37866         deactivate : true
37867     });
37868
37869     if(this.handler){
37870         this.on("click", this.handler, this.scope, true);
37871     }
37872 };
37873
37874 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37875     /**
37876      * @cfg {Function} handler
37877      * A function that will handle the click event of this menu item (defaults to undefined)
37878      */
37879     /**
37880      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37881      */
37882     canActivate : false,
37883     
37884      /**
37885      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37886      */
37887     hidden: false,
37888     
37889     /**
37890      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37891      */
37892     activeClass : "x-menu-item-active",
37893     /**
37894      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37895      */
37896     hideOnClick : true,
37897     /**
37898      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37899      */
37900     hideDelay : 100,
37901
37902     // private
37903     ctype: "Roo.menu.BaseItem",
37904
37905     // private
37906     actionMode : "container",
37907
37908     // private
37909     render : function(container, parentMenu){
37910         this.parentMenu = parentMenu;
37911         Roo.menu.BaseItem.superclass.render.call(this, container);
37912         this.container.menuItemId = this.id;
37913     },
37914
37915     // private
37916     onRender : function(container, position){
37917         this.el = Roo.get(this.el);
37918         container.dom.appendChild(this.el.dom);
37919     },
37920
37921     // private
37922     onClick : function(e){
37923         if(!this.disabled && this.fireEvent("click", this, e) !== false
37924                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37925             this.handleClick(e);
37926         }else{
37927             e.stopEvent();
37928         }
37929     },
37930
37931     // private
37932     activate : function(){
37933         if(this.disabled){
37934             return false;
37935         }
37936         var li = this.container;
37937         li.addClass(this.activeClass);
37938         this.region = li.getRegion().adjust(2, 2, -2, -2);
37939         this.fireEvent("activate", this);
37940         return true;
37941     },
37942
37943     // private
37944     deactivate : function(){
37945         this.container.removeClass(this.activeClass);
37946         this.fireEvent("deactivate", this);
37947     },
37948
37949     // private
37950     shouldDeactivate : function(e){
37951         return !this.region || !this.region.contains(e.getPoint());
37952     },
37953
37954     // private
37955     handleClick : function(e){
37956         if(this.hideOnClick){
37957             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37958         }
37959     },
37960
37961     // private
37962     expandMenu : function(autoActivate){
37963         // do nothing
37964     },
37965
37966     // private
37967     hideMenu : function(){
37968         // do nothing
37969     }
37970 });/*
37971  * Based on:
37972  * Ext JS Library 1.1.1
37973  * Copyright(c) 2006-2007, Ext JS, LLC.
37974  *
37975  * Originally Released Under LGPL - original licence link has changed is not relivant.
37976  *
37977  * Fork - LGPL
37978  * <script type="text/javascript">
37979  */
37980  
37981 /**
37982  * @class Roo.menu.Adapter
37983  * @extends Roo.menu.BaseItem
37984  * 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.
37985  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37986  * @constructor
37987  * Creates a new Adapter
37988  * @param {Object} config Configuration options
37989  */
37990 Roo.menu.Adapter = function(component, config){
37991     Roo.menu.Adapter.superclass.constructor.call(this, config);
37992     this.component = component;
37993 };
37994 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37995     // private
37996     canActivate : true,
37997
37998     // private
37999     onRender : function(container, position){
38000         this.component.render(container);
38001         this.el = this.component.getEl();
38002     },
38003
38004     // private
38005     activate : function(){
38006         if(this.disabled){
38007             return false;
38008         }
38009         this.component.focus();
38010         this.fireEvent("activate", this);
38011         return true;
38012     },
38013
38014     // private
38015     deactivate : function(){
38016         this.fireEvent("deactivate", this);
38017     },
38018
38019     // private
38020     disable : function(){
38021         this.component.disable();
38022         Roo.menu.Adapter.superclass.disable.call(this);
38023     },
38024
38025     // private
38026     enable : function(){
38027         this.component.enable();
38028         Roo.menu.Adapter.superclass.enable.call(this);
38029     }
38030 });/*
38031  * Based on:
38032  * Ext JS Library 1.1.1
38033  * Copyright(c) 2006-2007, Ext JS, LLC.
38034  *
38035  * Originally Released Under LGPL - original licence link has changed is not relivant.
38036  *
38037  * Fork - LGPL
38038  * <script type="text/javascript">
38039  */
38040
38041 /**
38042  * @class Roo.menu.TextItem
38043  * @extends Roo.menu.BaseItem
38044  * Adds a static text string to a menu, usually used as either a heading or group separator.
38045  * Note: old style constructor with text is still supported.
38046  * 
38047  * @constructor
38048  * Creates a new TextItem
38049  * @param {Object} cfg Configuration
38050  */
38051 Roo.menu.TextItem = function(cfg){
38052     if (typeof(cfg) == 'string') {
38053         this.text = cfg;
38054     } else {
38055         Roo.apply(this,cfg);
38056     }
38057     
38058     Roo.menu.TextItem.superclass.constructor.call(this);
38059 };
38060
38061 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38062     /**
38063      * @cfg {Boolean} text Text to show on item.
38064      */
38065     text : '',
38066     
38067     /**
38068      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38069      */
38070     hideOnClick : false,
38071     /**
38072      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38073      */
38074     itemCls : "x-menu-text",
38075
38076     // private
38077     onRender : function(){
38078         var s = document.createElement("span");
38079         s.className = this.itemCls;
38080         s.innerHTML = this.text;
38081         this.el = s;
38082         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38083     }
38084 });/*
38085  * Based on:
38086  * Ext JS Library 1.1.1
38087  * Copyright(c) 2006-2007, Ext JS, LLC.
38088  *
38089  * Originally Released Under LGPL - original licence link has changed is not relivant.
38090  *
38091  * Fork - LGPL
38092  * <script type="text/javascript">
38093  */
38094
38095 /**
38096  * @class Roo.menu.Separator
38097  * @extends Roo.menu.BaseItem
38098  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38099  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38100  * @constructor
38101  * @param {Object} config Configuration options
38102  */
38103 Roo.menu.Separator = function(config){
38104     Roo.menu.Separator.superclass.constructor.call(this, config);
38105 };
38106
38107 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38108     /**
38109      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38110      */
38111     itemCls : "x-menu-sep",
38112     /**
38113      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38114      */
38115     hideOnClick : false,
38116
38117     // private
38118     onRender : function(li){
38119         var s = document.createElement("span");
38120         s.className = this.itemCls;
38121         s.innerHTML = "&#160;";
38122         this.el = s;
38123         li.addClass("x-menu-sep-li");
38124         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38125     }
38126 });/*
38127  * Based on:
38128  * Ext JS Library 1.1.1
38129  * Copyright(c) 2006-2007, Ext JS, LLC.
38130  *
38131  * Originally Released Under LGPL - original licence link has changed is not relivant.
38132  *
38133  * Fork - LGPL
38134  * <script type="text/javascript">
38135  */
38136 /**
38137  * @class Roo.menu.Item
38138  * @extends Roo.menu.BaseItem
38139  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38140  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38141  * activation and click handling.
38142  * @constructor
38143  * Creates a new Item
38144  * @param {Object} config Configuration options
38145  */
38146 Roo.menu.Item = function(config){
38147     Roo.menu.Item.superclass.constructor.call(this, config);
38148     if(this.menu){
38149         this.menu = Roo.menu.MenuMgr.get(this.menu);
38150     }
38151 };
38152 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38153     
38154     /**
38155      * @cfg {String} text
38156      * The text to show on the menu item.
38157      */
38158     text: '',
38159      /**
38160      * @cfg {String} HTML to render in menu
38161      * The text to show on the menu item (HTML version).
38162      */
38163     html: '',
38164     /**
38165      * @cfg {String} icon
38166      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38167      */
38168     icon: undefined,
38169     /**
38170      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38171      */
38172     itemCls : "x-menu-item",
38173     /**
38174      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38175      */
38176     canActivate : true,
38177     /**
38178      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38179      */
38180     showDelay: 200,
38181     // doc'd in BaseItem
38182     hideDelay: 200,
38183
38184     // private
38185     ctype: "Roo.menu.Item",
38186     
38187     // private
38188     onRender : function(container, position){
38189         var el = document.createElement("a");
38190         el.hideFocus = true;
38191         el.unselectable = "on";
38192         el.href = this.href || "#";
38193         if(this.hrefTarget){
38194             el.target = this.hrefTarget;
38195         }
38196         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38197         
38198         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38199         
38200         el.innerHTML = String.format(
38201                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38202                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38203         this.el = el;
38204         Roo.menu.Item.superclass.onRender.call(this, container, position);
38205     },
38206
38207     /**
38208      * Sets the text to display in this menu item
38209      * @param {String} text The text to display
38210      * @param {Boolean} isHTML true to indicate text is pure html.
38211      */
38212     setText : function(text, isHTML){
38213         if (isHTML) {
38214             this.html = text;
38215         } else {
38216             this.text = text;
38217             this.html = '';
38218         }
38219         if(this.rendered){
38220             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38221      
38222             this.el.update(String.format(
38223                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38224                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38225             this.parentMenu.autoWidth();
38226         }
38227     },
38228
38229     // private
38230     handleClick : function(e){
38231         if(!this.href){ // if no link defined, stop the event automatically
38232             e.stopEvent();
38233         }
38234         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38235     },
38236
38237     // private
38238     activate : function(autoExpand){
38239         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38240             this.focus();
38241             if(autoExpand){
38242                 this.expandMenu();
38243             }
38244         }
38245         return true;
38246     },
38247
38248     // private
38249     shouldDeactivate : function(e){
38250         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38251             if(this.menu && this.menu.isVisible()){
38252                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38253             }
38254             return true;
38255         }
38256         return false;
38257     },
38258
38259     // private
38260     deactivate : function(){
38261         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38262         this.hideMenu();
38263     },
38264
38265     // private
38266     expandMenu : function(autoActivate){
38267         if(!this.disabled && this.menu){
38268             clearTimeout(this.hideTimer);
38269             delete this.hideTimer;
38270             if(!this.menu.isVisible() && !this.showTimer){
38271                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38272             }else if (this.menu.isVisible() && autoActivate){
38273                 this.menu.tryActivate(0, 1);
38274             }
38275         }
38276     },
38277
38278     // private
38279     deferExpand : function(autoActivate){
38280         delete this.showTimer;
38281         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38282         if(autoActivate){
38283             this.menu.tryActivate(0, 1);
38284         }
38285     },
38286
38287     // private
38288     hideMenu : function(){
38289         clearTimeout(this.showTimer);
38290         delete this.showTimer;
38291         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38292             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38293         }
38294     },
38295
38296     // private
38297     deferHide : function(){
38298         delete this.hideTimer;
38299         this.menu.hide();
38300     }
38301 });/*
38302  * Based on:
38303  * Ext JS Library 1.1.1
38304  * Copyright(c) 2006-2007, Ext JS, LLC.
38305  *
38306  * Originally Released Under LGPL - original licence link has changed is not relivant.
38307  *
38308  * Fork - LGPL
38309  * <script type="text/javascript">
38310  */
38311  
38312 /**
38313  * @class Roo.menu.CheckItem
38314  * @extends Roo.menu.Item
38315  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38316  * @constructor
38317  * Creates a new CheckItem
38318  * @param {Object} config Configuration options
38319  */
38320 Roo.menu.CheckItem = function(config){
38321     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38322     this.addEvents({
38323         /**
38324          * @event beforecheckchange
38325          * Fires before the checked value is set, providing an opportunity to cancel if needed
38326          * @param {Roo.menu.CheckItem} this
38327          * @param {Boolean} checked The new checked value that will be set
38328          */
38329         "beforecheckchange" : true,
38330         /**
38331          * @event checkchange
38332          * Fires after the checked value has been set
38333          * @param {Roo.menu.CheckItem} this
38334          * @param {Boolean} checked The checked value that was set
38335          */
38336         "checkchange" : true
38337     });
38338     if(this.checkHandler){
38339         this.on('checkchange', this.checkHandler, this.scope);
38340     }
38341 };
38342 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38343     /**
38344      * @cfg {String} group
38345      * All check items with the same group name will automatically be grouped into a single-select
38346      * radio button group (defaults to '')
38347      */
38348     /**
38349      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38350      */
38351     itemCls : "x-menu-item x-menu-check-item",
38352     /**
38353      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38354      */
38355     groupClass : "x-menu-group-item",
38356
38357     /**
38358      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38359      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38360      * initialized with checked = true will be rendered as checked.
38361      */
38362     checked: false,
38363
38364     // private
38365     ctype: "Roo.menu.CheckItem",
38366
38367     // private
38368     onRender : function(c){
38369         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38370         if(this.group){
38371             this.el.addClass(this.groupClass);
38372         }
38373         Roo.menu.MenuMgr.registerCheckable(this);
38374         if(this.checked){
38375             this.checked = false;
38376             this.setChecked(true, true);
38377         }
38378     },
38379
38380     // private
38381     destroy : function(){
38382         if(this.rendered){
38383             Roo.menu.MenuMgr.unregisterCheckable(this);
38384         }
38385         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38386     },
38387
38388     /**
38389      * Set the checked state of this item
38390      * @param {Boolean} checked The new checked value
38391      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38392      */
38393     setChecked : function(state, suppressEvent){
38394         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38395             if(this.container){
38396                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38397             }
38398             this.checked = state;
38399             if(suppressEvent !== true){
38400                 this.fireEvent("checkchange", this, state);
38401             }
38402         }
38403     },
38404
38405     // private
38406     handleClick : function(e){
38407        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38408            this.setChecked(!this.checked);
38409        }
38410        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38411     }
38412 });/*
38413  * Based on:
38414  * Ext JS Library 1.1.1
38415  * Copyright(c) 2006-2007, Ext JS, LLC.
38416  *
38417  * Originally Released Under LGPL - original licence link has changed is not relivant.
38418  *
38419  * Fork - LGPL
38420  * <script type="text/javascript">
38421  */
38422  
38423 /**
38424  * @class Roo.menu.DateItem
38425  * @extends Roo.menu.Adapter
38426  * A menu item that wraps the {@link Roo.DatPicker} component.
38427  * @constructor
38428  * Creates a new DateItem
38429  * @param {Object} config Configuration options
38430  */
38431 Roo.menu.DateItem = function(config){
38432     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38433     /** The Roo.DatePicker object @type Roo.DatePicker */
38434     this.picker = this.component;
38435     this.addEvents({select: true});
38436     
38437     this.picker.on("render", function(picker){
38438         picker.getEl().swallowEvent("click");
38439         picker.container.addClass("x-menu-date-item");
38440     });
38441
38442     this.picker.on("select", this.onSelect, this);
38443 };
38444
38445 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38446     // private
38447     onSelect : function(picker, date){
38448         this.fireEvent("select", this, date, picker);
38449         Roo.menu.DateItem.superclass.handleClick.call(this);
38450     }
38451 });/*
38452  * Based on:
38453  * Ext JS Library 1.1.1
38454  * Copyright(c) 2006-2007, Ext JS, LLC.
38455  *
38456  * Originally Released Under LGPL - original licence link has changed is not relivant.
38457  *
38458  * Fork - LGPL
38459  * <script type="text/javascript">
38460  */
38461  
38462 /**
38463  * @class Roo.menu.ColorItem
38464  * @extends Roo.menu.Adapter
38465  * A menu item that wraps the {@link Roo.ColorPalette} component.
38466  * @constructor
38467  * Creates a new ColorItem
38468  * @param {Object} config Configuration options
38469  */
38470 Roo.menu.ColorItem = function(config){
38471     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38472     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38473     this.palette = this.component;
38474     this.relayEvents(this.palette, ["select"]);
38475     if(this.selectHandler){
38476         this.on('select', this.selectHandler, this.scope);
38477     }
38478 };
38479 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38480  * Based on:
38481  * Ext JS Library 1.1.1
38482  * Copyright(c) 2006-2007, Ext JS, LLC.
38483  *
38484  * Originally Released Under LGPL - original licence link has changed is not relivant.
38485  *
38486  * Fork - LGPL
38487  * <script type="text/javascript">
38488  */
38489  
38490
38491 /**
38492  * @class Roo.menu.DateMenu
38493  * @extends Roo.menu.Menu
38494  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38495  * @constructor
38496  * Creates a new DateMenu
38497  * @param {Object} config Configuration options
38498  */
38499 Roo.menu.DateMenu = function(config){
38500     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38501     this.plain = true;
38502     var di = new Roo.menu.DateItem(config);
38503     this.add(di);
38504     /**
38505      * The {@link Roo.DatePicker} instance for this DateMenu
38506      * @type DatePicker
38507      */
38508     this.picker = di.picker;
38509     /**
38510      * @event select
38511      * @param {DatePicker} picker
38512      * @param {Date} date
38513      */
38514     this.relayEvents(di, ["select"]);
38515     this.on('beforeshow', function(){
38516         if(this.picker){
38517             this.picker.hideMonthPicker(false);
38518         }
38519     }, this);
38520 };
38521 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38522     cls:'x-date-menu'
38523 });/*
38524  * Based on:
38525  * Ext JS Library 1.1.1
38526  * Copyright(c) 2006-2007, Ext JS, LLC.
38527  *
38528  * Originally Released Under LGPL - original licence link has changed is not relivant.
38529  *
38530  * Fork - LGPL
38531  * <script type="text/javascript">
38532  */
38533  
38534
38535 /**
38536  * @class Roo.menu.ColorMenu
38537  * @extends Roo.menu.Menu
38538  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38539  * @constructor
38540  * Creates a new ColorMenu
38541  * @param {Object} config Configuration options
38542  */
38543 Roo.menu.ColorMenu = function(config){
38544     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38545     this.plain = true;
38546     var ci = new Roo.menu.ColorItem(config);
38547     this.add(ci);
38548     /**
38549      * The {@link Roo.ColorPalette} instance for this ColorMenu
38550      * @type ColorPalette
38551      */
38552     this.palette = ci.palette;
38553     /**
38554      * @event select
38555      * @param {ColorPalette} palette
38556      * @param {String} color
38557      */
38558     this.relayEvents(ci, ["select"]);
38559 };
38560 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38561  * Based on:
38562  * Ext JS Library 1.1.1
38563  * Copyright(c) 2006-2007, Ext JS, LLC.
38564  *
38565  * Originally Released Under LGPL - original licence link has changed is not relivant.
38566  *
38567  * Fork - LGPL
38568  * <script type="text/javascript">
38569  */
38570  
38571 /**
38572  * @class Roo.form.Field
38573  * @extends Roo.BoxComponent
38574  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38575  * @constructor
38576  * Creates a new Field
38577  * @param {Object} config Configuration options
38578  */
38579 Roo.form.Field = function(config){
38580     Roo.form.Field.superclass.constructor.call(this, config);
38581 };
38582
38583 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38584     /**
38585      * @cfg {String} fieldLabel Label to use when rendering a form.
38586      */
38587        /**
38588      * @cfg {String} qtip Mouse over tip
38589      */
38590      
38591     /**
38592      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38593      */
38594     invalidClass : "x-form-invalid",
38595     /**
38596      * @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")
38597      */
38598     invalidText : "The value in this field is invalid",
38599     /**
38600      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38601      */
38602     focusClass : "x-form-focus",
38603     /**
38604      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38605       automatic validation (defaults to "keyup").
38606      */
38607     validationEvent : "keyup",
38608     /**
38609      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38610      */
38611     validateOnBlur : true,
38612     /**
38613      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38614      */
38615     validationDelay : 250,
38616     /**
38617      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38618      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38619      */
38620     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38621     /**
38622      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38623      */
38624     fieldClass : "x-form-field",
38625     /**
38626      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38627      *<pre>
38628 Value         Description
38629 -----------   ----------------------------------------------------------------------
38630 qtip          Display a quick tip when the user hovers over the field
38631 title         Display a default browser title attribute popup
38632 under         Add a block div beneath the field containing the error text
38633 side          Add an error icon to the right of the field with a popup on hover
38634 [element id]  Add the error text directly to the innerHTML of the specified element
38635 </pre>
38636      */
38637     msgTarget : 'qtip',
38638     /**
38639      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38640      */
38641     msgFx : 'normal',
38642
38643     /**
38644      * @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.
38645      */
38646     readOnly : false,
38647
38648     /**
38649      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38650      */
38651     disabled : false,
38652
38653     /**
38654      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38655      */
38656     inputType : undefined,
38657     
38658     /**
38659      * @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).
38660          */
38661         tabIndex : undefined,
38662         
38663     // private
38664     isFormField : true,
38665
38666     // private
38667     hasFocus : false,
38668     /**
38669      * @property {Roo.Element} fieldEl
38670      * Element Containing the rendered Field (with label etc.)
38671      */
38672     /**
38673      * @cfg {Mixed} value A value to initialize this field with.
38674      */
38675     value : undefined,
38676
38677     /**
38678      * @cfg {String} name The field's HTML name attribute.
38679      */
38680     /**
38681      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38682      */
38683     // private
38684     loadedValue : false,
38685      
38686      
38687         // private ??
38688         initComponent : function(){
38689         Roo.form.Field.superclass.initComponent.call(this);
38690         this.addEvents({
38691             /**
38692              * @event focus
38693              * Fires when this field receives input focus.
38694              * @param {Roo.form.Field} this
38695              */
38696             focus : true,
38697             /**
38698              * @event blur
38699              * Fires when this field loses input focus.
38700              * @param {Roo.form.Field} this
38701              */
38702             blur : true,
38703             /**
38704              * @event specialkey
38705              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38706              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38707              * @param {Roo.form.Field} this
38708              * @param {Roo.EventObject} e The event object
38709              */
38710             specialkey : true,
38711             /**
38712              * @event change
38713              * Fires just before the field blurs if the field value has changed.
38714              * @param {Roo.form.Field} this
38715              * @param {Mixed} newValue The new value
38716              * @param {Mixed} oldValue The original value
38717              */
38718             change : true,
38719             /**
38720              * @event invalid
38721              * Fires after the field has been marked as invalid.
38722              * @param {Roo.form.Field} this
38723              * @param {String} msg The validation message
38724              */
38725             invalid : true,
38726             /**
38727              * @event valid
38728              * Fires after the field has been validated with no errors.
38729              * @param {Roo.form.Field} this
38730              */
38731             valid : true,
38732              /**
38733              * @event keyup
38734              * Fires after the key up
38735              * @param {Roo.form.Field} this
38736              * @param {Roo.EventObject}  e The event Object
38737              */
38738             keyup : true
38739         });
38740     },
38741
38742     /**
38743      * Returns the name attribute of the field if available
38744      * @return {String} name The field name
38745      */
38746     getName: function(){
38747          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38748     },
38749
38750     // private
38751     onRender : function(ct, position){
38752         Roo.form.Field.superclass.onRender.call(this, ct, position);
38753         if(!this.el){
38754             var cfg = this.getAutoCreate();
38755             if(!cfg.name){
38756                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38757             }
38758             if (!cfg.name.length) {
38759                 delete cfg.name;
38760             }
38761             if(this.inputType){
38762                 cfg.type = this.inputType;
38763             }
38764             this.el = ct.createChild(cfg, position);
38765         }
38766         var type = this.el.dom.type;
38767         if(type){
38768             if(type == 'password'){
38769                 type = 'text';
38770             }
38771             this.el.addClass('x-form-'+type);
38772         }
38773         if(this.readOnly){
38774             this.el.dom.readOnly = true;
38775         }
38776         if(this.tabIndex !== undefined){
38777             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38778         }
38779
38780         this.el.addClass([this.fieldClass, this.cls]);
38781         this.initValue();
38782     },
38783
38784     /**
38785      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38786      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38787      * @return {Roo.form.Field} this
38788      */
38789     applyTo : function(target){
38790         this.allowDomMove = false;
38791         this.el = Roo.get(target);
38792         this.render(this.el.dom.parentNode);
38793         return this;
38794     },
38795
38796     // private
38797     initValue : function(){
38798         if(this.value !== undefined){
38799             this.setValue(this.value);
38800         }else if(this.el.dom.value.length > 0){
38801             this.setValue(this.el.dom.value);
38802         }
38803     },
38804
38805     /**
38806      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38807      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38808      */
38809     isDirty : function() {
38810         if(this.disabled) {
38811             return false;
38812         }
38813         return String(this.getValue()) !== String(this.originalValue);
38814     },
38815
38816     /**
38817      * stores the current value in loadedValue
38818      */
38819     resetHasChanged : function()
38820     {
38821         this.loadedValue = String(this.getValue());
38822     },
38823     /**
38824      * checks the current value against the 'loaded' value.
38825      * Note - will return false if 'resetHasChanged' has not been called first.
38826      */
38827     hasChanged : function()
38828     {
38829         if(this.disabled || this.readOnly) {
38830             return false;
38831         }
38832         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38833     },
38834     
38835     
38836     
38837     // private
38838     afterRender : function(){
38839         Roo.form.Field.superclass.afterRender.call(this);
38840         this.initEvents();
38841     },
38842
38843     // private
38844     fireKey : function(e){
38845         //Roo.log('field ' + e.getKey());
38846         if(e.isNavKeyPress()){
38847             this.fireEvent("specialkey", this, e);
38848         }
38849     },
38850
38851     /**
38852      * Resets the current field value to the originally loaded value and clears any validation messages
38853      */
38854     reset : function(){
38855         this.setValue(this.resetValue);
38856         this.clearInvalid();
38857     },
38858
38859     // private
38860     initEvents : function(){
38861         // safari killled keypress - so keydown is now used..
38862         this.el.on("keydown" , this.fireKey,  this);
38863         this.el.on("focus", this.onFocus,  this);
38864         this.el.on("blur", this.onBlur,  this);
38865         this.el.relayEvent('keyup', this);
38866
38867         // reference to original value for reset
38868         this.originalValue = this.getValue();
38869         this.resetValue =  this.getValue();
38870     },
38871
38872     // private
38873     onFocus : function(){
38874         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38875             this.el.addClass(this.focusClass);
38876         }
38877         if(!this.hasFocus){
38878             this.hasFocus = true;
38879             this.startValue = this.getValue();
38880             this.fireEvent("focus", this);
38881         }
38882     },
38883
38884     beforeBlur : Roo.emptyFn,
38885
38886     // private
38887     onBlur : function(){
38888         this.beforeBlur();
38889         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38890             this.el.removeClass(this.focusClass);
38891         }
38892         this.hasFocus = false;
38893         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38894             this.validate();
38895         }
38896         var v = this.getValue();
38897         if(String(v) !== String(this.startValue)){
38898             this.fireEvent('change', this, v, this.startValue);
38899         }
38900         this.fireEvent("blur", this);
38901     },
38902
38903     /**
38904      * Returns whether or not the field value is currently valid
38905      * @param {Boolean} preventMark True to disable marking the field invalid
38906      * @return {Boolean} True if the value is valid, else false
38907      */
38908     isValid : function(preventMark){
38909         if(this.disabled){
38910             return true;
38911         }
38912         var restore = this.preventMark;
38913         this.preventMark = preventMark === true;
38914         var v = this.validateValue(this.processValue(this.getRawValue()));
38915         this.preventMark = restore;
38916         return v;
38917     },
38918
38919     /**
38920      * Validates the field value
38921      * @return {Boolean} True if the value is valid, else false
38922      */
38923     validate : function(){
38924         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38925             this.clearInvalid();
38926             return true;
38927         }
38928         return false;
38929     },
38930
38931     processValue : function(value){
38932         return value;
38933     },
38934
38935     // private
38936     // Subclasses should provide the validation implementation by overriding this
38937     validateValue : function(value){
38938         return true;
38939     },
38940
38941     /**
38942      * Mark this field as invalid
38943      * @param {String} msg The validation message
38944      */
38945     markInvalid : function(msg){
38946         if(!this.rendered || this.preventMark){ // not rendered
38947             return;
38948         }
38949         
38950         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38951         
38952         obj.el.addClass(this.invalidClass);
38953         msg = msg || this.invalidText;
38954         switch(this.msgTarget){
38955             case 'qtip':
38956                 obj.el.dom.qtip = msg;
38957                 obj.el.dom.qclass = 'x-form-invalid-tip';
38958                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38959                     Roo.QuickTips.enable();
38960                 }
38961                 break;
38962             case 'title':
38963                 this.el.dom.title = msg;
38964                 break;
38965             case 'under':
38966                 if(!this.errorEl){
38967                     var elp = this.el.findParent('.x-form-element', 5, true);
38968                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38969                     this.errorEl.setWidth(elp.getWidth(true)-20);
38970                 }
38971                 this.errorEl.update(msg);
38972                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38973                 break;
38974             case 'side':
38975                 if(!this.errorIcon){
38976                     var elp = this.el.findParent('.x-form-element', 5, true);
38977                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38978                 }
38979                 this.alignErrorIcon();
38980                 this.errorIcon.dom.qtip = msg;
38981                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38982                 this.errorIcon.show();
38983                 this.on('resize', this.alignErrorIcon, this);
38984                 break;
38985             default:
38986                 var t = Roo.getDom(this.msgTarget);
38987                 t.innerHTML = msg;
38988                 t.style.display = this.msgDisplay;
38989                 break;
38990         }
38991         this.fireEvent('invalid', this, msg);
38992     },
38993
38994     // private
38995     alignErrorIcon : function(){
38996         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38997     },
38998
38999     /**
39000      * Clear any invalid styles/messages for this field
39001      */
39002     clearInvalid : function(){
39003         if(!this.rendered || this.preventMark){ // not rendered
39004             return;
39005         }
39006         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39007         
39008         obj.el.removeClass(this.invalidClass);
39009         switch(this.msgTarget){
39010             case 'qtip':
39011                 obj.el.dom.qtip = '';
39012                 break;
39013             case 'title':
39014                 this.el.dom.title = '';
39015                 break;
39016             case 'under':
39017                 if(this.errorEl){
39018                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39019                 }
39020                 break;
39021             case 'side':
39022                 if(this.errorIcon){
39023                     this.errorIcon.dom.qtip = '';
39024                     this.errorIcon.hide();
39025                     this.un('resize', this.alignErrorIcon, this);
39026                 }
39027                 break;
39028             default:
39029                 var t = Roo.getDom(this.msgTarget);
39030                 t.innerHTML = '';
39031                 t.style.display = 'none';
39032                 break;
39033         }
39034         this.fireEvent('valid', this);
39035     },
39036
39037     /**
39038      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39039      * @return {Mixed} value The field value
39040      */
39041     getRawValue : function(){
39042         var v = this.el.getValue();
39043         
39044         return v;
39045     },
39046
39047     /**
39048      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39049      * @return {Mixed} value The field value
39050      */
39051     getValue : function(){
39052         var v = this.el.getValue();
39053          
39054         return v;
39055     },
39056
39057     /**
39058      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39059      * @param {Mixed} value The value to set
39060      */
39061     setRawValue : function(v){
39062         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39063     },
39064
39065     /**
39066      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39067      * @param {Mixed} value The value to set
39068      */
39069     setValue : function(v){
39070         this.value = v;
39071         if(this.rendered){
39072             this.el.dom.value = (v === null || v === undefined ? '' : v);
39073              this.validate();
39074         }
39075     },
39076
39077     adjustSize : function(w, h){
39078         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39079         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39080         return s;
39081     },
39082
39083     adjustWidth : function(tag, w){
39084         tag = tag.toLowerCase();
39085         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39086             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39087                 if(tag == 'input'){
39088                     return w + 2;
39089                 }
39090                 if(tag == 'textarea'){
39091                     return w-2;
39092                 }
39093             }else if(Roo.isOpera){
39094                 if(tag == 'input'){
39095                     return w + 2;
39096                 }
39097                 if(tag == 'textarea'){
39098                     return w-2;
39099                 }
39100             }
39101         }
39102         return w;
39103     }
39104 });
39105
39106
39107 // anything other than normal should be considered experimental
39108 Roo.form.Field.msgFx = {
39109     normal : {
39110         show: function(msgEl, f){
39111             msgEl.setDisplayed('block');
39112         },
39113
39114         hide : function(msgEl, f){
39115             msgEl.setDisplayed(false).update('');
39116         }
39117     },
39118
39119     slide : {
39120         show: function(msgEl, f){
39121             msgEl.slideIn('t', {stopFx:true});
39122         },
39123
39124         hide : function(msgEl, f){
39125             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39126         }
39127     },
39128
39129     slideRight : {
39130         show: function(msgEl, f){
39131             msgEl.fixDisplay();
39132             msgEl.alignTo(f.el, 'tl-tr');
39133             msgEl.slideIn('l', {stopFx:true});
39134         },
39135
39136         hide : function(msgEl, f){
39137             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39138         }
39139     }
39140 };/*
39141  * Based on:
39142  * Ext JS Library 1.1.1
39143  * Copyright(c) 2006-2007, Ext JS, LLC.
39144  *
39145  * Originally Released Under LGPL - original licence link has changed is not relivant.
39146  *
39147  * Fork - LGPL
39148  * <script type="text/javascript">
39149  */
39150  
39151
39152 /**
39153  * @class Roo.form.TextField
39154  * @extends Roo.form.Field
39155  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39156  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39157  * @constructor
39158  * Creates a new TextField
39159  * @param {Object} config Configuration options
39160  */
39161 Roo.form.TextField = function(config){
39162     Roo.form.TextField.superclass.constructor.call(this, config);
39163     this.addEvents({
39164         /**
39165          * @event autosize
39166          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39167          * according to the default logic, but this event provides a hook for the developer to apply additional
39168          * logic at runtime to resize the field if needed.
39169              * @param {Roo.form.Field} this This text field
39170              * @param {Number} width The new field width
39171              */
39172         autosize : true
39173     });
39174 };
39175
39176 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39177     /**
39178      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39179      */
39180     grow : false,
39181     /**
39182      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39183      */
39184     growMin : 30,
39185     /**
39186      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39187      */
39188     growMax : 800,
39189     /**
39190      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39191      */
39192     vtype : null,
39193     /**
39194      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39195      */
39196     maskRe : null,
39197     /**
39198      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39199      */
39200     disableKeyFilter : false,
39201     /**
39202      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39203      */
39204     allowBlank : true,
39205     /**
39206      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39207      */
39208     minLength : 0,
39209     /**
39210      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39211      */
39212     maxLength : Number.MAX_VALUE,
39213     /**
39214      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39215      */
39216     minLengthText : "The minimum length for this field is {0}",
39217     /**
39218      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39219      */
39220     maxLengthText : "The maximum length for this field is {0}",
39221     /**
39222      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39223      */
39224     selectOnFocus : false,
39225     /**
39226      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39227      */
39228     blankText : "This field is required",
39229     /**
39230      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39231      * If available, this function will be called only after the basic validators all return true, and will be passed the
39232      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39233      */
39234     validator : null,
39235     /**
39236      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39237      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39238      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39239      */
39240     regex : null,
39241     /**
39242      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39243      */
39244     regexText : "",
39245     /**
39246      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39247      */
39248     emptyText : null,
39249    
39250
39251     // private
39252     initEvents : function()
39253     {
39254         if (this.emptyText) {
39255             this.el.attr('placeholder', this.emptyText);
39256         }
39257         
39258         Roo.form.TextField.superclass.initEvents.call(this);
39259         if(this.validationEvent == 'keyup'){
39260             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39261             this.el.on('keyup', this.filterValidation, this);
39262         }
39263         else if(this.validationEvent !== false){
39264             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39265         }
39266         
39267         if(this.selectOnFocus){
39268             this.on("focus", this.preFocus, this);
39269             
39270         }
39271         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39272             this.el.on("keypress", this.filterKeys, this);
39273         }
39274         if(this.grow){
39275             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39276             this.el.on("click", this.autoSize,  this);
39277         }
39278         if(this.el.is('input[type=password]') && Roo.isSafari){
39279             this.el.on('keydown', this.SafariOnKeyDown, this);
39280         }
39281     },
39282
39283     processValue : function(value){
39284         if(this.stripCharsRe){
39285             var newValue = value.replace(this.stripCharsRe, '');
39286             if(newValue !== value){
39287                 this.setRawValue(newValue);
39288                 return newValue;
39289             }
39290         }
39291         return value;
39292     },
39293
39294     filterValidation : function(e){
39295         if(!e.isNavKeyPress()){
39296             this.validationTask.delay(this.validationDelay);
39297         }
39298     },
39299
39300     // private
39301     onKeyUp : function(e){
39302         if(!e.isNavKeyPress()){
39303             this.autoSize();
39304         }
39305     },
39306
39307     /**
39308      * Resets the current field value to the originally-loaded value and clears any validation messages.
39309      *  
39310      */
39311     reset : function(){
39312         Roo.form.TextField.superclass.reset.call(this);
39313        
39314     },
39315
39316     
39317     // private
39318     preFocus : function(){
39319         
39320         if(this.selectOnFocus){
39321             this.el.dom.select();
39322         }
39323     },
39324
39325     
39326     // private
39327     filterKeys : function(e){
39328         var k = e.getKey();
39329         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39330             return;
39331         }
39332         var c = e.getCharCode(), cc = String.fromCharCode(c);
39333         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39334             return;
39335         }
39336         if(!this.maskRe.test(cc)){
39337             e.stopEvent();
39338         }
39339     },
39340
39341     setValue : function(v){
39342         
39343         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39344         
39345         this.autoSize();
39346     },
39347
39348     /**
39349      * Validates a value according to the field's validation rules and marks the field as invalid
39350      * if the validation fails
39351      * @param {Mixed} value The value to validate
39352      * @return {Boolean} True if the value is valid, else false
39353      */
39354     validateValue : function(value){
39355         if(value.length < 1)  { // if it's blank
39356              if(this.allowBlank){
39357                 this.clearInvalid();
39358                 return true;
39359              }else{
39360                 this.markInvalid(this.blankText);
39361                 return false;
39362              }
39363         }
39364         if(value.length < this.minLength){
39365             this.markInvalid(String.format(this.minLengthText, this.minLength));
39366             return false;
39367         }
39368         if(value.length > this.maxLength){
39369             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39370             return false;
39371         }
39372         if(this.vtype){
39373             var vt = Roo.form.VTypes;
39374             if(!vt[this.vtype](value, this)){
39375                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39376                 return false;
39377             }
39378         }
39379         if(typeof this.validator == "function"){
39380             var msg = this.validator(value);
39381             if(msg !== true){
39382                 this.markInvalid(msg);
39383                 return false;
39384             }
39385         }
39386         if(this.regex && !this.regex.test(value)){
39387             this.markInvalid(this.regexText);
39388             return false;
39389         }
39390         return true;
39391     },
39392
39393     /**
39394      * Selects text in this field
39395      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39396      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39397      */
39398     selectText : function(start, end){
39399         var v = this.getRawValue();
39400         if(v.length > 0){
39401             start = start === undefined ? 0 : start;
39402             end = end === undefined ? v.length : end;
39403             var d = this.el.dom;
39404             if(d.setSelectionRange){
39405                 d.setSelectionRange(start, end);
39406             }else if(d.createTextRange){
39407                 var range = d.createTextRange();
39408                 range.moveStart("character", start);
39409                 range.moveEnd("character", v.length-end);
39410                 range.select();
39411             }
39412         }
39413     },
39414
39415     /**
39416      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39417      * This only takes effect if grow = true, and fires the autosize event.
39418      */
39419     autoSize : function(){
39420         if(!this.grow || !this.rendered){
39421             return;
39422         }
39423         if(!this.metrics){
39424             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39425         }
39426         var el = this.el;
39427         var v = el.dom.value;
39428         var d = document.createElement('div');
39429         d.appendChild(document.createTextNode(v));
39430         v = d.innerHTML;
39431         d = null;
39432         v += "&#160;";
39433         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39434         this.el.setWidth(w);
39435         this.fireEvent("autosize", this, w);
39436     },
39437     
39438     // private
39439     SafariOnKeyDown : function(event)
39440     {
39441         // this is a workaround for a password hang bug on chrome/ webkit.
39442         
39443         var isSelectAll = false;
39444         
39445         if(this.el.dom.selectionEnd > 0){
39446             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39447         }
39448         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39449             event.preventDefault();
39450             this.setValue('');
39451             return;
39452         }
39453         
39454         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39455             
39456             event.preventDefault();
39457             // this is very hacky as keydown always get's upper case.
39458             
39459             var cc = String.fromCharCode(event.getCharCode());
39460             
39461             
39462             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39463             
39464         }
39465         
39466         
39467     }
39468 });/*
39469  * Based on:
39470  * Ext JS Library 1.1.1
39471  * Copyright(c) 2006-2007, Ext JS, LLC.
39472  *
39473  * Originally Released Under LGPL - original licence link has changed is not relivant.
39474  *
39475  * Fork - LGPL
39476  * <script type="text/javascript">
39477  */
39478  
39479 /**
39480  * @class Roo.form.Hidden
39481  * @extends Roo.form.TextField
39482  * Simple Hidden element used on forms 
39483  * 
39484  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39485  * 
39486  * @constructor
39487  * Creates a new Hidden form element.
39488  * @param {Object} config Configuration options
39489  */
39490
39491
39492
39493 // easy hidden field...
39494 Roo.form.Hidden = function(config){
39495     Roo.form.Hidden.superclass.constructor.call(this, config);
39496 };
39497   
39498 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39499     fieldLabel:      '',
39500     inputType:      'hidden',
39501     width:          50,
39502     allowBlank:     true,
39503     labelSeparator: '',
39504     hidden:         true,
39505     itemCls :       'x-form-item-display-none'
39506
39507
39508 });
39509
39510
39511 /*
39512  * Based on:
39513  * Ext JS Library 1.1.1
39514  * Copyright(c) 2006-2007, Ext JS, LLC.
39515  *
39516  * Originally Released Under LGPL - original licence link has changed is not relivant.
39517  *
39518  * Fork - LGPL
39519  * <script type="text/javascript">
39520  */
39521  
39522 /**
39523  * @class Roo.form.TriggerField
39524  * @extends Roo.form.TextField
39525  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39526  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39527  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39528  * for which you can provide a custom implementation.  For example:
39529  * <pre><code>
39530 var trigger = new Roo.form.TriggerField();
39531 trigger.onTriggerClick = myTriggerFn;
39532 trigger.applyTo('my-field');
39533 </code></pre>
39534  *
39535  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39536  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39537  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39538  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39539  * @constructor
39540  * Create a new TriggerField.
39541  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39542  * to the base TextField)
39543  */
39544 Roo.form.TriggerField = function(config){
39545     this.mimicing = false;
39546     Roo.form.TriggerField.superclass.constructor.call(this, config);
39547 };
39548
39549 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39550     /**
39551      * @cfg {String} triggerClass A CSS class to apply to the trigger
39552      */
39553     /**
39554      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39555      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39556      */
39557     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39558     /**
39559      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39560      */
39561     hideTrigger:false,
39562
39563     /** @cfg {Boolean} grow @hide */
39564     /** @cfg {Number} growMin @hide */
39565     /** @cfg {Number} growMax @hide */
39566
39567     /**
39568      * @hide 
39569      * @method
39570      */
39571     autoSize: Roo.emptyFn,
39572     // private
39573     monitorTab : true,
39574     // private
39575     deferHeight : true,
39576
39577     
39578     actionMode : 'wrap',
39579     // private
39580     onResize : function(w, h){
39581         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39582         if(typeof w == 'number'){
39583             var x = w - this.trigger.getWidth();
39584             this.el.setWidth(this.adjustWidth('input', x));
39585             this.trigger.setStyle('left', x+'px');
39586         }
39587     },
39588
39589     // private
39590     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39591
39592     // private
39593     getResizeEl : function(){
39594         return this.wrap;
39595     },
39596
39597     // private
39598     getPositionEl : function(){
39599         return this.wrap;
39600     },
39601
39602     // private
39603     alignErrorIcon : function(){
39604         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39605     },
39606
39607     // private
39608     onRender : function(ct, position){
39609         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39610         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39611         this.trigger = this.wrap.createChild(this.triggerConfig ||
39612                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39613         if(this.hideTrigger){
39614             this.trigger.setDisplayed(false);
39615         }
39616         this.initTrigger();
39617         if(!this.width){
39618             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39619         }
39620     },
39621
39622     // private
39623     initTrigger : function(){
39624         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39625         this.trigger.addClassOnOver('x-form-trigger-over');
39626         this.trigger.addClassOnClick('x-form-trigger-click');
39627     },
39628
39629     // private
39630     onDestroy : function(){
39631         if(this.trigger){
39632             this.trigger.removeAllListeners();
39633             this.trigger.remove();
39634         }
39635         if(this.wrap){
39636             this.wrap.remove();
39637         }
39638         Roo.form.TriggerField.superclass.onDestroy.call(this);
39639     },
39640
39641     // private
39642     onFocus : function(){
39643         Roo.form.TriggerField.superclass.onFocus.call(this);
39644         if(!this.mimicing){
39645             this.wrap.addClass('x-trigger-wrap-focus');
39646             this.mimicing = true;
39647             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39648             if(this.monitorTab){
39649                 this.el.on("keydown", this.checkTab, this);
39650             }
39651         }
39652     },
39653
39654     // private
39655     checkTab : function(e){
39656         if(e.getKey() == e.TAB){
39657             this.triggerBlur();
39658         }
39659     },
39660
39661     // private
39662     onBlur : function(){
39663         // do nothing
39664     },
39665
39666     // private
39667     mimicBlur : function(e, t){
39668         if(!this.wrap.contains(t) && this.validateBlur()){
39669             this.triggerBlur();
39670         }
39671     },
39672
39673     // private
39674     triggerBlur : function(){
39675         this.mimicing = false;
39676         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39677         if(this.monitorTab){
39678             this.el.un("keydown", this.checkTab, this);
39679         }
39680         this.wrap.removeClass('x-trigger-wrap-focus');
39681         Roo.form.TriggerField.superclass.onBlur.call(this);
39682     },
39683
39684     // private
39685     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39686     validateBlur : function(e, t){
39687         return true;
39688     },
39689
39690     // private
39691     onDisable : function(){
39692         Roo.form.TriggerField.superclass.onDisable.call(this);
39693         if(this.wrap){
39694             this.wrap.addClass('x-item-disabled');
39695         }
39696     },
39697
39698     // private
39699     onEnable : function(){
39700         Roo.form.TriggerField.superclass.onEnable.call(this);
39701         if(this.wrap){
39702             this.wrap.removeClass('x-item-disabled');
39703         }
39704     },
39705
39706     // private
39707     onShow : function(){
39708         var ae = this.getActionEl();
39709         
39710         if(ae){
39711             ae.dom.style.display = '';
39712             ae.dom.style.visibility = 'visible';
39713         }
39714     },
39715
39716     // private
39717     
39718     onHide : function(){
39719         var ae = this.getActionEl();
39720         ae.dom.style.display = 'none';
39721     },
39722
39723     /**
39724      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39725      * by an implementing function.
39726      * @method
39727      * @param {EventObject} e
39728      */
39729     onTriggerClick : Roo.emptyFn
39730 });
39731
39732 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39733 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39734 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39735 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39736     initComponent : function(){
39737         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39738
39739         this.triggerConfig = {
39740             tag:'span', cls:'x-form-twin-triggers', cn:[
39741             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39742             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39743         ]};
39744     },
39745
39746     getTrigger : function(index){
39747         return this.triggers[index];
39748     },
39749
39750     initTrigger : function(){
39751         var ts = this.trigger.select('.x-form-trigger', true);
39752         this.wrap.setStyle('overflow', 'hidden');
39753         var triggerField = this;
39754         ts.each(function(t, all, index){
39755             t.hide = function(){
39756                 var w = triggerField.wrap.getWidth();
39757                 this.dom.style.display = 'none';
39758                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39759             };
39760             t.show = function(){
39761                 var w = triggerField.wrap.getWidth();
39762                 this.dom.style.display = '';
39763                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39764             };
39765             var triggerIndex = 'Trigger'+(index+1);
39766
39767             if(this['hide'+triggerIndex]){
39768                 t.dom.style.display = 'none';
39769             }
39770             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39771             t.addClassOnOver('x-form-trigger-over');
39772             t.addClassOnClick('x-form-trigger-click');
39773         }, this);
39774         this.triggers = ts.elements;
39775     },
39776
39777     onTrigger1Click : Roo.emptyFn,
39778     onTrigger2Click : Roo.emptyFn
39779 });/*
39780  * Based on:
39781  * Ext JS Library 1.1.1
39782  * Copyright(c) 2006-2007, Ext JS, LLC.
39783  *
39784  * Originally Released Under LGPL - original licence link has changed is not relivant.
39785  *
39786  * Fork - LGPL
39787  * <script type="text/javascript">
39788  */
39789  
39790 /**
39791  * @class Roo.form.TextArea
39792  * @extends Roo.form.TextField
39793  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39794  * support for auto-sizing.
39795  * @constructor
39796  * Creates a new TextArea
39797  * @param {Object} config Configuration options
39798  */
39799 Roo.form.TextArea = function(config){
39800     Roo.form.TextArea.superclass.constructor.call(this, config);
39801     // these are provided exchanges for backwards compat
39802     // minHeight/maxHeight were replaced by growMin/growMax to be
39803     // compatible with TextField growing config values
39804     if(this.minHeight !== undefined){
39805         this.growMin = this.minHeight;
39806     }
39807     if(this.maxHeight !== undefined){
39808         this.growMax = this.maxHeight;
39809     }
39810 };
39811
39812 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39813     /**
39814      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39815      */
39816     growMin : 60,
39817     /**
39818      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39819      */
39820     growMax: 1000,
39821     /**
39822      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39823      * in the field (equivalent to setting overflow: hidden, defaults to false)
39824      */
39825     preventScrollbars: false,
39826     /**
39827      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39828      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39829      */
39830
39831     // private
39832     onRender : function(ct, position){
39833         if(!this.el){
39834             this.defaultAutoCreate = {
39835                 tag: "textarea",
39836                 style:"width:300px;height:60px;",
39837                 autocomplete: "new-password"
39838             };
39839         }
39840         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39841         if(this.grow){
39842             this.textSizeEl = Roo.DomHelper.append(document.body, {
39843                 tag: "pre", cls: "x-form-grow-sizer"
39844             });
39845             if(this.preventScrollbars){
39846                 this.el.setStyle("overflow", "hidden");
39847             }
39848             this.el.setHeight(this.growMin);
39849         }
39850     },
39851
39852     onDestroy : function(){
39853         if(this.textSizeEl){
39854             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39855         }
39856         Roo.form.TextArea.superclass.onDestroy.call(this);
39857     },
39858
39859     // private
39860     onKeyUp : function(e){
39861         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39862             this.autoSize();
39863         }
39864     },
39865
39866     /**
39867      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39868      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39869      */
39870     autoSize : function(){
39871         if(!this.grow || !this.textSizeEl){
39872             return;
39873         }
39874         var el = this.el;
39875         var v = el.dom.value;
39876         var ts = this.textSizeEl;
39877
39878         ts.innerHTML = '';
39879         ts.appendChild(document.createTextNode(v));
39880         v = ts.innerHTML;
39881
39882         Roo.fly(ts).setWidth(this.el.getWidth());
39883         if(v.length < 1){
39884             v = "&#160;&#160;";
39885         }else{
39886             if(Roo.isIE){
39887                 v = v.replace(/\n/g, '<p>&#160;</p>');
39888             }
39889             v += "&#160;\n&#160;";
39890         }
39891         ts.innerHTML = v;
39892         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39893         if(h != this.lastHeight){
39894             this.lastHeight = h;
39895             this.el.setHeight(h);
39896             this.fireEvent("autosize", this, h);
39897         }
39898     }
39899 });/*
39900  * Based on:
39901  * Ext JS Library 1.1.1
39902  * Copyright(c) 2006-2007, Ext JS, LLC.
39903  *
39904  * Originally Released Under LGPL - original licence link has changed is not relivant.
39905  *
39906  * Fork - LGPL
39907  * <script type="text/javascript">
39908  */
39909  
39910
39911 /**
39912  * @class Roo.form.NumberField
39913  * @extends Roo.form.TextField
39914  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39915  * @constructor
39916  * Creates a new NumberField
39917  * @param {Object} config Configuration options
39918  */
39919 Roo.form.NumberField = function(config){
39920     Roo.form.NumberField.superclass.constructor.call(this, config);
39921 };
39922
39923 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39924     /**
39925      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39926      */
39927     fieldClass: "x-form-field x-form-num-field",
39928     /**
39929      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39930      */
39931     allowDecimals : true,
39932     /**
39933      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39934      */
39935     decimalSeparator : ".",
39936     /**
39937      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39938      */
39939     decimalPrecision : 2,
39940     /**
39941      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39942      */
39943     allowNegative : true,
39944     /**
39945      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39946      */
39947     minValue : Number.NEGATIVE_INFINITY,
39948     /**
39949      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39950      */
39951     maxValue : Number.MAX_VALUE,
39952     /**
39953      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39954      */
39955     minText : "The minimum value for this field is {0}",
39956     /**
39957      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39958      */
39959     maxText : "The maximum value for this field is {0}",
39960     /**
39961      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39962      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39963      */
39964     nanText : "{0} is not a valid number",
39965
39966     // private
39967     initEvents : function(){
39968         Roo.form.NumberField.superclass.initEvents.call(this);
39969         var allowed = "0123456789";
39970         if(this.allowDecimals){
39971             allowed += this.decimalSeparator;
39972         }
39973         if(this.allowNegative){
39974             allowed += "-";
39975         }
39976         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39977         var keyPress = function(e){
39978             var k = e.getKey();
39979             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39980                 return;
39981             }
39982             var c = e.getCharCode();
39983             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39984                 e.stopEvent();
39985             }
39986         };
39987         this.el.on("keypress", keyPress, this);
39988     },
39989
39990     // private
39991     validateValue : function(value){
39992         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39993             return false;
39994         }
39995         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39996              return true;
39997         }
39998         var num = this.parseValue(value);
39999         if(isNaN(num)){
40000             this.markInvalid(String.format(this.nanText, value));
40001             return false;
40002         }
40003         if(num < this.minValue){
40004             this.markInvalid(String.format(this.minText, this.minValue));
40005             return false;
40006         }
40007         if(num > this.maxValue){
40008             this.markInvalid(String.format(this.maxText, this.maxValue));
40009             return false;
40010         }
40011         return true;
40012     },
40013
40014     getValue : function(){
40015         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40016     },
40017
40018     // private
40019     parseValue : function(value){
40020         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40021         return isNaN(value) ? '' : value;
40022     },
40023
40024     // private
40025     fixPrecision : function(value){
40026         var nan = isNaN(value);
40027         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40028             return nan ? '' : value;
40029         }
40030         return parseFloat(value).toFixed(this.decimalPrecision);
40031     },
40032
40033     setValue : function(v){
40034         v = this.fixPrecision(v);
40035         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40036     },
40037
40038     // private
40039     decimalPrecisionFcn : function(v){
40040         return Math.floor(v);
40041     },
40042
40043     beforeBlur : function(){
40044         var v = this.parseValue(this.getRawValue());
40045         if(v){
40046             this.setValue(v);
40047         }
40048     }
40049 });/*
40050  * Based on:
40051  * Ext JS Library 1.1.1
40052  * Copyright(c) 2006-2007, Ext JS, LLC.
40053  *
40054  * Originally Released Under LGPL - original licence link has changed is not relivant.
40055  *
40056  * Fork - LGPL
40057  * <script type="text/javascript">
40058  */
40059  
40060 /**
40061  * @class Roo.form.DateField
40062  * @extends Roo.form.TriggerField
40063  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40064 * @constructor
40065 * Create a new DateField
40066 * @param {Object} config
40067  */
40068 Roo.form.DateField = function(config){
40069     Roo.form.DateField.superclass.constructor.call(this, config);
40070     
40071       this.addEvents({
40072          
40073         /**
40074          * @event select
40075          * Fires when a date is selected
40076              * @param {Roo.form.DateField} combo This combo box
40077              * @param {Date} date The date selected
40078              */
40079         'select' : true
40080          
40081     });
40082     
40083     
40084     if(typeof this.minValue == "string") {
40085         this.minValue = this.parseDate(this.minValue);
40086     }
40087     if(typeof this.maxValue == "string") {
40088         this.maxValue = this.parseDate(this.maxValue);
40089     }
40090     this.ddMatch = null;
40091     if(this.disabledDates){
40092         var dd = this.disabledDates;
40093         var re = "(?:";
40094         for(var i = 0; i < dd.length; i++){
40095             re += dd[i];
40096             if(i != dd.length-1) {
40097                 re += "|";
40098             }
40099         }
40100         this.ddMatch = new RegExp(re + ")");
40101     }
40102 };
40103
40104 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40105     /**
40106      * @cfg {String} format
40107      * The default date format string which can be overriden for localization support.  The format must be
40108      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40109      */
40110     format : "m/d/y",
40111     /**
40112      * @cfg {String} altFormats
40113      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40114      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40115      */
40116     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40117     /**
40118      * @cfg {Array} disabledDays
40119      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40120      */
40121     disabledDays : null,
40122     /**
40123      * @cfg {String} disabledDaysText
40124      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40125      */
40126     disabledDaysText : "Disabled",
40127     /**
40128      * @cfg {Array} disabledDates
40129      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40130      * expression so they are very powerful. Some examples:
40131      * <ul>
40132      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40133      * <li>["03/08", "09/16"] would disable those days for every year</li>
40134      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40135      * <li>["03/../2006"] would disable every day in March 2006</li>
40136      * <li>["^03"] would disable every day in every March</li>
40137      * </ul>
40138      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40139      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40140      */
40141     disabledDates : null,
40142     /**
40143      * @cfg {String} disabledDatesText
40144      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40145      */
40146     disabledDatesText : "Disabled",
40147     /**
40148      * @cfg {Date/String} minValue
40149      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40150      * valid format (defaults to null).
40151      */
40152     minValue : null,
40153     /**
40154      * @cfg {Date/String} maxValue
40155      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40156      * valid format (defaults to null).
40157      */
40158     maxValue : null,
40159     /**
40160      * @cfg {String} minText
40161      * The error text to display when the date in the cell is before minValue (defaults to
40162      * 'The date in this field must be after {minValue}').
40163      */
40164     minText : "The date in this field must be equal to or after {0}",
40165     /**
40166      * @cfg {String} maxText
40167      * The error text to display when the date in the cell is after maxValue (defaults to
40168      * 'The date in this field must be before {maxValue}').
40169      */
40170     maxText : "The date in this field must be equal to or before {0}",
40171     /**
40172      * @cfg {String} invalidText
40173      * The error text to display when the date in the field is invalid (defaults to
40174      * '{value} is not a valid date - it must be in the format {format}').
40175      */
40176     invalidText : "{0} is not a valid date - it must be in the format {1}",
40177     /**
40178      * @cfg {String} triggerClass
40179      * An additional CSS class used to style the trigger button.  The trigger will always get the
40180      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40181      * which displays a calendar icon).
40182      */
40183     triggerClass : 'x-form-date-trigger',
40184     
40185
40186     /**
40187      * @cfg {Boolean} useIso
40188      * if enabled, then the date field will use a hidden field to store the 
40189      * real value as iso formated date. default (false)
40190      */ 
40191     useIso : false,
40192     /**
40193      * @cfg {String/Object} autoCreate
40194      * A DomHelper element spec, or true for a default element spec (defaults to
40195      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40196      */ 
40197     // private
40198     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40199     
40200     // private
40201     hiddenField: false,
40202     
40203     onRender : function(ct, position)
40204     {
40205         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40206         if (this.useIso) {
40207             //this.el.dom.removeAttribute('name'); 
40208             Roo.log("Changing name?");
40209             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40210             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40211                     'before', true);
40212             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40213             // prevent input submission
40214             this.hiddenName = this.name;
40215         }
40216             
40217             
40218     },
40219     
40220     // private
40221     validateValue : function(value)
40222     {
40223         value = this.formatDate(value);
40224         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40225             Roo.log('super failed');
40226             return false;
40227         }
40228         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40229              return true;
40230         }
40231         var svalue = value;
40232         value = this.parseDate(value);
40233         if(!value){
40234             Roo.log('parse date failed' + svalue);
40235             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40236             return false;
40237         }
40238         var time = value.getTime();
40239         if(this.minValue && time < this.minValue.getTime()){
40240             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40241             return false;
40242         }
40243         if(this.maxValue && time > this.maxValue.getTime()){
40244             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40245             return false;
40246         }
40247         if(this.disabledDays){
40248             var day = value.getDay();
40249             for(var i = 0; i < this.disabledDays.length; i++) {
40250                 if(day === this.disabledDays[i]){
40251                     this.markInvalid(this.disabledDaysText);
40252                     return false;
40253                 }
40254             }
40255         }
40256         var fvalue = this.formatDate(value);
40257         if(this.ddMatch && this.ddMatch.test(fvalue)){
40258             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40259             return false;
40260         }
40261         return true;
40262     },
40263
40264     // private
40265     // Provides logic to override the default TriggerField.validateBlur which just returns true
40266     validateBlur : function(){
40267         return !this.menu || !this.menu.isVisible();
40268     },
40269     
40270     getName: function()
40271     {
40272         // returns hidden if it's set..
40273         if (!this.rendered) {return ''};
40274         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40275         
40276     },
40277
40278     /**
40279      * Returns the current date value of the date field.
40280      * @return {Date} The date value
40281      */
40282     getValue : function(){
40283         
40284         return  this.hiddenField ?
40285                 this.hiddenField.value :
40286                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40287     },
40288
40289     /**
40290      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40291      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40292      * (the default format used is "m/d/y").
40293      * <br />Usage:
40294      * <pre><code>
40295 //All of these calls set the same date value (May 4, 2006)
40296
40297 //Pass a date object:
40298 var dt = new Date('5/4/06');
40299 dateField.setValue(dt);
40300
40301 //Pass a date string (default format):
40302 dateField.setValue('5/4/06');
40303
40304 //Pass a date string (custom format):
40305 dateField.format = 'Y-m-d';
40306 dateField.setValue('2006-5-4');
40307 </code></pre>
40308      * @param {String/Date} date The date or valid date string
40309      */
40310     setValue : function(date){
40311         if (this.hiddenField) {
40312             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40313         }
40314         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40315         // make sure the value field is always stored as a date..
40316         this.value = this.parseDate(date);
40317         
40318         
40319     },
40320
40321     // private
40322     parseDate : function(value){
40323         if(!value || value instanceof Date){
40324             return value;
40325         }
40326         var v = Date.parseDate(value, this.format);
40327          if (!v && this.useIso) {
40328             v = Date.parseDate(value, 'Y-m-d');
40329         }
40330         if(!v && this.altFormats){
40331             if(!this.altFormatsArray){
40332                 this.altFormatsArray = this.altFormats.split("|");
40333             }
40334             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40335                 v = Date.parseDate(value, this.altFormatsArray[i]);
40336             }
40337         }
40338         return v;
40339     },
40340
40341     // private
40342     formatDate : function(date, fmt){
40343         return (!date || !(date instanceof Date)) ?
40344                date : date.dateFormat(fmt || this.format);
40345     },
40346
40347     // private
40348     menuListeners : {
40349         select: function(m, d){
40350             
40351             this.setValue(d);
40352             this.fireEvent('select', this, d);
40353         },
40354         show : function(){ // retain focus styling
40355             this.onFocus();
40356         },
40357         hide : function(){
40358             this.focus.defer(10, this);
40359             var ml = this.menuListeners;
40360             this.menu.un("select", ml.select,  this);
40361             this.menu.un("show", ml.show,  this);
40362             this.menu.un("hide", ml.hide,  this);
40363         }
40364     },
40365
40366     // private
40367     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40368     onTriggerClick : function(){
40369         if(this.disabled){
40370             return;
40371         }
40372         if(this.menu == null){
40373             this.menu = new Roo.menu.DateMenu();
40374         }
40375         Roo.apply(this.menu.picker,  {
40376             showClear: this.allowBlank,
40377             minDate : this.minValue,
40378             maxDate : this.maxValue,
40379             disabledDatesRE : this.ddMatch,
40380             disabledDatesText : this.disabledDatesText,
40381             disabledDays : this.disabledDays,
40382             disabledDaysText : this.disabledDaysText,
40383             format : this.useIso ? 'Y-m-d' : this.format,
40384             minText : String.format(this.minText, this.formatDate(this.minValue)),
40385             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40386         });
40387         this.menu.on(Roo.apply({}, this.menuListeners, {
40388             scope:this
40389         }));
40390         this.menu.picker.setValue(this.getValue() || new Date());
40391         this.menu.show(this.el, "tl-bl?");
40392     },
40393
40394     beforeBlur : function(){
40395         var v = this.parseDate(this.getRawValue());
40396         if(v){
40397             this.setValue(v);
40398         }
40399     },
40400
40401     /*@
40402      * overide
40403      * 
40404      */
40405     isDirty : function() {
40406         if(this.disabled) {
40407             return false;
40408         }
40409         
40410         if(typeof(this.startValue) === 'undefined'){
40411             return false;
40412         }
40413         
40414         return String(this.getValue()) !== String(this.startValue);
40415         
40416     }
40417 });/*
40418  * Based on:
40419  * Ext JS Library 1.1.1
40420  * Copyright(c) 2006-2007, Ext JS, LLC.
40421  *
40422  * Originally Released Under LGPL - original licence link has changed is not relivant.
40423  *
40424  * Fork - LGPL
40425  * <script type="text/javascript">
40426  */
40427  
40428 /**
40429  * @class Roo.form.MonthField
40430  * @extends Roo.form.TriggerField
40431  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40432 * @constructor
40433 * Create a new MonthField
40434 * @param {Object} config
40435  */
40436 Roo.form.MonthField = function(config){
40437     
40438     Roo.form.MonthField.superclass.constructor.call(this, config);
40439     
40440       this.addEvents({
40441          
40442         /**
40443          * @event select
40444          * Fires when a date is selected
40445              * @param {Roo.form.MonthFieeld} combo This combo box
40446              * @param {Date} date The date selected
40447              */
40448         'select' : true
40449          
40450     });
40451     
40452     
40453     if(typeof this.minValue == "string") {
40454         this.minValue = this.parseDate(this.minValue);
40455     }
40456     if(typeof this.maxValue == "string") {
40457         this.maxValue = this.parseDate(this.maxValue);
40458     }
40459     this.ddMatch = null;
40460     if(this.disabledDates){
40461         var dd = this.disabledDates;
40462         var re = "(?:";
40463         for(var i = 0; i < dd.length; i++){
40464             re += dd[i];
40465             if(i != dd.length-1) {
40466                 re += "|";
40467             }
40468         }
40469         this.ddMatch = new RegExp(re + ")");
40470     }
40471 };
40472
40473 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40474     /**
40475      * @cfg {String} format
40476      * The default date format string which can be overriden for localization support.  The format must be
40477      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40478      */
40479     format : "M Y",
40480     /**
40481      * @cfg {String} altFormats
40482      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40483      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40484      */
40485     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40486     /**
40487      * @cfg {Array} disabledDays
40488      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40489      */
40490     disabledDays : [0,1,2,3,4,5,6],
40491     /**
40492      * @cfg {String} disabledDaysText
40493      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40494      */
40495     disabledDaysText : "Disabled",
40496     /**
40497      * @cfg {Array} disabledDates
40498      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40499      * expression so they are very powerful. Some examples:
40500      * <ul>
40501      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40502      * <li>["03/08", "09/16"] would disable those days for every year</li>
40503      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40504      * <li>["03/../2006"] would disable every day in March 2006</li>
40505      * <li>["^03"] would disable every day in every March</li>
40506      * </ul>
40507      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40508      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40509      */
40510     disabledDates : null,
40511     /**
40512      * @cfg {String} disabledDatesText
40513      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40514      */
40515     disabledDatesText : "Disabled",
40516     /**
40517      * @cfg {Date/String} minValue
40518      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40519      * valid format (defaults to null).
40520      */
40521     minValue : null,
40522     /**
40523      * @cfg {Date/String} maxValue
40524      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40525      * valid format (defaults to null).
40526      */
40527     maxValue : null,
40528     /**
40529      * @cfg {String} minText
40530      * The error text to display when the date in the cell is before minValue (defaults to
40531      * 'The date in this field must be after {minValue}').
40532      */
40533     minText : "The date in this field must be equal to or after {0}",
40534     /**
40535      * @cfg {String} maxTextf
40536      * The error text to display when the date in the cell is after maxValue (defaults to
40537      * 'The date in this field must be before {maxValue}').
40538      */
40539     maxText : "The date in this field must be equal to or before {0}",
40540     /**
40541      * @cfg {String} invalidText
40542      * The error text to display when the date in the field is invalid (defaults to
40543      * '{value} is not a valid date - it must be in the format {format}').
40544      */
40545     invalidText : "{0} is not a valid date - it must be in the format {1}",
40546     /**
40547      * @cfg {String} triggerClass
40548      * An additional CSS class used to style the trigger button.  The trigger will always get the
40549      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40550      * which displays a calendar icon).
40551      */
40552     triggerClass : 'x-form-date-trigger',
40553     
40554
40555     /**
40556      * @cfg {Boolean} useIso
40557      * if enabled, then the date field will use a hidden field to store the 
40558      * real value as iso formated date. default (true)
40559      */ 
40560     useIso : true,
40561     /**
40562      * @cfg {String/Object} autoCreate
40563      * A DomHelper element spec, or true for a default element spec (defaults to
40564      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40565      */ 
40566     // private
40567     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40568     
40569     // private
40570     hiddenField: false,
40571     
40572     hideMonthPicker : false,
40573     
40574     onRender : function(ct, position)
40575     {
40576         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40577         if (this.useIso) {
40578             this.el.dom.removeAttribute('name'); 
40579             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40580                     'before', true);
40581             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40582             // prevent input submission
40583             this.hiddenName = this.name;
40584         }
40585             
40586             
40587     },
40588     
40589     // private
40590     validateValue : function(value)
40591     {
40592         value = this.formatDate(value);
40593         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40594             return false;
40595         }
40596         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40597              return true;
40598         }
40599         var svalue = value;
40600         value = this.parseDate(value);
40601         if(!value){
40602             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40603             return false;
40604         }
40605         var time = value.getTime();
40606         if(this.minValue && time < this.minValue.getTime()){
40607             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40608             return false;
40609         }
40610         if(this.maxValue && time > this.maxValue.getTime()){
40611             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40612             return false;
40613         }
40614         /*if(this.disabledDays){
40615             var day = value.getDay();
40616             for(var i = 0; i < this.disabledDays.length; i++) {
40617                 if(day === this.disabledDays[i]){
40618                     this.markInvalid(this.disabledDaysText);
40619                     return false;
40620                 }
40621             }
40622         }
40623         */
40624         var fvalue = this.formatDate(value);
40625         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40626             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40627             return false;
40628         }
40629         */
40630         return true;
40631     },
40632
40633     // private
40634     // Provides logic to override the default TriggerField.validateBlur which just returns true
40635     validateBlur : function(){
40636         return !this.menu || !this.menu.isVisible();
40637     },
40638
40639     /**
40640      * Returns the current date value of the date field.
40641      * @return {Date} The date value
40642      */
40643     getValue : function(){
40644         
40645         
40646         
40647         return  this.hiddenField ?
40648                 this.hiddenField.value :
40649                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40650     },
40651
40652     /**
40653      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40654      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40655      * (the default format used is "m/d/y").
40656      * <br />Usage:
40657      * <pre><code>
40658 //All of these calls set the same date value (May 4, 2006)
40659
40660 //Pass a date object:
40661 var dt = new Date('5/4/06');
40662 monthField.setValue(dt);
40663
40664 //Pass a date string (default format):
40665 monthField.setValue('5/4/06');
40666
40667 //Pass a date string (custom format):
40668 monthField.format = 'Y-m-d';
40669 monthField.setValue('2006-5-4');
40670 </code></pre>
40671      * @param {String/Date} date The date or valid date string
40672      */
40673     setValue : function(date){
40674         Roo.log('month setValue' + date);
40675         // can only be first of month..
40676         
40677         var val = this.parseDate(date);
40678         
40679         if (this.hiddenField) {
40680             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40681         }
40682         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40683         this.value = this.parseDate(date);
40684     },
40685
40686     // private
40687     parseDate : function(value){
40688         if(!value || value instanceof Date){
40689             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40690             return value;
40691         }
40692         var v = Date.parseDate(value, this.format);
40693         if (!v && this.useIso) {
40694             v = Date.parseDate(value, 'Y-m-d');
40695         }
40696         if (v) {
40697             // 
40698             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40699         }
40700         
40701         
40702         if(!v && this.altFormats){
40703             if(!this.altFormatsArray){
40704                 this.altFormatsArray = this.altFormats.split("|");
40705             }
40706             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40707                 v = Date.parseDate(value, this.altFormatsArray[i]);
40708             }
40709         }
40710         return v;
40711     },
40712
40713     // private
40714     formatDate : function(date, fmt){
40715         return (!date || !(date instanceof Date)) ?
40716                date : date.dateFormat(fmt || this.format);
40717     },
40718
40719     // private
40720     menuListeners : {
40721         select: function(m, d){
40722             this.setValue(d);
40723             this.fireEvent('select', this, d);
40724         },
40725         show : function(){ // retain focus styling
40726             this.onFocus();
40727         },
40728         hide : function(){
40729             this.focus.defer(10, this);
40730             var ml = this.menuListeners;
40731             this.menu.un("select", ml.select,  this);
40732             this.menu.un("show", ml.show,  this);
40733             this.menu.un("hide", ml.hide,  this);
40734         }
40735     },
40736     // private
40737     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40738     onTriggerClick : function(){
40739         if(this.disabled){
40740             return;
40741         }
40742         if(this.menu == null){
40743             this.menu = new Roo.menu.DateMenu();
40744            
40745         }
40746         
40747         Roo.apply(this.menu.picker,  {
40748             
40749             showClear: this.allowBlank,
40750             minDate : this.minValue,
40751             maxDate : this.maxValue,
40752             disabledDatesRE : this.ddMatch,
40753             disabledDatesText : this.disabledDatesText,
40754             
40755             format : this.useIso ? 'Y-m-d' : this.format,
40756             minText : String.format(this.minText, this.formatDate(this.minValue)),
40757             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40758             
40759         });
40760          this.menu.on(Roo.apply({}, this.menuListeners, {
40761             scope:this
40762         }));
40763        
40764         
40765         var m = this.menu;
40766         var p = m.picker;
40767         
40768         // hide month picker get's called when we called by 'before hide';
40769         
40770         var ignorehide = true;
40771         p.hideMonthPicker  = function(disableAnim){
40772             if (ignorehide) {
40773                 return;
40774             }
40775              if(this.monthPicker){
40776                 Roo.log("hideMonthPicker called");
40777                 if(disableAnim === true){
40778                     this.monthPicker.hide();
40779                 }else{
40780                     this.monthPicker.slideOut('t', {duration:.2});
40781                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40782                     p.fireEvent("select", this, this.value);
40783                     m.hide();
40784                 }
40785             }
40786         }
40787         
40788         Roo.log('picker set value');
40789         Roo.log(this.getValue());
40790         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40791         m.show(this.el, 'tl-bl?');
40792         ignorehide  = false;
40793         // this will trigger hideMonthPicker..
40794         
40795         
40796         // hidden the day picker
40797         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40798         
40799         
40800         
40801       
40802         
40803         p.showMonthPicker.defer(100, p);
40804     
40805         
40806        
40807     },
40808
40809     beforeBlur : function(){
40810         var v = this.parseDate(this.getRawValue());
40811         if(v){
40812             this.setValue(v);
40813         }
40814     }
40815
40816     /** @cfg {Boolean} grow @hide */
40817     /** @cfg {Number} growMin @hide */
40818     /** @cfg {Number} growMax @hide */
40819     /**
40820      * @hide
40821      * @method autoSize
40822      */
40823 });/*
40824  * Based on:
40825  * Ext JS Library 1.1.1
40826  * Copyright(c) 2006-2007, Ext JS, LLC.
40827  *
40828  * Originally Released Under LGPL - original licence link has changed is not relivant.
40829  *
40830  * Fork - LGPL
40831  * <script type="text/javascript">
40832  */
40833  
40834
40835 /**
40836  * @class Roo.form.ComboBox
40837  * @extends Roo.form.TriggerField
40838  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40839  * @constructor
40840  * Create a new ComboBox.
40841  * @param {Object} config Configuration options
40842  */
40843 Roo.form.ComboBox = function(config){
40844     Roo.form.ComboBox.superclass.constructor.call(this, config);
40845     this.addEvents({
40846         /**
40847          * @event expand
40848          * Fires when the dropdown list is expanded
40849              * @param {Roo.form.ComboBox} combo This combo box
40850              */
40851         'expand' : true,
40852         /**
40853          * @event collapse
40854          * Fires when the dropdown list is collapsed
40855              * @param {Roo.form.ComboBox} combo This combo box
40856              */
40857         'collapse' : true,
40858         /**
40859          * @event beforeselect
40860          * Fires before a list item is selected. Return false to cancel the selection.
40861              * @param {Roo.form.ComboBox} combo This combo box
40862              * @param {Roo.data.Record} record The data record returned from the underlying store
40863              * @param {Number} index The index of the selected item in the dropdown list
40864              */
40865         'beforeselect' : true,
40866         /**
40867          * @event select
40868          * Fires when a list item is selected
40869              * @param {Roo.form.ComboBox} combo This combo box
40870              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40871              * @param {Number} index The index of the selected item in the dropdown list
40872              */
40873         'select' : true,
40874         /**
40875          * @event beforequery
40876          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40877          * The event object passed has these properties:
40878              * @param {Roo.form.ComboBox} combo This combo box
40879              * @param {String} query The query
40880              * @param {Boolean} forceAll true to force "all" query
40881              * @param {Boolean} cancel true to cancel the query
40882              * @param {Object} e The query event object
40883              */
40884         'beforequery': true,
40885          /**
40886          * @event add
40887          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40888              * @param {Roo.form.ComboBox} combo This combo box
40889              */
40890         'add' : true,
40891         /**
40892          * @event edit
40893          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40894              * @param {Roo.form.ComboBox} combo This combo box
40895              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40896              */
40897         'edit' : true
40898         
40899         
40900     });
40901     if(this.transform){
40902         this.allowDomMove = false;
40903         var s = Roo.getDom(this.transform);
40904         if(!this.hiddenName){
40905             this.hiddenName = s.name;
40906         }
40907         if(!this.store){
40908             this.mode = 'local';
40909             var d = [], opts = s.options;
40910             for(var i = 0, len = opts.length;i < len; i++){
40911                 var o = opts[i];
40912                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40913                 if(o.selected) {
40914                     this.value = value;
40915                 }
40916                 d.push([value, o.text]);
40917             }
40918             this.store = new Roo.data.SimpleStore({
40919                 'id': 0,
40920                 fields: ['value', 'text'],
40921                 data : d
40922             });
40923             this.valueField = 'value';
40924             this.displayField = 'text';
40925         }
40926         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40927         if(!this.lazyRender){
40928             this.target = true;
40929             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40930             s.parentNode.removeChild(s); // remove it
40931             this.render(this.el.parentNode);
40932         }else{
40933             s.parentNode.removeChild(s); // remove it
40934         }
40935
40936     }
40937     if (this.store) {
40938         this.store = Roo.factory(this.store, Roo.data);
40939     }
40940     
40941     this.selectedIndex = -1;
40942     if(this.mode == 'local'){
40943         if(config.queryDelay === undefined){
40944             this.queryDelay = 10;
40945         }
40946         if(config.minChars === undefined){
40947             this.minChars = 0;
40948         }
40949     }
40950 };
40951
40952 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40953     /**
40954      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40955      */
40956     /**
40957      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40958      * rendering into an Roo.Editor, defaults to false)
40959      */
40960     /**
40961      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40962      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40963      */
40964     /**
40965      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40966      */
40967     /**
40968      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40969      * the dropdown list (defaults to undefined, with no header element)
40970      */
40971
40972      /**
40973      * @cfg {String/Roo.Template} tpl The template to use to render the output
40974      */
40975      
40976     // private
40977     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40978     /**
40979      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40980      */
40981     listWidth: undefined,
40982     /**
40983      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40984      * mode = 'remote' or 'text' if mode = 'local')
40985      */
40986     displayField: undefined,
40987     /**
40988      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40989      * mode = 'remote' or 'value' if mode = 'local'). 
40990      * Note: use of a valueField requires the user make a selection
40991      * in order for a value to be mapped.
40992      */
40993     valueField: undefined,
40994     
40995     
40996     /**
40997      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40998      * field's data value (defaults to the underlying DOM element's name)
40999      */
41000     hiddenName: undefined,
41001     /**
41002      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41003      */
41004     listClass: '',
41005     /**
41006      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41007      */
41008     selectedClass: 'x-combo-selected',
41009     /**
41010      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41011      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41012      * which displays a downward arrow icon).
41013      */
41014     triggerClass : 'x-form-arrow-trigger',
41015     /**
41016      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41017      */
41018     shadow:'sides',
41019     /**
41020      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41021      * anchor positions (defaults to 'tl-bl')
41022      */
41023     listAlign: 'tl-bl?',
41024     /**
41025      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41026      */
41027     maxHeight: 300,
41028     /**
41029      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41030      * query specified by the allQuery config option (defaults to 'query')
41031      */
41032     triggerAction: 'query',
41033     /**
41034      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41035      * (defaults to 4, does not apply if editable = false)
41036      */
41037     minChars : 4,
41038     /**
41039      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41040      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41041      */
41042     typeAhead: false,
41043     /**
41044      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41045      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41046      */
41047     queryDelay: 500,
41048     /**
41049      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41050      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41051      */
41052     pageSize: 0,
41053     /**
41054      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41055      * when editable = true (defaults to false)
41056      */
41057     selectOnFocus:false,
41058     /**
41059      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41060      */
41061     queryParam: 'query',
41062     /**
41063      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41064      * when mode = 'remote' (defaults to 'Loading...')
41065      */
41066     loadingText: 'Loading...',
41067     /**
41068      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41069      */
41070     resizable: false,
41071     /**
41072      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41073      */
41074     handleHeight : 8,
41075     /**
41076      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41077      * traditional select (defaults to true)
41078      */
41079     editable: true,
41080     /**
41081      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41082      */
41083     allQuery: '',
41084     /**
41085      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41086      */
41087     mode: 'remote',
41088     /**
41089      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41090      * listWidth has a higher value)
41091      */
41092     minListWidth : 70,
41093     /**
41094      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41095      * allow the user to set arbitrary text into the field (defaults to false)
41096      */
41097     forceSelection:false,
41098     /**
41099      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41100      * if typeAhead = true (defaults to 250)
41101      */
41102     typeAheadDelay : 250,
41103     /**
41104      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41105      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41106      */
41107     valueNotFoundText : undefined,
41108     /**
41109      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41110      */
41111     blockFocus : false,
41112     
41113     /**
41114      * @cfg {Boolean} disableClear Disable showing of clear button.
41115      */
41116     disableClear : false,
41117     /**
41118      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41119      */
41120     alwaysQuery : false,
41121     
41122     //private
41123     addicon : false,
41124     editicon: false,
41125     
41126     // element that contains real text value.. (when hidden is used..)
41127      
41128     // private
41129     onRender : function(ct, position){
41130         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41131         if(this.hiddenName){
41132             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41133                     'before', true);
41134             this.hiddenField.value =
41135                 this.hiddenValue !== undefined ? this.hiddenValue :
41136                 this.value !== undefined ? this.value : '';
41137
41138             // prevent input submission
41139             this.el.dom.removeAttribute('name');
41140              
41141              
41142         }
41143         if(Roo.isGecko){
41144             this.el.dom.setAttribute('autocomplete', 'off');
41145         }
41146
41147         var cls = 'x-combo-list';
41148
41149         this.list = new Roo.Layer({
41150             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41151         });
41152
41153         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41154         this.list.setWidth(lw);
41155         this.list.swallowEvent('mousewheel');
41156         this.assetHeight = 0;
41157
41158         if(this.title){
41159             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41160             this.assetHeight += this.header.getHeight();
41161         }
41162
41163         this.innerList = this.list.createChild({cls:cls+'-inner'});
41164         this.innerList.on('mouseover', this.onViewOver, this);
41165         this.innerList.on('mousemove', this.onViewMove, this);
41166         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41167         
41168         if(this.allowBlank && !this.pageSize && !this.disableClear){
41169             this.footer = this.list.createChild({cls:cls+'-ft'});
41170             this.pageTb = new Roo.Toolbar(this.footer);
41171            
41172         }
41173         if(this.pageSize){
41174             this.footer = this.list.createChild({cls:cls+'-ft'});
41175             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41176                     {pageSize: this.pageSize});
41177             
41178         }
41179         
41180         if (this.pageTb && this.allowBlank && !this.disableClear) {
41181             var _this = this;
41182             this.pageTb.add(new Roo.Toolbar.Fill(), {
41183                 cls: 'x-btn-icon x-btn-clear',
41184                 text: '&#160;',
41185                 handler: function()
41186                 {
41187                     _this.collapse();
41188                     _this.clearValue();
41189                     _this.onSelect(false, -1);
41190                 }
41191             });
41192         }
41193         if (this.footer) {
41194             this.assetHeight += this.footer.getHeight();
41195         }
41196         
41197
41198         if(!this.tpl){
41199             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41200         }
41201
41202         this.view = new Roo.View(this.innerList, this.tpl, {
41203             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41204         });
41205
41206         this.view.on('click', this.onViewClick, this);
41207
41208         this.store.on('beforeload', this.onBeforeLoad, this);
41209         this.store.on('load', this.onLoad, this);
41210         this.store.on('loadexception', this.onLoadException, this);
41211
41212         if(this.resizable){
41213             this.resizer = new Roo.Resizable(this.list,  {
41214                pinned:true, handles:'se'
41215             });
41216             this.resizer.on('resize', function(r, w, h){
41217                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41218                 this.listWidth = w;
41219                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41220                 this.restrictHeight();
41221             }, this);
41222             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41223         }
41224         if(!this.editable){
41225             this.editable = true;
41226             this.setEditable(false);
41227         }  
41228         
41229         
41230         if (typeof(this.events.add.listeners) != 'undefined') {
41231             
41232             this.addicon = this.wrap.createChild(
41233                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41234        
41235             this.addicon.on('click', function(e) {
41236                 this.fireEvent('add', this);
41237             }, this);
41238         }
41239         if (typeof(this.events.edit.listeners) != 'undefined') {
41240             
41241             this.editicon = this.wrap.createChild(
41242                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41243             if (this.addicon) {
41244                 this.editicon.setStyle('margin-left', '40px');
41245             }
41246             this.editicon.on('click', function(e) {
41247                 
41248                 // we fire even  if inothing is selected..
41249                 this.fireEvent('edit', this, this.lastData );
41250                 
41251             }, this);
41252         }
41253         
41254         
41255         
41256     },
41257
41258     // private
41259     initEvents : function(){
41260         Roo.form.ComboBox.superclass.initEvents.call(this);
41261
41262         this.keyNav = new Roo.KeyNav(this.el, {
41263             "up" : function(e){
41264                 this.inKeyMode = true;
41265                 this.selectPrev();
41266             },
41267
41268             "down" : function(e){
41269                 if(!this.isExpanded()){
41270                     this.onTriggerClick();
41271                 }else{
41272                     this.inKeyMode = true;
41273                     this.selectNext();
41274                 }
41275             },
41276
41277             "enter" : function(e){
41278                 this.onViewClick();
41279                 //return true;
41280             },
41281
41282             "esc" : function(e){
41283                 this.collapse();
41284             },
41285
41286             "tab" : function(e){
41287                 this.onViewClick(false);
41288                 this.fireEvent("specialkey", this, e);
41289                 return true;
41290             },
41291
41292             scope : this,
41293
41294             doRelay : function(foo, bar, hname){
41295                 if(hname == 'down' || this.scope.isExpanded()){
41296                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41297                 }
41298                 return true;
41299             },
41300
41301             forceKeyDown: true
41302         });
41303         this.queryDelay = Math.max(this.queryDelay || 10,
41304                 this.mode == 'local' ? 10 : 250);
41305         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41306         if(this.typeAhead){
41307             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41308         }
41309         if(this.editable !== false){
41310             this.el.on("keyup", this.onKeyUp, this);
41311         }
41312         if(this.forceSelection){
41313             this.on('blur', this.doForce, this);
41314         }
41315     },
41316
41317     onDestroy : function(){
41318         if(this.view){
41319             this.view.setStore(null);
41320             this.view.el.removeAllListeners();
41321             this.view.el.remove();
41322             this.view.purgeListeners();
41323         }
41324         if(this.list){
41325             this.list.destroy();
41326         }
41327         if(this.store){
41328             this.store.un('beforeload', this.onBeforeLoad, this);
41329             this.store.un('load', this.onLoad, this);
41330             this.store.un('loadexception', this.onLoadException, this);
41331         }
41332         Roo.form.ComboBox.superclass.onDestroy.call(this);
41333     },
41334
41335     // private
41336     fireKey : function(e){
41337         if(e.isNavKeyPress() && !this.list.isVisible()){
41338             this.fireEvent("specialkey", this, e);
41339         }
41340     },
41341
41342     // private
41343     onResize: function(w, h){
41344         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41345         
41346         if(typeof w != 'number'){
41347             // we do not handle it!?!?
41348             return;
41349         }
41350         var tw = this.trigger.getWidth();
41351         tw += this.addicon ? this.addicon.getWidth() : 0;
41352         tw += this.editicon ? this.editicon.getWidth() : 0;
41353         var x = w - tw;
41354         this.el.setWidth( this.adjustWidth('input', x));
41355             
41356         this.trigger.setStyle('left', x+'px');
41357         
41358         if(this.list && this.listWidth === undefined){
41359             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41360             this.list.setWidth(lw);
41361             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41362         }
41363         
41364     
41365         
41366     },
41367
41368     /**
41369      * Allow or prevent the user from directly editing the field text.  If false is passed,
41370      * the user will only be able to select from the items defined in the dropdown list.  This method
41371      * is the runtime equivalent of setting the 'editable' config option at config time.
41372      * @param {Boolean} value True to allow the user to directly edit the field text
41373      */
41374     setEditable : function(value){
41375         if(value == this.editable){
41376             return;
41377         }
41378         this.editable = value;
41379         if(!value){
41380             this.el.dom.setAttribute('readOnly', true);
41381             this.el.on('mousedown', this.onTriggerClick,  this);
41382             this.el.addClass('x-combo-noedit');
41383         }else{
41384             this.el.dom.setAttribute('readOnly', false);
41385             this.el.un('mousedown', this.onTriggerClick,  this);
41386             this.el.removeClass('x-combo-noedit');
41387         }
41388     },
41389
41390     // private
41391     onBeforeLoad : function(){
41392         if(!this.hasFocus){
41393             return;
41394         }
41395         this.innerList.update(this.loadingText ?
41396                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41397         this.restrictHeight();
41398         this.selectedIndex = -1;
41399     },
41400
41401     // private
41402     onLoad : function(){
41403         if(!this.hasFocus){
41404             return;
41405         }
41406         if(this.store.getCount() > 0){
41407             this.expand();
41408             this.restrictHeight();
41409             if(this.lastQuery == this.allQuery){
41410                 if(this.editable){
41411                     this.el.dom.select();
41412                 }
41413                 if(!this.selectByValue(this.value, true)){
41414                     this.select(0, true);
41415                 }
41416             }else{
41417                 this.selectNext();
41418                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41419                     this.taTask.delay(this.typeAheadDelay);
41420                 }
41421             }
41422         }else{
41423             this.onEmptyResults();
41424         }
41425         //this.el.focus();
41426     },
41427     // private
41428     onLoadException : function()
41429     {
41430         this.collapse();
41431         Roo.log(this.store.reader.jsonData);
41432         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41433             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41434         }
41435         
41436         
41437     },
41438     // private
41439     onTypeAhead : function(){
41440         if(this.store.getCount() > 0){
41441             var r = this.store.getAt(0);
41442             var newValue = r.data[this.displayField];
41443             var len = newValue.length;
41444             var selStart = this.getRawValue().length;
41445             if(selStart != len){
41446                 this.setRawValue(newValue);
41447                 this.selectText(selStart, newValue.length);
41448             }
41449         }
41450     },
41451
41452     // private
41453     onSelect : function(record, index){
41454         if(this.fireEvent('beforeselect', this, record, index) !== false){
41455             this.setFromData(index > -1 ? record.data : false);
41456             this.collapse();
41457             this.fireEvent('select', this, record, index);
41458         }
41459     },
41460
41461     /**
41462      * Returns the currently selected field value or empty string if no value is set.
41463      * @return {String} value The selected value
41464      */
41465     getValue : function(){
41466         if(this.valueField){
41467             return typeof this.value != 'undefined' ? this.value : '';
41468         }
41469         return Roo.form.ComboBox.superclass.getValue.call(this);
41470     },
41471
41472     /**
41473      * Clears any text/value currently set in the field
41474      */
41475     clearValue : function(){
41476         if(this.hiddenField){
41477             this.hiddenField.value = '';
41478         }
41479         this.value = '';
41480         this.setRawValue('');
41481         this.lastSelectionText = '';
41482         
41483     },
41484
41485     /**
41486      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41487      * will be displayed in the field.  If the value does not match the data value of an existing item,
41488      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41489      * Otherwise the field will be blank (although the value will still be set).
41490      * @param {String} value The value to match
41491      */
41492     setValue : function(v){
41493         var text = v;
41494         if(this.valueField){
41495             var r = this.findRecord(this.valueField, v);
41496             if(r){
41497                 text = r.data[this.displayField];
41498             }else if(this.valueNotFoundText !== undefined){
41499                 text = this.valueNotFoundText;
41500             }
41501         }
41502         this.lastSelectionText = text;
41503         if(this.hiddenField){
41504             this.hiddenField.value = v;
41505         }
41506         Roo.form.ComboBox.superclass.setValue.call(this, text);
41507         this.value = v;
41508     },
41509     /**
41510      * @property {Object} the last set data for the element
41511      */
41512     
41513     lastData : false,
41514     /**
41515      * Sets the value of the field based on a object which is related to the record format for the store.
41516      * @param {Object} value the value to set as. or false on reset?
41517      */
41518     setFromData : function(o){
41519         var dv = ''; // display value
41520         var vv = ''; // value value..
41521         this.lastData = o;
41522         if (this.displayField) {
41523             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41524         } else {
41525             // this is an error condition!!!
41526             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41527         }
41528         
41529         if(this.valueField){
41530             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41531         }
41532         if(this.hiddenField){
41533             this.hiddenField.value = vv;
41534             
41535             this.lastSelectionText = dv;
41536             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41537             this.value = vv;
41538             return;
41539         }
41540         // no hidden field.. - we store the value in 'value', but still display
41541         // display field!!!!
41542         this.lastSelectionText = dv;
41543         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41544         this.value = vv;
41545         
41546         
41547     },
41548     // private
41549     reset : function(){
41550         // overridden so that last data is reset..
41551         this.setValue(this.resetValue);
41552         this.clearInvalid();
41553         this.lastData = false;
41554         if (this.view) {
41555             this.view.clearSelections();
41556         }
41557     },
41558     // private
41559     findRecord : function(prop, value){
41560         var record;
41561         if(this.store.getCount() > 0){
41562             this.store.each(function(r){
41563                 if(r.data[prop] == value){
41564                     record = r;
41565                     return false;
41566                 }
41567                 return true;
41568             });
41569         }
41570         return record;
41571     },
41572     
41573     getName: function()
41574     {
41575         // returns hidden if it's set..
41576         if (!this.rendered) {return ''};
41577         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41578         
41579     },
41580     // private
41581     onViewMove : function(e, t){
41582         this.inKeyMode = false;
41583     },
41584
41585     // private
41586     onViewOver : function(e, t){
41587         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41588             return;
41589         }
41590         var item = this.view.findItemFromChild(t);
41591         if(item){
41592             var index = this.view.indexOf(item);
41593             this.select(index, false);
41594         }
41595     },
41596
41597     // private
41598     onViewClick : function(doFocus)
41599     {
41600         var index = this.view.getSelectedIndexes()[0];
41601         var r = this.store.getAt(index);
41602         if(r){
41603             this.onSelect(r, index);
41604         }
41605         if(doFocus !== false && !this.blockFocus){
41606             this.el.focus();
41607         }
41608     },
41609
41610     // private
41611     restrictHeight : function(){
41612         this.innerList.dom.style.height = '';
41613         var inner = this.innerList.dom;
41614         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41615         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41616         this.list.beginUpdate();
41617         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41618         this.list.alignTo(this.el, this.listAlign);
41619         this.list.endUpdate();
41620     },
41621
41622     // private
41623     onEmptyResults : function(){
41624         this.collapse();
41625     },
41626
41627     /**
41628      * Returns true if the dropdown list is expanded, else false.
41629      */
41630     isExpanded : function(){
41631         return this.list.isVisible();
41632     },
41633
41634     /**
41635      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41636      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41637      * @param {String} value The data value of the item to select
41638      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41639      * selected item if it is not currently in view (defaults to true)
41640      * @return {Boolean} True if the value matched an item in the list, else false
41641      */
41642     selectByValue : function(v, scrollIntoView){
41643         if(v !== undefined && v !== null){
41644             var r = this.findRecord(this.valueField || this.displayField, v);
41645             if(r){
41646                 this.select(this.store.indexOf(r), scrollIntoView);
41647                 return true;
41648             }
41649         }
41650         return false;
41651     },
41652
41653     /**
41654      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41655      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41656      * @param {Number} index The zero-based index of the list item to select
41657      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41658      * selected item if it is not currently in view (defaults to true)
41659      */
41660     select : function(index, scrollIntoView){
41661         this.selectedIndex = index;
41662         this.view.select(index);
41663         if(scrollIntoView !== false){
41664             var el = this.view.getNode(index);
41665             if(el){
41666                 this.innerList.scrollChildIntoView(el, false);
41667             }
41668         }
41669     },
41670
41671     // private
41672     selectNext : 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 < ct-1){
41678                 this.select(this.selectedIndex+1);
41679             }
41680         }
41681     },
41682
41683     // private
41684     selectPrev : function(){
41685         var ct = this.store.getCount();
41686         if(ct > 0){
41687             if(this.selectedIndex == -1){
41688                 this.select(0);
41689             }else if(this.selectedIndex != 0){
41690                 this.select(this.selectedIndex-1);
41691             }
41692         }
41693     },
41694
41695     // private
41696     onKeyUp : function(e){
41697         if(this.editable !== false && !e.isSpecialKey()){
41698             this.lastKey = e.getKey();
41699             this.dqTask.delay(this.queryDelay);
41700         }
41701     },
41702
41703     // private
41704     validateBlur : function(){
41705         return !this.list || !this.list.isVisible();   
41706     },
41707
41708     // private
41709     initQuery : function(){
41710         this.doQuery(this.getRawValue());
41711     },
41712
41713     // private
41714     doForce : function(){
41715         if(this.el.dom.value.length > 0){
41716             this.el.dom.value =
41717                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41718              
41719         }
41720     },
41721
41722     /**
41723      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41724      * query allowing the query action to be canceled if needed.
41725      * @param {String} query The SQL query to execute
41726      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41727      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41728      * saved in the current store (defaults to false)
41729      */
41730     doQuery : function(q, forceAll){
41731         if(q === undefined || q === null){
41732             q = '';
41733         }
41734         var qe = {
41735             query: q,
41736             forceAll: forceAll,
41737             combo: this,
41738             cancel:false
41739         };
41740         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41741             return false;
41742         }
41743         q = qe.query;
41744         forceAll = qe.forceAll;
41745         if(forceAll === true || (q.length >= this.minChars)){
41746             if(this.lastQuery != q || this.alwaysQuery){
41747                 this.lastQuery = q;
41748                 if(this.mode == 'local'){
41749                     this.selectedIndex = -1;
41750                     if(forceAll){
41751                         this.store.clearFilter();
41752                     }else{
41753                         this.store.filter(this.displayField, q);
41754                     }
41755                     this.onLoad();
41756                 }else{
41757                     this.store.baseParams[this.queryParam] = q;
41758                     this.store.load({
41759                         params: this.getParams(q)
41760                     });
41761                     this.expand();
41762                 }
41763             }else{
41764                 this.selectedIndex = -1;
41765                 this.onLoad();   
41766             }
41767         }
41768     },
41769
41770     // private
41771     getParams : function(q){
41772         var p = {};
41773         //p[this.queryParam] = q;
41774         if(this.pageSize){
41775             p.start = 0;
41776             p.limit = this.pageSize;
41777         }
41778         return p;
41779     },
41780
41781     /**
41782      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41783      */
41784     collapse : function(){
41785         if(!this.isExpanded()){
41786             return;
41787         }
41788         this.list.hide();
41789         Roo.get(document).un('mousedown', this.collapseIf, this);
41790         Roo.get(document).un('mousewheel', this.collapseIf, this);
41791         if (!this.editable) {
41792             Roo.get(document).un('keydown', this.listKeyPress, this);
41793         }
41794         this.fireEvent('collapse', this);
41795     },
41796
41797     // private
41798     collapseIf : function(e){
41799         if(!e.within(this.wrap) && !e.within(this.list)){
41800             this.collapse();
41801         }
41802     },
41803
41804     /**
41805      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41806      */
41807     expand : function(){
41808         if(this.isExpanded() || !this.hasFocus){
41809             return;
41810         }
41811         this.list.alignTo(this.el, this.listAlign);
41812         this.list.show();
41813         Roo.get(document).on('mousedown', this.collapseIf, this);
41814         Roo.get(document).on('mousewheel', this.collapseIf, this);
41815         if (!this.editable) {
41816             Roo.get(document).on('keydown', this.listKeyPress, this);
41817         }
41818         
41819         this.fireEvent('expand', this);
41820     },
41821
41822     // private
41823     // Implements the default empty TriggerField.onTriggerClick function
41824     onTriggerClick : function(){
41825         if(this.disabled){
41826             return;
41827         }
41828         if(this.isExpanded()){
41829             this.collapse();
41830             if (!this.blockFocus) {
41831                 this.el.focus();
41832             }
41833             
41834         }else {
41835             this.hasFocus = true;
41836             if(this.triggerAction == 'all') {
41837                 this.doQuery(this.allQuery, true);
41838             } else {
41839                 this.doQuery(this.getRawValue());
41840             }
41841             if (!this.blockFocus) {
41842                 this.el.focus();
41843             }
41844         }
41845     },
41846     listKeyPress : function(e)
41847     {
41848         //Roo.log('listkeypress');
41849         // scroll to first matching element based on key pres..
41850         if (e.isSpecialKey()) {
41851             return false;
41852         }
41853         var k = String.fromCharCode(e.getKey()).toUpperCase();
41854         //Roo.log(k);
41855         var match  = false;
41856         var csel = this.view.getSelectedNodes();
41857         var cselitem = false;
41858         if (csel.length) {
41859             var ix = this.view.indexOf(csel[0]);
41860             cselitem  = this.store.getAt(ix);
41861             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41862                 cselitem = false;
41863             }
41864             
41865         }
41866         
41867         this.store.each(function(v) { 
41868             if (cselitem) {
41869                 // start at existing selection.
41870                 if (cselitem.id == v.id) {
41871                     cselitem = false;
41872                 }
41873                 return;
41874             }
41875                 
41876             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41877                 match = this.store.indexOf(v);
41878                 return false;
41879             }
41880         }, this);
41881         
41882         if (match === false) {
41883             return true; // no more action?
41884         }
41885         // scroll to?
41886         this.view.select(match);
41887         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41888         sn.scrollIntoView(sn.dom.parentNode, false);
41889     }
41890
41891     /** 
41892     * @cfg {Boolean} grow 
41893     * @hide 
41894     */
41895     /** 
41896     * @cfg {Number} growMin 
41897     * @hide 
41898     */
41899     /** 
41900     * @cfg {Number} growMax 
41901     * @hide 
41902     */
41903     /**
41904      * @hide
41905      * @method autoSize
41906      */
41907 });/*
41908  * Copyright(c) 2010-2012, Roo J Solutions Limited
41909  *
41910  * Licence LGPL
41911  *
41912  */
41913
41914 /**
41915  * @class Roo.form.ComboBoxArray
41916  * @extends Roo.form.TextField
41917  * A facebook style adder... for lists of email / people / countries  etc...
41918  * pick multiple items from a combo box, and shows each one.
41919  *
41920  *  Fred [x]  Brian [x]  [Pick another |v]
41921  *
41922  *
41923  *  For this to work: it needs various extra information
41924  *    - normal combo problay has
41925  *      name, hiddenName
41926  *    + displayField, valueField
41927  *
41928  *    For our purpose...
41929  *
41930  *
41931  *   If we change from 'extends' to wrapping...
41932  *   
41933  *  
41934  *
41935  
41936  
41937  * @constructor
41938  * Create a new ComboBoxArray.
41939  * @param {Object} config Configuration options
41940  */
41941  
41942
41943 Roo.form.ComboBoxArray = function(config)
41944 {
41945     this.addEvents({
41946         /**
41947          * @event beforeremove
41948          * Fires before remove the value from the list
41949              * @param {Roo.form.ComboBoxArray} _self This combo box array
41950              * @param {Roo.form.ComboBoxArray.Item} item removed item
41951              */
41952         'beforeremove' : true,
41953         /**
41954          * @event remove
41955          * Fires when remove the value from the list
41956              * @param {Roo.form.ComboBoxArray} _self This combo box array
41957              * @param {Roo.form.ComboBoxArray.Item} item removed item
41958              */
41959         'remove' : true
41960         
41961         
41962     });
41963     
41964     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41965     
41966     this.items = new Roo.util.MixedCollection(false);
41967     
41968     // construct the child combo...
41969     
41970     
41971     
41972     
41973    
41974     
41975 }
41976
41977  
41978 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41979
41980     /**
41981      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41982      */
41983     
41984     lastData : false,
41985     
41986     // behavies liek a hiddne field
41987     inputType:      'hidden',
41988     /**
41989      * @cfg {Number} width The width of the box that displays the selected element
41990      */ 
41991     width:          300,
41992
41993     
41994     
41995     /**
41996      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41997      */
41998     name : false,
41999     /**
42000      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42001      */
42002     hiddenName : false,
42003     
42004     
42005     // private the array of items that are displayed..
42006     items  : false,
42007     // private - the hidden field el.
42008     hiddenEl : false,
42009     // private - the filed el..
42010     el : false,
42011     
42012     //validateValue : function() { return true; }, // all values are ok!
42013     //onAddClick: function() { },
42014     
42015     onRender : function(ct, position) 
42016     {
42017         
42018         // create the standard hidden element
42019         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42020         
42021         
42022         // give fake names to child combo;
42023         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42024         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42025         
42026         this.combo = Roo.factory(this.combo, Roo.form);
42027         this.combo.onRender(ct, position);
42028         if (typeof(this.combo.width) != 'undefined') {
42029             this.combo.onResize(this.combo.width,0);
42030         }
42031         
42032         this.combo.initEvents();
42033         
42034         // assigned so form know we need to do this..
42035         this.store          = this.combo.store;
42036         this.valueField     = this.combo.valueField;
42037         this.displayField   = this.combo.displayField ;
42038         
42039         
42040         this.combo.wrap.addClass('x-cbarray-grp');
42041         
42042         var cbwrap = this.combo.wrap.createChild(
42043             {tag: 'div', cls: 'x-cbarray-cb'},
42044             this.combo.el.dom
42045         );
42046         
42047              
42048         this.hiddenEl = this.combo.wrap.createChild({
42049             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42050         });
42051         this.el = this.combo.wrap.createChild({
42052             tag: 'input',  type:'hidden' , name: this.name, value : ''
42053         });
42054          //   this.el.dom.removeAttribute("name");
42055         
42056         
42057         this.outerWrap = this.combo.wrap;
42058         this.wrap = cbwrap;
42059         
42060         this.outerWrap.setWidth(this.width);
42061         this.outerWrap.dom.removeChild(this.el.dom);
42062         
42063         this.wrap.dom.appendChild(this.el.dom);
42064         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42065         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42066         
42067         this.combo.trigger.setStyle('position','relative');
42068         this.combo.trigger.setStyle('left', '0px');
42069         this.combo.trigger.setStyle('top', '2px');
42070         
42071         this.combo.el.setStyle('vertical-align', 'text-bottom');
42072         
42073         //this.trigger.setStyle('vertical-align', 'top');
42074         
42075         // this should use the code from combo really... on('add' ....)
42076         if (this.adder) {
42077             
42078         
42079             this.adder = this.outerWrap.createChild(
42080                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42081             var _t = this;
42082             this.adder.on('click', function(e) {
42083                 _t.fireEvent('adderclick', this, e);
42084             }, _t);
42085         }
42086         //var _t = this;
42087         //this.adder.on('click', this.onAddClick, _t);
42088         
42089         
42090         this.combo.on('select', function(cb, rec, ix) {
42091             this.addItem(rec.data);
42092             
42093             cb.setValue('');
42094             cb.el.dom.value = '';
42095             //cb.lastData = rec.data;
42096             // add to list
42097             
42098         }, this);
42099         
42100         
42101     },
42102     
42103     
42104     getName: function()
42105     {
42106         // returns hidden if it's set..
42107         if (!this.rendered) {return ''};
42108         return  this.hiddenName ? this.hiddenName : this.name;
42109         
42110     },
42111     
42112     
42113     onResize: function(w, h){
42114         
42115         return;
42116         // not sure if this is needed..
42117         //this.combo.onResize(w,h);
42118         
42119         if(typeof w != 'number'){
42120             // we do not handle it!?!?
42121             return;
42122         }
42123         var tw = this.combo.trigger.getWidth();
42124         tw += this.addicon ? this.addicon.getWidth() : 0;
42125         tw += this.editicon ? this.editicon.getWidth() : 0;
42126         var x = w - tw;
42127         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42128             
42129         this.combo.trigger.setStyle('left', '0px');
42130         
42131         if(this.list && this.listWidth === undefined){
42132             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42133             this.list.setWidth(lw);
42134             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42135         }
42136         
42137     
42138         
42139     },
42140     
42141     addItem: function(rec)
42142     {
42143         var valueField = this.combo.valueField;
42144         var displayField = this.combo.displayField;
42145         if (this.items.indexOfKey(rec[valueField]) > -1) {
42146             //console.log("GOT " + rec.data.id);
42147             return;
42148         }
42149         
42150         var x = new Roo.form.ComboBoxArray.Item({
42151             //id : rec[this.idField],
42152             data : rec,
42153             displayField : displayField ,
42154             tipField : displayField ,
42155             cb : this
42156         });
42157         // use the 
42158         this.items.add(rec[valueField],x);
42159         // add it before the element..
42160         this.updateHiddenEl();
42161         x.render(this.outerWrap, this.wrap.dom);
42162         // add the image handler..
42163     },
42164     
42165     updateHiddenEl : function()
42166     {
42167         this.validate();
42168         if (!this.hiddenEl) {
42169             return;
42170         }
42171         var ar = [];
42172         var idField = this.combo.valueField;
42173         
42174         this.items.each(function(f) {
42175             ar.push(f.data[idField]);
42176            
42177         });
42178         this.hiddenEl.dom.value = ar.join(',');
42179         this.validate();
42180     },
42181     
42182     reset : function()
42183     {
42184         this.items.clear();
42185         
42186         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42187            el.remove();
42188         });
42189         
42190         this.el.dom.value = '';
42191         if (this.hiddenEl) {
42192             this.hiddenEl.dom.value = '';
42193         }
42194         
42195     },
42196     getValue: function()
42197     {
42198         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42199     },
42200     setValue: function(v) // not a valid action - must use addItems..
42201     {
42202          
42203         this.reset();
42204         
42205         
42206         
42207         if (this.store.isLocal && (typeof(v) == 'string')) {
42208             // then we can use the store to find the values..
42209             // comma seperated at present.. this needs to allow JSON based encoding..
42210             this.hiddenEl.value  = v;
42211             var v_ar = [];
42212             Roo.each(v.split(','), function(k) {
42213                 Roo.log("CHECK " + this.valueField + ',' + k);
42214                 var li = this.store.query(this.valueField, k);
42215                 if (!li.length) {
42216                     return;
42217                 }
42218                 var add = {};
42219                 add[this.valueField] = k;
42220                 add[this.displayField] = li.item(0).data[this.displayField];
42221                 
42222                 this.addItem(add);
42223             }, this) 
42224              
42225         }
42226         if (typeof(v) == 'object' ) {
42227             // then let's assume it's an array of objects..
42228             Roo.each(v, function(l) {
42229                 this.addItem(l);
42230             }, this);
42231              
42232         }
42233         
42234         
42235     },
42236     setFromData: function(v)
42237     {
42238         // this recieves an object, if setValues is called.
42239         this.reset();
42240         this.el.dom.value = v[this.displayField];
42241         this.hiddenEl.dom.value = v[this.valueField];
42242         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42243             return;
42244         }
42245         var kv = v[this.valueField];
42246         var dv = v[this.displayField];
42247         kv = typeof(kv) != 'string' ? '' : kv;
42248         dv = typeof(dv) != 'string' ? '' : dv;
42249         
42250         
42251         var keys = kv.split(',');
42252         var display = dv.split(',');
42253         for (var i = 0 ; i < keys.length; i++) {
42254             
42255             add = {};
42256             add[this.valueField] = keys[i];
42257             add[this.displayField] = display[i];
42258             this.addItem(add);
42259         }
42260       
42261         
42262     },
42263     
42264     /**
42265      * Validates the combox array value
42266      * @return {Boolean} True if the value is valid, else false
42267      */
42268     validate : function(){
42269         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42270             this.clearInvalid();
42271             return true;
42272         }
42273         return false;
42274     },
42275     
42276     validateValue : function(value){
42277         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42278         
42279     },
42280     
42281     /*@
42282      * overide
42283      * 
42284      */
42285     isDirty : function() {
42286         if(this.disabled) {
42287             return false;
42288         }
42289         
42290         try {
42291             var d = Roo.decode(String(this.originalValue));
42292         } catch (e) {
42293             return String(this.getValue()) !== String(this.originalValue);
42294         }
42295         
42296         var originalValue = [];
42297         
42298         for (var i = 0; i < d.length; i++){
42299             originalValue.push(d[i][this.valueField]);
42300         }
42301         
42302         return String(this.getValue()) !== String(originalValue.join(','));
42303         
42304     }
42305     
42306 });
42307
42308
42309
42310 /**
42311  * @class Roo.form.ComboBoxArray.Item
42312  * @extends Roo.BoxComponent
42313  * A selected item in the list
42314  *  Fred [x]  Brian [x]  [Pick another |v]
42315  * 
42316  * @constructor
42317  * Create a new item.
42318  * @param {Object} config Configuration options
42319  */
42320  
42321 Roo.form.ComboBoxArray.Item = function(config) {
42322     config.id = Roo.id();
42323     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42324 }
42325
42326 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42327     data : {},
42328     cb: false,
42329     displayField : false,
42330     tipField : false,
42331     
42332     
42333     defaultAutoCreate : {
42334         tag: 'div',
42335         cls: 'x-cbarray-item',
42336         cn : [ 
42337             { tag: 'div' },
42338             {
42339                 tag: 'img',
42340                 width:16,
42341                 height : 16,
42342                 src : Roo.BLANK_IMAGE_URL ,
42343                 align: 'center'
42344             }
42345         ]
42346         
42347     },
42348     
42349  
42350     onRender : function(ct, position)
42351     {
42352         Roo.form.Field.superclass.onRender.call(this, ct, position);
42353         
42354         if(!this.el){
42355             var cfg = this.getAutoCreate();
42356             this.el = ct.createChild(cfg, position);
42357         }
42358         
42359         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42360         
42361         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42362             this.cb.renderer(this.data) :
42363             String.format('{0}',this.data[this.displayField]);
42364         
42365             
42366         this.el.child('div').dom.setAttribute('qtip',
42367                         String.format('{0}',this.data[this.tipField])
42368         );
42369         
42370         this.el.child('img').on('click', this.remove, this);
42371         
42372     },
42373    
42374     remove : function()
42375     {
42376         if(this.cb.disabled){
42377             return;
42378         }
42379         
42380         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42381             this.cb.items.remove(this);
42382             this.el.child('img').un('click', this.remove, this);
42383             this.el.remove();
42384             this.cb.updateHiddenEl();
42385
42386             this.cb.fireEvent('remove', this.cb, this);
42387         }
42388         
42389     }
42390 });/*
42391  * Based on:
42392  * Ext JS Library 1.1.1
42393  * Copyright(c) 2006-2007, Ext JS, LLC.
42394  *
42395  * Originally Released Under LGPL - original licence link has changed is not relivant.
42396  *
42397  * Fork - LGPL
42398  * <script type="text/javascript">
42399  */
42400 /**
42401  * @class Roo.form.Checkbox
42402  * @extends Roo.form.Field
42403  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42404  * @constructor
42405  * Creates a new Checkbox
42406  * @param {Object} config Configuration options
42407  */
42408 Roo.form.Checkbox = function(config){
42409     Roo.form.Checkbox.superclass.constructor.call(this, config);
42410     this.addEvents({
42411         /**
42412          * @event check
42413          * Fires when the checkbox is checked or unchecked.
42414              * @param {Roo.form.Checkbox} this This checkbox
42415              * @param {Boolean} checked The new checked value
42416              */
42417         check : true
42418     });
42419 };
42420
42421 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42422     /**
42423      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42424      */
42425     focusClass : undefined,
42426     /**
42427      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42428      */
42429     fieldClass: "x-form-field",
42430     /**
42431      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42432      */
42433     checked: false,
42434     /**
42435      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42436      * {tag: "input", type: "checkbox", autocomplete: "off"})
42437      */
42438     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42439     /**
42440      * @cfg {String} boxLabel The text that appears beside the checkbox
42441      */
42442     boxLabel : "",
42443     /**
42444      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42445      */  
42446     inputValue : '1',
42447     /**
42448      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42449      */
42450      valueOff: '0', // value when not checked..
42451
42452     actionMode : 'viewEl', 
42453     //
42454     // private
42455     itemCls : 'x-menu-check-item x-form-item',
42456     groupClass : 'x-menu-group-item',
42457     inputType : 'hidden',
42458     
42459     
42460     inSetChecked: false, // check that we are not calling self...
42461     
42462     inputElement: false, // real input element?
42463     basedOn: false, // ????
42464     
42465     isFormField: true, // not sure where this is needed!!!!
42466
42467     onResize : function(){
42468         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42469         if(!this.boxLabel){
42470             this.el.alignTo(this.wrap, 'c-c');
42471         }
42472     },
42473
42474     initEvents : function(){
42475         Roo.form.Checkbox.superclass.initEvents.call(this);
42476         this.el.on("click", this.onClick,  this);
42477         this.el.on("change", this.onClick,  this);
42478     },
42479
42480
42481     getResizeEl : function(){
42482         return this.wrap;
42483     },
42484
42485     getPositionEl : function(){
42486         return this.wrap;
42487     },
42488
42489     // private
42490     onRender : function(ct, position){
42491         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42492         /*
42493         if(this.inputValue !== undefined){
42494             this.el.dom.value = this.inputValue;
42495         }
42496         */
42497         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42498         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42499         var viewEl = this.wrap.createChild({ 
42500             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42501         this.viewEl = viewEl;   
42502         this.wrap.on('click', this.onClick,  this); 
42503         
42504         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42505         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42506         
42507         
42508         
42509         if(this.boxLabel){
42510             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42511         //    viewEl.on('click', this.onClick,  this); 
42512         }
42513         //if(this.checked){
42514             this.setChecked(this.checked);
42515         //}else{
42516             //this.checked = this.el.dom;
42517         //}
42518
42519     },
42520
42521     // private
42522     initValue : Roo.emptyFn,
42523
42524     /**
42525      * Returns the checked state of the checkbox.
42526      * @return {Boolean} True if checked, else false
42527      */
42528     getValue : function(){
42529         if(this.el){
42530             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42531         }
42532         return this.valueOff;
42533         
42534     },
42535
42536         // private
42537     onClick : function(){ 
42538         if (this.disabled) {
42539             return;
42540         }
42541         this.setChecked(!this.checked);
42542
42543         //if(this.el.dom.checked != this.checked){
42544         //    this.setValue(this.el.dom.checked);
42545        // }
42546     },
42547
42548     /**
42549      * Sets the checked state of the checkbox.
42550      * On is always based on a string comparison between inputValue and the param.
42551      * @param {Boolean/String} value - the value to set 
42552      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42553      */
42554     setValue : function(v,suppressEvent){
42555         
42556         
42557         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42558         //if(this.el && this.el.dom){
42559         //    this.el.dom.checked = this.checked;
42560         //    this.el.dom.defaultChecked = this.checked;
42561         //}
42562         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42563         //this.fireEvent("check", this, this.checked);
42564     },
42565     // private..
42566     setChecked : function(state,suppressEvent)
42567     {
42568         if (this.inSetChecked) {
42569             this.checked = state;
42570             return;
42571         }
42572         
42573     
42574         if(this.wrap){
42575             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42576         }
42577         this.checked = state;
42578         if(suppressEvent !== true){
42579             this.fireEvent('check', this, state);
42580         }
42581         this.inSetChecked = true;
42582         this.el.dom.value = state ? this.inputValue : this.valueOff;
42583         this.inSetChecked = false;
42584         
42585     },
42586     // handle setting of hidden value by some other method!!?!?
42587     setFromHidden: function()
42588     {
42589         if(!this.el){
42590             return;
42591         }
42592         //console.log("SET FROM HIDDEN");
42593         //alert('setFrom hidden');
42594         this.setValue(this.el.dom.value);
42595     },
42596     
42597     onDestroy : function()
42598     {
42599         if(this.viewEl){
42600             Roo.get(this.viewEl).remove();
42601         }
42602          
42603         Roo.form.Checkbox.superclass.onDestroy.call(this);
42604     },
42605     
42606     setBoxLabel : function(str)
42607     {
42608         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42609     }
42610
42611 });/*
42612  * Based on:
42613  * Ext JS Library 1.1.1
42614  * Copyright(c) 2006-2007, Ext JS, LLC.
42615  *
42616  * Originally Released Under LGPL - original licence link has changed is not relivant.
42617  *
42618  * Fork - LGPL
42619  * <script type="text/javascript">
42620  */
42621  
42622 /**
42623  * @class Roo.form.Radio
42624  * @extends Roo.form.Checkbox
42625  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42626  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42627  * @constructor
42628  * Creates a new Radio
42629  * @param {Object} config Configuration options
42630  */
42631 Roo.form.Radio = function(){
42632     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42633 };
42634 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42635     inputType: 'radio',
42636
42637     /**
42638      * If this radio is part of a group, it will return the selected value
42639      * @return {String}
42640      */
42641     getGroupValue : function(){
42642         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42643     },
42644     
42645     
42646     onRender : function(ct, position){
42647         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42648         
42649         if(this.inputValue !== undefined){
42650             this.el.dom.value = this.inputValue;
42651         }
42652          
42653         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42654         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42655         //var viewEl = this.wrap.createChild({ 
42656         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42657         //this.viewEl = viewEl;   
42658         //this.wrap.on('click', this.onClick,  this); 
42659         
42660         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42661         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42662         
42663         
42664         
42665         if(this.boxLabel){
42666             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42667         //    viewEl.on('click', this.onClick,  this); 
42668         }
42669          if(this.checked){
42670             this.el.dom.checked =   'checked' ;
42671         }
42672          
42673     } 
42674     
42675     
42676 });//<script type="text/javascript">
42677
42678 /*
42679  * Based  Ext JS Library 1.1.1
42680  * Copyright(c) 2006-2007, Ext JS, LLC.
42681  * LGPL
42682  *
42683  */
42684  
42685 /**
42686  * @class Roo.HtmlEditorCore
42687  * @extends Roo.Component
42688  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42689  *
42690  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42691  */
42692
42693 Roo.HtmlEditorCore = function(config){
42694     
42695     
42696     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42697     
42698     
42699     this.addEvents({
42700         /**
42701          * @event initialize
42702          * Fires when the editor is fully initialized (including the iframe)
42703          * @param {Roo.HtmlEditorCore} this
42704          */
42705         initialize: true,
42706         /**
42707          * @event activate
42708          * Fires when the editor is first receives the focus. Any insertion must wait
42709          * until after this event.
42710          * @param {Roo.HtmlEditorCore} this
42711          */
42712         activate: true,
42713          /**
42714          * @event beforesync
42715          * Fires before the textarea is updated with content from the editor iframe. Return false
42716          * to cancel the sync.
42717          * @param {Roo.HtmlEditorCore} this
42718          * @param {String} html
42719          */
42720         beforesync: true,
42721          /**
42722          * @event beforepush
42723          * Fires before the iframe editor is updated with content from the textarea. Return false
42724          * to cancel the push.
42725          * @param {Roo.HtmlEditorCore} this
42726          * @param {String} html
42727          */
42728         beforepush: true,
42729          /**
42730          * @event sync
42731          * Fires when the textarea is updated with content from the editor iframe.
42732          * @param {Roo.HtmlEditorCore} this
42733          * @param {String} html
42734          */
42735         sync: true,
42736          /**
42737          * @event push
42738          * Fires when the iframe editor is updated with content from the textarea.
42739          * @param {Roo.HtmlEditorCore} this
42740          * @param {String} html
42741          */
42742         push: true,
42743         
42744         /**
42745          * @event editorevent
42746          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42747          * @param {Roo.HtmlEditorCore} this
42748          */
42749         editorevent: true
42750         
42751     });
42752     
42753     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42754     
42755     // defaults : white / black...
42756     this.applyBlacklists();
42757     
42758     
42759     
42760 };
42761
42762
42763 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42764
42765
42766      /**
42767      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42768      */
42769     
42770     owner : false,
42771     
42772      /**
42773      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42774      *                        Roo.resizable.
42775      */
42776     resizable : false,
42777      /**
42778      * @cfg {Number} height (in pixels)
42779      */   
42780     height: 300,
42781    /**
42782      * @cfg {Number} width (in pixels)
42783      */   
42784     width: 500,
42785     
42786     /**
42787      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42788      * 
42789      */
42790     stylesheets: false,
42791     
42792     // id of frame..
42793     frameId: false,
42794     
42795     // private properties
42796     validationEvent : false,
42797     deferHeight: true,
42798     initialized : false,
42799     activated : false,
42800     sourceEditMode : false,
42801     onFocus : Roo.emptyFn,
42802     iframePad:3,
42803     hideMode:'offsets',
42804     
42805     clearUp: true,
42806     
42807     // blacklist + whitelisted elements..
42808     black: false,
42809     white: false,
42810      
42811     
42812
42813     /**
42814      * Protected method that will not generally be called directly. It
42815      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42816      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42817      */
42818     getDocMarkup : function(){
42819         // body styles..
42820         var st = '';
42821         
42822         // inherit styels from page...?? 
42823         if (this.stylesheets === false) {
42824             
42825             Roo.get(document.head).select('style').each(function(node) {
42826                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42827             });
42828             
42829             Roo.get(document.head).select('link').each(function(node) { 
42830                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42831             });
42832             
42833         } else if (!this.stylesheets.length) {
42834                 // simple..
42835                 st = '<style type="text/css">' +
42836                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42837                    '</style>';
42838         } else { 
42839             
42840         }
42841         
42842         st +=  '<style type="text/css">' +
42843             'IMG { cursor: pointer } ' +
42844         '</style>';
42845
42846         
42847         return '<html><head>' + st  +
42848             //<style type="text/css">' +
42849             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42850             //'</style>' +
42851             ' </head><body class="roo-htmleditor-body"></body></html>';
42852     },
42853
42854     // private
42855     onRender : function(ct, position)
42856     {
42857         var _t = this;
42858         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42859         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42860         
42861         
42862         this.el.dom.style.border = '0 none';
42863         this.el.dom.setAttribute('tabIndex', -1);
42864         this.el.addClass('x-hidden hide');
42865         
42866         
42867         
42868         if(Roo.isIE){ // fix IE 1px bogus margin
42869             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42870         }
42871        
42872         
42873         this.frameId = Roo.id();
42874         
42875          
42876         
42877         var iframe = this.owner.wrap.createChild({
42878             tag: 'iframe',
42879             cls: 'form-control', // bootstrap..
42880             id: this.frameId,
42881             name: this.frameId,
42882             frameBorder : 'no',
42883             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42884         }, this.el
42885         );
42886         
42887         
42888         this.iframe = iframe.dom;
42889
42890          this.assignDocWin();
42891         
42892         this.doc.designMode = 'on';
42893        
42894         this.doc.open();
42895         this.doc.write(this.getDocMarkup());
42896         this.doc.close();
42897
42898         
42899         var task = { // must defer to wait for browser to be ready
42900             run : function(){
42901                 //console.log("run task?" + this.doc.readyState);
42902                 this.assignDocWin();
42903                 if(this.doc.body || this.doc.readyState == 'complete'){
42904                     try {
42905                         this.doc.designMode="on";
42906                     } catch (e) {
42907                         return;
42908                     }
42909                     Roo.TaskMgr.stop(task);
42910                     this.initEditor.defer(10, this);
42911                 }
42912             },
42913             interval : 10,
42914             duration: 10000,
42915             scope: this
42916         };
42917         Roo.TaskMgr.start(task);
42918
42919     },
42920
42921     // private
42922     onResize : function(w, h)
42923     {
42924          Roo.log('resize: ' +w + ',' + h );
42925         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42926         if(!this.iframe){
42927             return;
42928         }
42929         if(typeof w == 'number'){
42930             
42931             this.iframe.style.width = w + 'px';
42932         }
42933         if(typeof h == 'number'){
42934             
42935             this.iframe.style.height = h + 'px';
42936             if(this.doc){
42937                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42938             }
42939         }
42940         
42941     },
42942
42943     /**
42944      * Toggles the editor between standard and source edit mode.
42945      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42946      */
42947     toggleSourceEdit : function(sourceEditMode){
42948         
42949         this.sourceEditMode = sourceEditMode === true;
42950         
42951         if(this.sourceEditMode){
42952  
42953             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42954             
42955         }else{
42956             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42957             //this.iframe.className = '';
42958             this.deferFocus();
42959         }
42960         //this.setSize(this.owner.wrap.getSize());
42961         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42962     },
42963
42964     
42965   
42966
42967     /**
42968      * Protected method that will not generally be called directly. If you need/want
42969      * custom HTML cleanup, this is the method you should override.
42970      * @param {String} html The HTML to be cleaned
42971      * return {String} The cleaned HTML
42972      */
42973     cleanHtml : function(html){
42974         html = String(html);
42975         if(html.length > 5){
42976             if(Roo.isSafari){ // strip safari nonsense
42977                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42978             }
42979         }
42980         if(html == '&nbsp;'){
42981             html = '';
42982         }
42983         return html;
42984     },
42985
42986     /**
42987      * HTML Editor -> Textarea
42988      * Protected method that will not generally be called directly. Syncs the contents
42989      * of the editor iframe with the textarea.
42990      */
42991     syncValue : function(){
42992         if(this.initialized){
42993             var bd = (this.doc.body || this.doc.documentElement);
42994             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42995             var html = bd.innerHTML;
42996             if(Roo.isSafari){
42997                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42998                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42999                 if(m && m[1]){
43000                     html = '<div style="'+m[0]+'">' + html + '</div>';
43001                 }
43002             }
43003             html = this.cleanHtml(html);
43004             // fix up the special chars.. normaly like back quotes in word...
43005             // however we do not want to do this with chinese..
43006             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43007                 var cc = b.charCodeAt();
43008                 if (
43009                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43010                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43011                     (cc >= 0xf900 && cc < 0xfb00 )
43012                 ) {
43013                         return b;
43014                 }
43015                 return "&#"+cc+";" 
43016             });
43017             if(this.owner.fireEvent('beforesync', this, html) !== false){
43018                 this.el.dom.value = html;
43019                 this.owner.fireEvent('sync', this, html);
43020             }
43021         }
43022     },
43023
43024     /**
43025      * Protected method that will not generally be called directly. Pushes the value of the textarea
43026      * into the iframe editor.
43027      */
43028     pushValue : function(){
43029         if(this.initialized){
43030             var v = this.el.dom.value.trim();
43031             
43032 //            if(v.length < 1){
43033 //                v = '&#160;';
43034 //            }
43035             
43036             if(this.owner.fireEvent('beforepush', this, v) !== false){
43037                 var d = (this.doc.body || this.doc.documentElement);
43038                 d.innerHTML = v;
43039                 this.cleanUpPaste();
43040                 this.el.dom.value = d.innerHTML;
43041                 this.owner.fireEvent('push', this, v);
43042             }
43043         }
43044     },
43045
43046     // private
43047     deferFocus : function(){
43048         this.focus.defer(10, this);
43049     },
43050
43051     // doc'ed in Field
43052     focus : function(){
43053         if(this.win && !this.sourceEditMode){
43054             this.win.focus();
43055         }else{
43056             this.el.focus();
43057         }
43058     },
43059     
43060     assignDocWin: function()
43061     {
43062         var iframe = this.iframe;
43063         
43064          if(Roo.isIE){
43065             this.doc = iframe.contentWindow.document;
43066             this.win = iframe.contentWindow;
43067         } else {
43068 //            if (!Roo.get(this.frameId)) {
43069 //                return;
43070 //            }
43071 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43072 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43073             
43074             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43075                 return;
43076             }
43077             
43078             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43079             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43080         }
43081     },
43082     
43083     // private
43084     initEditor : function(){
43085         //console.log("INIT EDITOR");
43086         this.assignDocWin();
43087         
43088         
43089         
43090         this.doc.designMode="on";
43091         this.doc.open();
43092         this.doc.write(this.getDocMarkup());
43093         this.doc.close();
43094         
43095         var dbody = (this.doc.body || this.doc.documentElement);
43096         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43097         // this copies styles from the containing element into thsi one..
43098         // not sure why we need all of this..
43099         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43100         
43101         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43102         //ss['background-attachment'] = 'fixed'; // w3c
43103         dbody.bgProperties = 'fixed'; // ie
43104         //Roo.DomHelper.applyStyles(dbody, ss);
43105         Roo.EventManager.on(this.doc, {
43106             //'mousedown': this.onEditorEvent,
43107             'mouseup': this.onEditorEvent,
43108             'dblclick': this.onEditorEvent,
43109             'click': this.onEditorEvent,
43110             'keyup': this.onEditorEvent,
43111             buffer:100,
43112             scope: this
43113         });
43114         if(Roo.isGecko){
43115             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43116         }
43117         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43118             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43119         }
43120         this.initialized = true;
43121
43122         this.owner.fireEvent('initialize', this);
43123         this.pushValue();
43124     },
43125
43126     // private
43127     onDestroy : function(){
43128         
43129         
43130         
43131         if(this.rendered){
43132             
43133             //for (var i =0; i < this.toolbars.length;i++) {
43134             //    // fixme - ask toolbars for heights?
43135             //    this.toolbars[i].onDestroy();
43136            // }
43137             
43138             //this.wrap.dom.innerHTML = '';
43139             //this.wrap.remove();
43140         }
43141     },
43142
43143     // private
43144     onFirstFocus : function(){
43145         
43146         this.assignDocWin();
43147         
43148         
43149         this.activated = true;
43150          
43151     
43152         if(Roo.isGecko){ // prevent silly gecko errors
43153             this.win.focus();
43154             var s = this.win.getSelection();
43155             if(!s.focusNode || s.focusNode.nodeType != 3){
43156                 var r = s.getRangeAt(0);
43157                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43158                 r.collapse(true);
43159                 this.deferFocus();
43160             }
43161             try{
43162                 this.execCmd('useCSS', true);
43163                 this.execCmd('styleWithCSS', false);
43164             }catch(e){}
43165         }
43166         this.owner.fireEvent('activate', this);
43167     },
43168
43169     // private
43170     adjustFont: function(btn){
43171         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43172         //if(Roo.isSafari){ // safari
43173         //    adjust *= 2;
43174        // }
43175         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43176         if(Roo.isSafari){ // safari
43177             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43178             v =  (v < 10) ? 10 : v;
43179             v =  (v > 48) ? 48 : v;
43180             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43181             
43182         }
43183         
43184         
43185         v = Math.max(1, v+adjust);
43186         
43187         this.execCmd('FontSize', v  );
43188     },
43189
43190     onEditorEvent : function(e)
43191     {
43192         this.owner.fireEvent('editorevent', this, e);
43193       //  this.updateToolbar();
43194         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43195     },
43196
43197     insertTag : function(tg)
43198     {
43199         // could be a bit smarter... -> wrap the current selected tRoo..
43200         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43201             
43202             range = this.createRange(this.getSelection());
43203             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43204             wrappingNode.appendChild(range.extractContents());
43205             range.insertNode(wrappingNode);
43206
43207             return;
43208             
43209             
43210             
43211         }
43212         this.execCmd("formatblock",   tg);
43213         
43214     },
43215     
43216     insertText : function(txt)
43217     {
43218         
43219         
43220         var range = this.createRange();
43221         range.deleteContents();
43222                //alert(Sender.getAttribute('label'));
43223                
43224         range.insertNode(this.doc.createTextNode(txt));
43225     } ,
43226     
43227      
43228
43229     /**
43230      * Executes a Midas editor command on the editor document and performs necessary focus and
43231      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43232      * @param {String} cmd The Midas command
43233      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43234      */
43235     relayCmd : function(cmd, value){
43236         this.win.focus();
43237         this.execCmd(cmd, value);
43238         this.owner.fireEvent('editorevent', this);
43239         //this.updateToolbar();
43240         this.owner.deferFocus();
43241     },
43242
43243     /**
43244      * Executes a Midas editor command directly on the editor document.
43245      * For visual commands, you should use {@link #relayCmd} instead.
43246      * <b>This should only be called after the editor is initialized.</b>
43247      * @param {String} cmd The Midas command
43248      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43249      */
43250     execCmd : function(cmd, value){
43251         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43252         this.syncValue();
43253     },
43254  
43255  
43256    
43257     /**
43258      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43259      * to insert tRoo.
43260      * @param {String} text | dom node.. 
43261      */
43262     insertAtCursor : function(text)
43263     {
43264         
43265         if(!this.activated){
43266             return;
43267         }
43268         /*
43269         if(Roo.isIE){
43270             this.win.focus();
43271             var r = this.doc.selection.createRange();
43272             if(r){
43273                 r.collapse(true);
43274                 r.pasteHTML(text);
43275                 this.syncValue();
43276                 this.deferFocus();
43277             
43278             }
43279             return;
43280         }
43281         */
43282         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43283             this.win.focus();
43284             
43285             
43286             // from jquery ui (MIT licenced)
43287             var range, node;
43288             var win = this.win;
43289             
43290             if (win.getSelection && win.getSelection().getRangeAt) {
43291                 range = win.getSelection().getRangeAt(0);
43292                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43293                 range.insertNode(node);
43294             } else if (win.document.selection && win.document.selection.createRange) {
43295                 // no firefox support
43296                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43297                 win.document.selection.createRange().pasteHTML(txt);
43298             } else {
43299                 // no firefox support
43300                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43301                 this.execCmd('InsertHTML', txt);
43302             } 
43303             
43304             this.syncValue();
43305             
43306             this.deferFocus();
43307         }
43308     },
43309  // private
43310     mozKeyPress : function(e){
43311         if(e.ctrlKey){
43312             var c = e.getCharCode(), cmd;
43313           
43314             if(c > 0){
43315                 c = String.fromCharCode(c).toLowerCase();
43316                 switch(c){
43317                     case 'b':
43318                         cmd = 'bold';
43319                         break;
43320                     case 'i':
43321                         cmd = 'italic';
43322                         break;
43323                     
43324                     case 'u':
43325                         cmd = 'underline';
43326                         break;
43327                     
43328                     case 'v':
43329                         this.cleanUpPaste.defer(100, this);
43330                         return;
43331                         
43332                 }
43333                 if(cmd){
43334                     this.win.focus();
43335                     this.execCmd(cmd);
43336                     this.deferFocus();
43337                     e.preventDefault();
43338                 }
43339                 
43340             }
43341         }
43342     },
43343
43344     // private
43345     fixKeys : function(){ // load time branching for fastest keydown performance
43346         if(Roo.isIE){
43347             return function(e){
43348                 var k = e.getKey(), r;
43349                 if(k == e.TAB){
43350                     e.stopEvent();
43351                     r = this.doc.selection.createRange();
43352                     if(r){
43353                         r.collapse(true);
43354                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43355                         this.deferFocus();
43356                     }
43357                     return;
43358                 }
43359                 
43360                 if(k == e.ENTER){
43361                     r = this.doc.selection.createRange();
43362                     if(r){
43363                         var target = r.parentElement();
43364                         if(!target || target.tagName.toLowerCase() != 'li'){
43365                             e.stopEvent();
43366                             r.pasteHTML('<br />');
43367                             r.collapse(false);
43368                             r.select();
43369                         }
43370                     }
43371                 }
43372                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43373                     this.cleanUpPaste.defer(100, this);
43374                     return;
43375                 }
43376                 
43377                 
43378             };
43379         }else if(Roo.isOpera){
43380             return function(e){
43381                 var k = e.getKey();
43382                 if(k == e.TAB){
43383                     e.stopEvent();
43384                     this.win.focus();
43385                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43386                     this.deferFocus();
43387                 }
43388                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43389                     this.cleanUpPaste.defer(100, this);
43390                     return;
43391                 }
43392                 
43393             };
43394         }else if(Roo.isSafari){
43395             return function(e){
43396                 var k = e.getKey();
43397                 
43398                 if(k == e.TAB){
43399                     e.stopEvent();
43400                     this.execCmd('InsertText','\t');
43401                     this.deferFocus();
43402                     return;
43403                 }
43404                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43405                     this.cleanUpPaste.defer(100, this);
43406                     return;
43407                 }
43408                 
43409              };
43410         }
43411     }(),
43412     
43413     getAllAncestors: function()
43414     {
43415         var p = this.getSelectedNode();
43416         var a = [];
43417         if (!p) {
43418             a.push(p); // push blank onto stack..
43419             p = this.getParentElement();
43420         }
43421         
43422         
43423         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43424             a.push(p);
43425             p = p.parentNode;
43426         }
43427         a.push(this.doc.body);
43428         return a;
43429     },
43430     lastSel : false,
43431     lastSelNode : false,
43432     
43433     
43434     getSelection : function() 
43435     {
43436         this.assignDocWin();
43437         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43438     },
43439     
43440     getSelectedNode: function() 
43441     {
43442         // this may only work on Gecko!!!
43443         
43444         // should we cache this!!!!
43445         
43446         
43447         
43448          
43449         var range = this.createRange(this.getSelection()).cloneRange();
43450         
43451         if (Roo.isIE) {
43452             var parent = range.parentElement();
43453             while (true) {
43454                 var testRange = range.duplicate();
43455                 testRange.moveToElementText(parent);
43456                 if (testRange.inRange(range)) {
43457                     break;
43458                 }
43459                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43460                     break;
43461                 }
43462                 parent = parent.parentElement;
43463             }
43464             return parent;
43465         }
43466         
43467         // is ancestor a text element.
43468         var ac =  range.commonAncestorContainer;
43469         if (ac.nodeType == 3) {
43470             ac = ac.parentNode;
43471         }
43472         
43473         var ar = ac.childNodes;
43474          
43475         var nodes = [];
43476         var other_nodes = [];
43477         var has_other_nodes = false;
43478         for (var i=0;i<ar.length;i++) {
43479             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43480                 continue;
43481             }
43482             // fullly contained node.
43483             
43484             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43485                 nodes.push(ar[i]);
43486                 continue;
43487             }
43488             
43489             // probably selected..
43490             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43491                 other_nodes.push(ar[i]);
43492                 continue;
43493             }
43494             // outer..
43495             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43496                 continue;
43497             }
43498             
43499             
43500             has_other_nodes = true;
43501         }
43502         if (!nodes.length && other_nodes.length) {
43503             nodes= other_nodes;
43504         }
43505         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43506             return false;
43507         }
43508         
43509         return nodes[0];
43510     },
43511     createRange: function(sel)
43512     {
43513         // this has strange effects when using with 
43514         // top toolbar - not sure if it's a great idea.
43515         //this.editor.contentWindow.focus();
43516         if (typeof sel != "undefined") {
43517             try {
43518                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43519             } catch(e) {
43520                 return this.doc.createRange();
43521             }
43522         } else {
43523             return this.doc.createRange();
43524         }
43525     },
43526     getParentElement: function()
43527     {
43528         
43529         this.assignDocWin();
43530         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43531         
43532         var range = this.createRange(sel);
43533          
43534         try {
43535             var p = range.commonAncestorContainer;
43536             while (p.nodeType == 3) { // text node
43537                 p = p.parentNode;
43538             }
43539             return p;
43540         } catch (e) {
43541             return null;
43542         }
43543     
43544     },
43545     /***
43546      *
43547      * Range intersection.. the hard stuff...
43548      *  '-1' = before
43549      *  '0' = hits..
43550      *  '1' = after.
43551      *         [ -- selected range --- ]
43552      *   [fail]                        [fail]
43553      *
43554      *    basically..
43555      *      if end is before start or  hits it. fail.
43556      *      if start is after end or hits it fail.
43557      *
43558      *   if either hits (but other is outside. - then it's not 
43559      *   
43560      *    
43561      **/
43562     
43563     
43564     // @see http://www.thismuchiknow.co.uk/?p=64.
43565     rangeIntersectsNode : function(range, node)
43566     {
43567         var nodeRange = node.ownerDocument.createRange();
43568         try {
43569             nodeRange.selectNode(node);
43570         } catch (e) {
43571             nodeRange.selectNodeContents(node);
43572         }
43573     
43574         var rangeStartRange = range.cloneRange();
43575         rangeStartRange.collapse(true);
43576     
43577         var rangeEndRange = range.cloneRange();
43578         rangeEndRange.collapse(false);
43579     
43580         var nodeStartRange = nodeRange.cloneRange();
43581         nodeStartRange.collapse(true);
43582     
43583         var nodeEndRange = nodeRange.cloneRange();
43584         nodeEndRange.collapse(false);
43585     
43586         return rangeStartRange.compareBoundaryPoints(
43587                  Range.START_TO_START, nodeEndRange) == -1 &&
43588                rangeEndRange.compareBoundaryPoints(
43589                  Range.START_TO_START, nodeStartRange) == 1;
43590         
43591          
43592     },
43593     rangeCompareNode : function(range, node)
43594     {
43595         var nodeRange = node.ownerDocument.createRange();
43596         try {
43597             nodeRange.selectNode(node);
43598         } catch (e) {
43599             nodeRange.selectNodeContents(node);
43600         }
43601         
43602         
43603         range.collapse(true);
43604     
43605         nodeRange.collapse(true);
43606      
43607         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43608         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43609          
43610         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43611         
43612         var nodeIsBefore   =  ss == 1;
43613         var nodeIsAfter    = ee == -1;
43614         
43615         if (nodeIsBefore && nodeIsAfter) {
43616             return 0; // outer
43617         }
43618         if (!nodeIsBefore && nodeIsAfter) {
43619             return 1; //right trailed.
43620         }
43621         
43622         if (nodeIsBefore && !nodeIsAfter) {
43623             return 2;  // left trailed.
43624         }
43625         // fully contined.
43626         return 3;
43627     },
43628
43629     // private? - in a new class?
43630     cleanUpPaste :  function()
43631     {
43632         // cleans up the whole document..
43633         Roo.log('cleanuppaste');
43634         
43635         this.cleanUpChildren(this.doc.body);
43636         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43637         if (clean != this.doc.body.innerHTML) {
43638             this.doc.body.innerHTML = clean;
43639         }
43640         
43641     },
43642     
43643     cleanWordChars : function(input) {// change the chars to hex code
43644         var he = Roo.HtmlEditorCore;
43645         
43646         var output = input;
43647         Roo.each(he.swapCodes, function(sw) { 
43648             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43649             
43650             output = output.replace(swapper, sw[1]);
43651         });
43652         
43653         return output;
43654     },
43655     
43656     
43657     cleanUpChildren : function (n)
43658     {
43659         if (!n.childNodes.length) {
43660             return;
43661         }
43662         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43663            this.cleanUpChild(n.childNodes[i]);
43664         }
43665     },
43666     
43667     
43668         
43669     
43670     cleanUpChild : function (node)
43671     {
43672         var ed = this;
43673         //console.log(node);
43674         if (node.nodeName == "#text") {
43675             // clean up silly Windows -- stuff?
43676             return; 
43677         }
43678         if (node.nodeName == "#comment") {
43679             node.parentNode.removeChild(node);
43680             // clean up silly Windows -- stuff?
43681             return; 
43682         }
43683         var lcname = node.tagName.toLowerCase();
43684         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43685         // whitelist of tags..
43686         
43687         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43688             // remove node.
43689             node.parentNode.removeChild(node);
43690             return;
43691             
43692         }
43693         
43694         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43695         
43696         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43697         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43698         
43699         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43700         //    remove_keep_children = true;
43701         //}
43702         
43703         if (remove_keep_children) {
43704             this.cleanUpChildren(node);
43705             // inserts everything just before this node...
43706             while (node.childNodes.length) {
43707                 var cn = node.childNodes[0];
43708                 node.removeChild(cn);
43709                 node.parentNode.insertBefore(cn, node);
43710             }
43711             node.parentNode.removeChild(node);
43712             return;
43713         }
43714         
43715         if (!node.attributes || !node.attributes.length) {
43716             this.cleanUpChildren(node);
43717             return;
43718         }
43719         
43720         function cleanAttr(n,v)
43721         {
43722             
43723             if (v.match(/^\./) || v.match(/^\//)) {
43724                 return;
43725             }
43726             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43727                 return;
43728             }
43729             if (v.match(/^#/)) {
43730                 return;
43731             }
43732 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43733             node.removeAttribute(n);
43734             
43735         }
43736         
43737         var cwhite = this.cwhite;
43738         var cblack = this.cblack;
43739             
43740         function cleanStyle(n,v)
43741         {
43742             if (v.match(/expression/)) { //XSS?? should we even bother..
43743                 node.removeAttribute(n);
43744                 return;
43745             }
43746             
43747             var parts = v.split(/;/);
43748             var clean = [];
43749             
43750             Roo.each(parts, function(p) {
43751                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43752                 if (!p.length) {
43753                     return true;
43754                 }
43755                 var l = p.split(':').shift().replace(/\s+/g,'');
43756                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43757                 
43758                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43759 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43760                     //node.removeAttribute(n);
43761                     return true;
43762                 }
43763                 //Roo.log()
43764                 // only allow 'c whitelisted system attributes'
43765                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43766 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43767                     //node.removeAttribute(n);
43768                     return true;
43769                 }
43770                 
43771                 
43772                  
43773                 
43774                 clean.push(p);
43775                 return true;
43776             });
43777             if (clean.length) { 
43778                 node.setAttribute(n, clean.join(';'));
43779             } else {
43780                 node.removeAttribute(n);
43781             }
43782             
43783         }
43784         
43785         
43786         for (var i = node.attributes.length-1; i > -1 ; i--) {
43787             var a = node.attributes[i];
43788             //console.log(a);
43789             
43790             if (a.name.toLowerCase().substr(0,2)=='on')  {
43791                 node.removeAttribute(a.name);
43792                 continue;
43793             }
43794             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43795                 node.removeAttribute(a.name);
43796                 continue;
43797             }
43798             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43799                 cleanAttr(a.name,a.value); // fixme..
43800                 continue;
43801             }
43802             if (a.name == 'style') {
43803                 cleanStyle(a.name,a.value);
43804                 continue;
43805             }
43806             /// clean up MS crap..
43807             // tecnically this should be a list of valid class'es..
43808             
43809             
43810             if (a.name == 'class') {
43811                 if (a.value.match(/^Mso/)) {
43812                     node.className = '';
43813                 }
43814                 
43815                 if (a.value.match(/^body$/)) {
43816                     node.className = '';
43817                 }
43818                 continue;
43819             }
43820             
43821             // style cleanup!?
43822             // class cleanup?
43823             
43824         }
43825         
43826         
43827         this.cleanUpChildren(node);
43828         
43829         
43830     },
43831     
43832     /**
43833      * Clean up MS wordisms...
43834      */
43835     cleanWord : function(node)
43836     {
43837         
43838         
43839         if (!node) {
43840             this.cleanWord(this.doc.body);
43841             return;
43842         }
43843         if (node.nodeName == "#text") {
43844             // clean up silly Windows -- stuff?
43845             return; 
43846         }
43847         if (node.nodeName == "#comment") {
43848             node.parentNode.removeChild(node);
43849             // clean up silly Windows -- stuff?
43850             return; 
43851         }
43852         
43853         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43854             node.parentNode.removeChild(node);
43855             return;
43856         }
43857         
43858         // remove - but keep children..
43859         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43860             while (node.childNodes.length) {
43861                 var cn = node.childNodes[0];
43862                 node.removeChild(cn);
43863                 node.parentNode.insertBefore(cn, node);
43864             }
43865             node.parentNode.removeChild(node);
43866             this.iterateChildren(node, this.cleanWord);
43867             return;
43868         }
43869         // clean styles
43870         if (node.className.length) {
43871             
43872             var cn = node.className.split(/\W+/);
43873             var cna = [];
43874             Roo.each(cn, function(cls) {
43875                 if (cls.match(/Mso[a-zA-Z]+/)) {
43876                     return;
43877                 }
43878                 cna.push(cls);
43879             });
43880             node.className = cna.length ? cna.join(' ') : '';
43881             if (!cna.length) {
43882                 node.removeAttribute("class");
43883             }
43884         }
43885         
43886         if (node.hasAttribute("lang")) {
43887             node.removeAttribute("lang");
43888         }
43889         
43890         if (node.hasAttribute("style")) {
43891             
43892             var styles = node.getAttribute("style").split(";");
43893             var nstyle = [];
43894             Roo.each(styles, function(s) {
43895                 if (!s.match(/:/)) {
43896                     return;
43897                 }
43898                 var kv = s.split(":");
43899                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43900                     return;
43901                 }
43902                 // what ever is left... we allow.
43903                 nstyle.push(s);
43904             });
43905             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43906             if (!nstyle.length) {
43907                 node.removeAttribute('style');
43908             }
43909         }
43910         this.iterateChildren(node, this.cleanWord);
43911         
43912         
43913         
43914     },
43915     /**
43916      * iterateChildren of a Node, calling fn each time, using this as the scole..
43917      * @param {DomNode} node node to iterate children of.
43918      * @param {Function} fn method of this class to call on each item.
43919      */
43920     iterateChildren : function(node, fn)
43921     {
43922         if (!node.childNodes.length) {
43923                 return;
43924         }
43925         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43926            fn.call(this, node.childNodes[i])
43927         }
43928     },
43929     
43930     
43931     /**
43932      * cleanTableWidths.
43933      *
43934      * Quite often pasting from word etc.. results in tables with column and widths.
43935      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43936      *
43937      */
43938     cleanTableWidths : function(node)
43939     {
43940          
43941          
43942         if (!node) {
43943             this.cleanTableWidths(this.doc.body);
43944             return;
43945         }
43946         
43947         // ignore list...
43948         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43949             return; 
43950         }
43951         Roo.log(node.tagName);
43952         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43953             this.iterateChildren(node, this.cleanTableWidths);
43954             return;
43955         }
43956         if (node.hasAttribute('width')) {
43957             node.removeAttribute('width');
43958         }
43959         
43960          
43961         if (node.hasAttribute("style")) {
43962             // pretty basic...
43963             
43964             var styles = node.getAttribute("style").split(";");
43965             var nstyle = [];
43966             Roo.each(styles, function(s) {
43967                 if (!s.match(/:/)) {
43968                     return;
43969                 }
43970                 var kv = s.split(":");
43971                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43972                     return;
43973                 }
43974                 // what ever is left... we allow.
43975                 nstyle.push(s);
43976             });
43977             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43978             if (!nstyle.length) {
43979                 node.removeAttribute('style');
43980             }
43981         }
43982         
43983         this.iterateChildren(node, this.cleanTableWidths);
43984         
43985         
43986     },
43987     
43988     
43989     
43990     
43991     domToHTML : function(currentElement, depth, nopadtext) {
43992         
43993         depth = depth || 0;
43994         nopadtext = nopadtext || false;
43995     
43996         if (!currentElement) {
43997             return this.domToHTML(this.doc.body);
43998         }
43999         
44000         //Roo.log(currentElement);
44001         var j;
44002         var allText = false;
44003         var nodeName = currentElement.nodeName;
44004         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44005         
44006         if  (nodeName == '#text') {
44007             
44008             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44009         }
44010         
44011         
44012         var ret = '';
44013         if (nodeName != 'BODY') {
44014              
44015             var i = 0;
44016             // Prints the node tagName, such as <A>, <IMG>, etc
44017             if (tagName) {
44018                 var attr = [];
44019                 for(i = 0; i < currentElement.attributes.length;i++) {
44020                     // quoting?
44021                     var aname = currentElement.attributes.item(i).name;
44022                     if (!currentElement.attributes.item(i).value.length) {
44023                         continue;
44024                     }
44025                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44026                 }
44027                 
44028                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44029             } 
44030             else {
44031                 
44032                 // eack
44033             }
44034         } else {
44035             tagName = false;
44036         }
44037         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44038             return ret;
44039         }
44040         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44041             nopadtext = true;
44042         }
44043         
44044         
44045         // Traverse the tree
44046         i = 0;
44047         var currentElementChild = currentElement.childNodes.item(i);
44048         var allText = true;
44049         var innerHTML  = '';
44050         lastnode = '';
44051         while (currentElementChild) {
44052             // Formatting code (indent the tree so it looks nice on the screen)
44053             var nopad = nopadtext;
44054             if (lastnode == 'SPAN') {
44055                 nopad  = true;
44056             }
44057             // text
44058             if  (currentElementChild.nodeName == '#text') {
44059                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44060                 toadd = nopadtext ? toadd : toadd.trim();
44061                 if (!nopad && toadd.length > 80) {
44062                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44063                 }
44064                 innerHTML  += toadd;
44065                 
44066                 i++;
44067                 currentElementChild = currentElement.childNodes.item(i);
44068                 lastNode = '';
44069                 continue;
44070             }
44071             allText = false;
44072             
44073             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44074                 
44075             // Recursively traverse the tree structure of the child node
44076             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44077             lastnode = currentElementChild.nodeName;
44078             i++;
44079             currentElementChild=currentElement.childNodes.item(i);
44080         }
44081         
44082         ret += innerHTML;
44083         
44084         if (!allText) {
44085                 // The remaining code is mostly for formatting the tree
44086             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44087         }
44088         
44089         
44090         if (tagName) {
44091             ret+= "</"+tagName+">";
44092         }
44093         return ret;
44094         
44095     },
44096         
44097     applyBlacklists : function()
44098     {
44099         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44100         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44101         
44102         this.white = [];
44103         this.black = [];
44104         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44105             if (b.indexOf(tag) > -1) {
44106                 return;
44107             }
44108             this.white.push(tag);
44109             
44110         }, this);
44111         
44112         Roo.each(w, function(tag) {
44113             if (b.indexOf(tag) > -1) {
44114                 return;
44115             }
44116             if (this.white.indexOf(tag) > -1) {
44117                 return;
44118             }
44119             this.white.push(tag);
44120             
44121         }, this);
44122         
44123         
44124         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44125             if (w.indexOf(tag) > -1) {
44126                 return;
44127             }
44128             this.black.push(tag);
44129             
44130         }, this);
44131         
44132         Roo.each(b, function(tag) {
44133             if (w.indexOf(tag) > -1) {
44134                 return;
44135             }
44136             if (this.black.indexOf(tag) > -1) {
44137                 return;
44138             }
44139             this.black.push(tag);
44140             
44141         }, this);
44142         
44143         
44144         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44145         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44146         
44147         this.cwhite = [];
44148         this.cblack = [];
44149         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44150             if (b.indexOf(tag) > -1) {
44151                 return;
44152             }
44153             this.cwhite.push(tag);
44154             
44155         }, this);
44156         
44157         Roo.each(w, function(tag) {
44158             if (b.indexOf(tag) > -1) {
44159                 return;
44160             }
44161             if (this.cwhite.indexOf(tag) > -1) {
44162                 return;
44163             }
44164             this.cwhite.push(tag);
44165             
44166         }, this);
44167         
44168         
44169         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44170             if (w.indexOf(tag) > -1) {
44171                 return;
44172             }
44173             this.cblack.push(tag);
44174             
44175         }, this);
44176         
44177         Roo.each(b, function(tag) {
44178             if (w.indexOf(tag) > -1) {
44179                 return;
44180             }
44181             if (this.cblack.indexOf(tag) > -1) {
44182                 return;
44183             }
44184             this.cblack.push(tag);
44185             
44186         }, this);
44187     },
44188     
44189     setStylesheets : function(stylesheets)
44190     {
44191         if(typeof(stylesheets) == 'string'){
44192             Roo.get(this.iframe.contentDocument.head).createChild({
44193                 tag : 'link',
44194                 rel : 'stylesheet',
44195                 type : 'text/css',
44196                 href : stylesheets
44197             });
44198             
44199             return;
44200         }
44201         var _this = this;
44202      
44203         Roo.each(stylesheets, function(s) {
44204             if(!s.length){
44205                 return;
44206             }
44207             
44208             Roo.get(_this.iframe.contentDocument.head).createChild({
44209                 tag : 'link',
44210                 rel : 'stylesheet',
44211                 type : 'text/css',
44212                 href : s
44213             });
44214         });
44215
44216         
44217     },
44218     
44219     removeStylesheets : function()
44220     {
44221         var _this = this;
44222         
44223         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44224             s.remove();
44225         });
44226     }
44227     
44228     // hide stuff that is not compatible
44229     /**
44230      * @event blur
44231      * @hide
44232      */
44233     /**
44234      * @event change
44235      * @hide
44236      */
44237     /**
44238      * @event focus
44239      * @hide
44240      */
44241     /**
44242      * @event specialkey
44243      * @hide
44244      */
44245     /**
44246      * @cfg {String} fieldClass @hide
44247      */
44248     /**
44249      * @cfg {String} focusClass @hide
44250      */
44251     /**
44252      * @cfg {String} autoCreate @hide
44253      */
44254     /**
44255      * @cfg {String} inputType @hide
44256      */
44257     /**
44258      * @cfg {String} invalidClass @hide
44259      */
44260     /**
44261      * @cfg {String} invalidText @hide
44262      */
44263     /**
44264      * @cfg {String} msgFx @hide
44265      */
44266     /**
44267      * @cfg {String} validateOnBlur @hide
44268      */
44269 });
44270
44271 Roo.HtmlEditorCore.white = [
44272         'area', 'br', 'img', 'input', 'hr', 'wbr',
44273         
44274        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44275        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44276        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44277        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44278        'table',   'ul',         'xmp', 
44279        
44280        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44281       'thead',   'tr', 
44282      
44283       'dir', 'menu', 'ol', 'ul', 'dl',
44284        
44285       'embed',  'object'
44286 ];
44287
44288
44289 Roo.HtmlEditorCore.black = [
44290     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44291         'applet', // 
44292         'base',   'basefont', 'bgsound', 'blink',  'body', 
44293         'frame',  'frameset', 'head',    'html',   'ilayer', 
44294         'iframe', 'layer',  'link',     'meta',    'object',   
44295         'script', 'style' ,'title',  'xml' // clean later..
44296 ];
44297 Roo.HtmlEditorCore.clean = [
44298     'script', 'style', 'title', 'xml'
44299 ];
44300 Roo.HtmlEditorCore.remove = [
44301     'font'
44302 ];
44303 // attributes..
44304
44305 Roo.HtmlEditorCore.ablack = [
44306     'on'
44307 ];
44308     
44309 Roo.HtmlEditorCore.aclean = [ 
44310     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44311 ];
44312
44313 // protocols..
44314 Roo.HtmlEditorCore.pwhite= [
44315         'http',  'https',  'mailto'
44316 ];
44317
44318 // white listed style attributes.
44319 Roo.HtmlEditorCore.cwhite= [
44320       //  'text-align', /// default is to allow most things..
44321       
44322          
44323 //        'font-size'//??
44324 ];
44325
44326 // black listed style attributes.
44327 Roo.HtmlEditorCore.cblack= [
44328       //  'font-size' -- this can be set by the project 
44329 ];
44330
44331
44332 Roo.HtmlEditorCore.swapCodes   =[ 
44333     [    8211, "--" ], 
44334     [    8212, "--" ], 
44335     [    8216,  "'" ],  
44336     [    8217, "'" ],  
44337     [    8220, '"' ],  
44338     [    8221, '"' ],  
44339     [    8226, "*" ],  
44340     [    8230, "..." ]
44341 ]; 
44342
44343     //<script type="text/javascript">
44344
44345 /*
44346  * Ext JS Library 1.1.1
44347  * Copyright(c) 2006-2007, Ext JS, LLC.
44348  * Licence LGPL
44349  * 
44350  */
44351  
44352  
44353 Roo.form.HtmlEditor = function(config){
44354     
44355     
44356     
44357     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44358     
44359     if (!this.toolbars) {
44360         this.toolbars = [];
44361     }
44362     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44363     
44364     
44365 };
44366
44367 /**
44368  * @class Roo.form.HtmlEditor
44369  * @extends Roo.form.Field
44370  * Provides a lightweight HTML Editor component.
44371  *
44372  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44373  * 
44374  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44375  * supported by this editor.</b><br/><br/>
44376  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44377  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44378  */
44379 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44380     /**
44381      * @cfg {Boolean} clearUp
44382      */
44383     clearUp : true,
44384       /**
44385      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44386      */
44387     toolbars : false,
44388    
44389      /**
44390      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44391      *                        Roo.resizable.
44392      */
44393     resizable : false,
44394      /**
44395      * @cfg {Number} height (in pixels)
44396      */   
44397     height: 300,
44398    /**
44399      * @cfg {Number} width (in pixels)
44400      */   
44401     width: 500,
44402     
44403     /**
44404      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44405      * 
44406      */
44407     stylesheets: false,
44408     
44409     
44410      /**
44411      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44412      * 
44413      */
44414     cblack: false,
44415     /**
44416      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44417      * 
44418      */
44419     cwhite: false,
44420     
44421      /**
44422      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44423      * 
44424      */
44425     black: false,
44426     /**
44427      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44428      * 
44429      */
44430     white: false,
44431     
44432     // id of frame..
44433     frameId: false,
44434     
44435     // private properties
44436     validationEvent : false,
44437     deferHeight: true,
44438     initialized : false,
44439     activated : false,
44440     
44441     onFocus : Roo.emptyFn,
44442     iframePad:3,
44443     hideMode:'offsets',
44444     
44445     actionMode : 'container', // defaults to hiding it...
44446     
44447     defaultAutoCreate : { // modified by initCompnoent..
44448         tag: "textarea",
44449         style:"width:500px;height:300px;",
44450         autocomplete: "new-password"
44451     },
44452
44453     // private
44454     initComponent : function(){
44455         this.addEvents({
44456             /**
44457              * @event initialize
44458              * Fires when the editor is fully initialized (including the iframe)
44459              * @param {HtmlEditor} this
44460              */
44461             initialize: true,
44462             /**
44463              * @event activate
44464              * Fires when the editor is first receives the focus. Any insertion must wait
44465              * until after this event.
44466              * @param {HtmlEditor} this
44467              */
44468             activate: true,
44469              /**
44470              * @event beforesync
44471              * Fires before the textarea is updated with content from the editor iframe. Return false
44472              * to cancel the sync.
44473              * @param {HtmlEditor} this
44474              * @param {String} html
44475              */
44476             beforesync: true,
44477              /**
44478              * @event beforepush
44479              * Fires before the iframe editor is updated with content from the textarea. Return false
44480              * to cancel the push.
44481              * @param {HtmlEditor} this
44482              * @param {String} html
44483              */
44484             beforepush: true,
44485              /**
44486              * @event sync
44487              * Fires when the textarea is updated with content from the editor iframe.
44488              * @param {HtmlEditor} this
44489              * @param {String} html
44490              */
44491             sync: true,
44492              /**
44493              * @event push
44494              * Fires when the iframe editor is updated with content from the textarea.
44495              * @param {HtmlEditor} this
44496              * @param {String} html
44497              */
44498             push: true,
44499              /**
44500              * @event editmodechange
44501              * Fires when the editor switches edit modes
44502              * @param {HtmlEditor} this
44503              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44504              */
44505             editmodechange: true,
44506             /**
44507              * @event editorevent
44508              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44509              * @param {HtmlEditor} this
44510              */
44511             editorevent: true,
44512             /**
44513              * @event firstfocus
44514              * Fires when on first focus - needed by toolbars..
44515              * @param {HtmlEditor} this
44516              */
44517             firstfocus: true,
44518             /**
44519              * @event autosave
44520              * Auto save the htmlEditor value as a file into Events
44521              * @param {HtmlEditor} this
44522              */
44523             autosave: true,
44524             /**
44525              * @event savedpreview
44526              * preview the saved version of htmlEditor
44527              * @param {HtmlEditor} this
44528              */
44529             savedpreview: true,
44530             
44531             /**
44532             * @event stylesheetsclick
44533             * Fires when press the Sytlesheets button
44534             * @param {Roo.HtmlEditorCore} this
44535             */
44536             stylesheetsclick: true
44537         });
44538         this.defaultAutoCreate =  {
44539             tag: "textarea",
44540             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44541             autocomplete: "new-password"
44542         };
44543     },
44544
44545     /**
44546      * Protected method that will not generally be called directly. It
44547      * is called when the editor creates its toolbar. Override this method if you need to
44548      * add custom toolbar buttons.
44549      * @param {HtmlEditor} editor
44550      */
44551     createToolbar : function(editor){
44552         Roo.log("create toolbars");
44553         if (!editor.toolbars || !editor.toolbars.length) {
44554             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44555         }
44556         
44557         for (var i =0 ; i < editor.toolbars.length;i++) {
44558             editor.toolbars[i] = Roo.factory(
44559                     typeof(editor.toolbars[i]) == 'string' ?
44560                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44561                 Roo.form.HtmlEditor);
44562             editor.toolbars[i].init(editor);
44563         }
44564          
44565         
44566     },
44567
44568      
44569     // private
44570     onRender : function(ct, position)
44571     {
44572         var _t = this;
44573         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44574         
44575         this.wrap = this.el.wrap({
44576             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44577         });
44578         
44579         this.editorcore.onRender(ct, position);
44580          
44581         if (this.resizable) {
44582             this.resizeEl = new Roo.Resizable(this.wrap, {
44583                 pinned : true,
44584                 wrap: true,
44585                 dynamic : true,
44586                 minHeight : this.height,
44587                 height: this.height,
44588                 handles : this.resizable,
44589                 width: this.width,
44590                 listeners : {
44591                     resize : function(r, w, h) {
44592                         _t.onResize(w,h); // -something
44593                     }
44594                 }
44595             });
44596             
44597         }
44598         this.createToolbar(this);
44599        
44600         
44601         if(!this.width){
44602             this.setSize(this.wrap.getSize());
44603         }
44604         if (this.resizeEl) {
44605             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44606             // should trigger onReize..
44607         }
44608         
44609         this.keyNav = new Roo.KeyNav(this.el, {
44610             
44611             "tab" : function(e){
44612                 e.preventDefault();
44613                 
44614                 var value = this.getValue();
44615                 
44616                 var start = this.el.dom.selectionStart;
44617                 var end = this.el.dom.selectionEnd;
44618                 
44619                 if(!e.shiftKey){
44620                     
44621                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44622                     this.el.dom.setSelectionRange(end + 1, end + 1);
44623                     return;
44624                 }
44625                 
44626                 var f = value.substring(0, start).split("\t");
44627                 
44628                 if(f.pop().length != 0){
44629                     return;
44630                 }
44631                 
44632                 this.setValue(f.join("\t") + value.substring(end));
44633                 this.el.dom.setSelectionRange(start - 1, start - 1);
44634                 
44635             },
44636             
44637             "home" : function(e){
44638                 e.preventDefault();
44639                 
44640                 var curr = this.el.dom.selectionStart;
44641                 var lines = this.getValue().split("\n");
44642                 
44643                 if(!lines.length){
44644                     return;
44645                 }
44646                 
44647                 if(e.ctrlKey){
44648                     this.el.dom.setSelectionRange(0, 0);
44649                     return;
44650                 }
44651                 
44652                 var pos = 0;
44653                 
44654                 for (var i = 0; i < lines.length;i++) {
44655                     pos += lines[i].length;
44656                     
44657                     if(i != 0){
44658                         pos += 1;
44659                     }
44660                     
44661                     if(pos < curr){
44662                         continue;
44663                     }
44664                     
44665                     pos -= lines[i].length;
44666                     
44667                     break;
44668                 }
44669                 
44670                 if(!e.shiftKey){
44671                     this.el.dom.setSelectionRange(pos, pos);
44672                     return;
44673                 }
44674                 
44675                 this.el.dom.selectionStart = pos;
44676                 this.el.dom.selectionEnd = curr;
44677             },
44678             
44679             "end" : function(e){
44680                 e.preventDefault();
44681                 
44682                 var curr = this.el.dom.selectionStart;
44683                 var lines = this.getValue().split("\n");
44684                 
44685                 if(!lines.length){
44686                     return;
44687                 }
44688                 
44689                 if(e.ctrlKey){
44690                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44691                     return;
44692                 }
44693                 
44694                 var pos = 0;
44695                 
44696                 for (var i = 0; i < lines.length;i++) {
44697                     
44698                     pos += lines[i].length;
44699                     
44700                     if(i != 0){
44701                         pos += 1;
44702                     }
44703                     
44704                     if(pos < curr){
44705                         continue;
44706                     }
44707                     
44708                     break;
44709                 }
44710                 
44711                 if(!e.shiftKey){
44712                     this.el.dom.setSelectionRange(pos, pos);
44713                     return;
44714                 }
44715                 
44716                 this.el.dom.selectionStart = curr;
44717                 this.el.dom.selectionEnd = pos;
44718             },
44719
44720             scope : this,
44721
44722             doRelay : function(foo, bar, hname){
44723                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44724             },
44725
44726             forceKeyDown: true
44727         });
44728         
44729 //        if(this.autosave && this.w){
44730 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44731 //        }
44732     },
44733
44734     // private
44735     onResize : function(w, h)
44736     {
44737         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44738         var ew = false;
44739         var eh = false;
44740         
44741         if(this.el ){
44742             if(typeof w == 'number'){
44743                 var aw = w - this.wrap.getFrameWidth('lr');
44744                 this.el.setWidth(this.adjustWidth('textarea', aw));
44745                 ew = aw;
44746             }
44747             if(typeof h == 'number'){
44748                 var tbh = 0;
44749                 for (var i =0; i < this.toolbars.length;i++) {
44750                     // fixme - ask toolbars for heights?
44751                     tbh += this.toolbars[i].tb.el.getHeight();
44752                     if (this.toolbars[i].footer) {
44753                         tbh += this.toolbars[i].footer.el.getHeight();
44754                     }
44755                 }
44756                 
44757                 
44758                 
44759                 
44760                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44761                 ah -= 5; // knock a few pixes off for look..
44762 //                Roo.log(ah);
44763                 this.el.setHeight(this.adjustWidth('textarea', ah));
44764                 var eh = ah;
44765             }
44766         }
44767         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44768         this.editorcore.onResize(ew,eh);
44769         
44770     },
44771
44772     /**
44773      * Toggles the editor between standard and source edit mode.
44774      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44775      */
44776     toggleSourceEdit : function(sourceEditMode)
44777     {
44778         this.editorcore.toggleSourceEdit(sourceEditMode);
44779         
44780         if(this.editorcore.sourceEditMode){
44781             Roo.log('editor - showing textarea');
44782             
44783 //            Roo.log('in');
44784 //            Roo.log(this.syncValue());
44785             this.editorcore.syncValue();
44786             this.el.removeClass('x-hidden');
44787             this.el.dom.removeAttribute('tabIndex');
44788             this.el.focus();
44789             
44790             for (var i = 0; i < this.toolbars.length; i++) {
44791                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44792                     this.toolbars[i].tb.hide();
44793                     this.toolbars[i].footer.hide();
44794                 }
44795             }
44796             
44797         }else{
44798             Roo.log('editor - hiding textarea');
44799 //            Roo.log('out')
44800 //            Roo.log(this.pushValue()); 
44801             this.editorcore.pushValue();
44802             
44803             this.el.addClass('x-hidden');
44804             this.el.dom.setAttribute('tabIndex', -1);
44805             
44806             for (var i = 0; i < this.toolbars.length; i++) {
44807                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44808                     this.toolbars[i].tb.show();
44809                     this.toolbars[i].footer.show();
44810                 }
44811             }
44812             
44813             //this.deferFocus();
44814         }
44815         
44816         this.setSize(this.wrap.getSize());
44817         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44818         
44819         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44820     },
44821  
44822     // private (for BoxComponent)
44823     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44824
44825     // private (for BoxComponent)
44826     getResizeEl : function(){
44827         return this.wrap;
44828     },
44829
44830     // private (for BoxComponent)
44831     getPositionEl : function(){
44832         return this.wrap;
44833     },
44834
44835     // private
44836     initEvents : function(){
44837         this.originalValue = this.getValue();
44838     },
44839
44840     /**
44841      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44842      * @method
44843      */
44844     markInvalid : Roo.emptyFn,
44845     /**
44846      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44847      * @method
44848      */
44849     clearInvalid : Roo.emptyFn,
44850
44851     setValue : function(v){
44852         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44853         this.editorcore.pushValue();
44854     },
44855
44856      
44857     // private
44858     deferFocus : function(){
44859         this.focus.defer(10, this);
44860     },
44861
44862     // doc'ed in Field
44863     focus : function(){
44864         this.editorcore.focus();
44865         
44866     },
44867       
44868
44869     // private
44870     onDestroy : function(){
44871         
44872         
44873         
44874         if(this.rendered){
44875             
44876             for (var i =0; i < this.toolbars.length;i++) {
44877                 // fixme - ask toolbars for heights?
44878                 this.toolbars[i].onDestroy();
44879             }
44880             
44881             this.wrap.dom.innerHTML = '';
44882             this.wrap.remove();
44883         }
44884     },
44885
44886     // private
44887     onFirstFocus : function(){
44888         //Roo.log("onFirstFocus");
44889         this.editorcore.onFirstFocus();
44890          for (var i =0; i < this.toolbars.length;i++) {
44891             this.toolbars[i].onFirstFocus();
44892         }
44893         
44894     },
44895     
44896     // private
44897     syncValue : function()
44898     {
44899         this.editorcore.syncValue();
44900     },
44901     
44902     pushValue : function()
44903     {
44904         this.editorcore.pushValue();
44905     },
44906     
44907     setStylesheets : function(stylesheets)
44908     {
44909         this.editorcore.setStylesheets(stylesheets);
44910     },
44911     
44912     removeStylesheets : function()
44913     {
44914         this.editorcore.removeStylesheets();
44915     }
44916      
44917     
44918     // hide stuff that is not compatible
44919     /**
44920      * @event blur
44921      * @hide
44922      */
44923     /**
44924      * @event change
44925      * @hide
44926      */
44927     /**
44928      * @event focus
44929      * @hide
44930      */
44931     /**
44932      * @event specialkey
44933      * @hide
44934      */
44935     /**
44936      * @cfg {String} fieldClass @hide
44937      */
44938     /**
44939      * @cfg {String} focusClass @hide
44940      */
44941     /**
44942      * @cfg {String} autoCreate @hide
44943      */
44944     /**
44945      * @cfg {String} inputType @hide
44946      */
44947     /**
44948      * @cfg {String} invalidClass @hide
44949      */
44950     /**
44951      * @cfg {String} invalidText @hide
44952      */
44953     /**
44954      * @cfg {String} msgFx @hide
44955      */
44956     /**
44957      * @cfg {String} validateOnBlur @hide
44958      */
44959 });
44960  
44961     // <script type="text/javascript">
44962 /*
44963  * Based on
44964  * Ext JS Library 1.1.1
44965  * Copyright(c) 2006-2007, Ext JS, LLC.
44966  *  
44967  
44968  */
44969
44970 /**
44971  * @class Roo.form.HtmlEditorToolbar1
44972  * Basic Toolbar
44973  * 
44974  * Usage:
44975  *
44976  new Roo.form.HtmlEditor({
44977     ....
44978     toolbars : [
44979         new Roo.form.HtmlEditorToolbar1({
44980             disable : { fonts: 1 , format: 1, ..., ... , ...],
44981             btns : [ .... ]
44982         })
44983     }
44984      
44985  * 
44986  * @cfg {Object} disable List of elements to disable..
44987  * @cfg {Array} btns List of additional buttons.
44988  * 
44989  * 
44990  * NEEDS Extra CSS? 
44991  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44992  */
44993  
44994 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44995 {
44996     
44997     Roo.apply(this, config);
44998     
44999     // default disabled, based on 'good practice'..
45000     this.disable = this.disable || {};
45001     Roo.applyIf(this.disable, {
45002         fontSize : true,
45003         colors : true,
45004         specialElements : true
45005     });
45006     
45007     
45008     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45009     // dont call parent... till later.
45010 }
45011
45012 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45013     
45014     tb: false,
45015     
45016     rendered: false,
45017     
45018     editor : false,
45019     editorcore : false,
45020     /**
45021      * @cfg {Object} disable  List of toolbar elements to disable
45022          
45023      */
45024     disable : false,
45025     
45026     
45027      /**
45028      * @cfg {String} createLinkText The default text for the create link prompt
45029      */
45030     createLinkText : 'Please enter the URL for the link:',
45031     /**
45032      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45033      */
45034     defaultLinkValue : 'http:/'+'/',
45035    
45036     
45037       /**
45038      * @cfg {Array} fontFamilies An array of available font families
45039      */
45040     fontFamilies : [
45041         'Arial',
45042         'Courier New',
45043         'Tahoma',
45044         'Times New Roman',
45045         'Verdana'
45046     ],
45047     
45048     specialChars : [
45049            "&#169;",
45050           "&#174;",     
45051           "&#8482;",    
45052           "&#163;" ,    
45053          // "&#8212;",    
45054           "&#8230;",    
45055           "&#247;" ,    
45056         //  "&#225;" ,     ?? a acute?
45057            "&#8364;"    , //Euro
45058        //   "&#8220;"    ,
45059         //  "&#8221;"    ,
45060         //  "&#8226;"    ,
45061           "&#176;"  //   , // degrees
45062
45063          // "&#233;"     , // e ecute
45064          // "&#250;"     , // u ecute?
45065     ],
45066     
45067     specialElements : [
45068         {
45069             text: "Insert Table",
45070             xtype: 'MenuItem',
45071             xns : Roo.Menu,
45072             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45073                 
45074         },
45075         {    
45076             text: "Insert Image",
45077             xtype: 'MenuItem',
45078             xns : Roo.Menu,
45079             ihtml : '<img src="about:blank"/>'
45080             
45081         }
45082         
45083          
45084     ],
45085     
45086     
45087     inputElements : [ 
45088             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45089             "input:submit", "input:button", "select", "textarea", "label" ],
45090     formats : [
45091         ["p"] ,  
45092         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45093         ["pre"],[ "code"], 
45094         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45095         ['div'],['span']
45096     ],
45097     
45098     cleanStyles : [
45099         "font-size"
45100     ],
45101      /**
45102      * @cfg {String} defaultFont default font to use.
45103      */
45104     defaultFont: 'tahoma',
45105    
45106     fontSelect : false,
45107     
45108     
45109     formatCombo : false,
45110     
45111     init : function(editor)
45112     {
45113         this.editor = editor;
45114         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45115         var editorcore = this.editorcore;
45116         
45117         var _t = this;
45118         
45119         var fid = editorcore.frameId;
45120         var etb = this;
45121         function btn(id, toggle, handler){
45122             var xid = fid + '-'+ id ;
45123             return {
45124                 id : xid,
45125                 cmd : id,
45126                 cls : 'x-btn-icon x-edit-'+id,
45127                 enableToggle:toggle !== false,
45128                 scope: _t, // was editor...
45129                 handler:handler||_t.relayBtnCmd,
45130                 clickEvent:'mousedown',
45131                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45132                 tabIndex:-1
45133             };
45134         }
45135         
45136         
45137         
45138         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45139         this.tb = tb;
45140          // stop form submits
45141         tb.el.on('click', function(e){
45142             e.preventDefault(); // what does this do?
45143         });
45144
45145         if(!this.disable.font) { // && !Roo.isSafari){
45146             /* why no safari for fonts 
45147             editor.fontSelect = tb.el.createChild({
45148                 tag:'select',
45149                 tabIndex: -1,
45150                 cls:'x-font-select',
45151                 html: this.createFontOptions()
45152             });
45153             
45154             editor.fontSelect.on('change', function(){
45155                 var font = editor.fontSelect.dom.value;
45156                 editor.relayCmd('fontname', font);
45157                 editor.deferFocus();
45158             }, editor);
45159             
45160             tb.add(
45161                 editor.fontSelect.dom,
45162                 '-'
45163             );
45164             */
45165             
45166         };
45167         if(!this.disable.formats){
45168             this.formatCombo = new Roo.form.ComboBox({
45169                 store: new Roo.data.SimpleStore({
45170                     id : 'tag',
45171                     fields: ['tag'],
45172                     data : this.formats // from states.js
45173                 }),
45174                 blockFocus : true,
45175                 name : '',
45176                 //autoCreate : {tag: "div",  size: "20"},
45177                 displayField:'tag',
45178                 typeAhead: false,
45179                 mode: 'local',
45180                 editable : false,
45181                 triggerAction: 'all',
45182                 emptyText:'Add tag',
45183                 selectOnFocus:true,
45184                 width:135,
45185                 listeners : {
45186                     'select': function(c, r, i) {
45187                         editorcore.insertTag(r.get('tag'));
45188                         editor.focus();
45189                     }
45190                 }
45191
45192             });
45193             tb.addField(this.formatCombo);
45194             
45195         }
45196         
45197         if(!this.disable.format){
45198             tb.add(
45199                 btn('bold'),
45200                 btn('italic'),
45201                 btn('underline'),
45202                 btn('strikethrough')
45203             );
45204         };
45205         if(!this.disable.fontSize){
45206             tb.add(
45207                 '-',
45208                 
45209                 
45210                 btn('increasefontsize', false, editorcore.adjustFont),
45211                 btn('decreasefontsize', false, editorcore.adjustFont)
45212             );
45213         };
45214         
45215         
45216         if(!this.disable.colors){
45217             tb.add(
45218                 '-', {
45219                     id:editorcore.frameId +'-forecolor',
45220                     cls:'x-btn-icon x-edit-forecolor',
45221                     clickEvent:'mousedown',
45222                     tooltip: this.buttonTips['forecolor'] || undefined,
45223                     tabIndex:-1,
45224                     menu : new Roo.menu.ColorMenu({
45225                         allowReselect: true,
45226                         focus: Roo.emptyFn,
45227                         value:'000000',
45228                         plain:true,
45229                         selectHandler: function(cp, color){
45230                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45231                             editor.deferFocus();
45232                         },
45233                         scope: editorcore,
45234                         clickEvent:'mousedown'
45235                     })
45236                 }, {
45237                     id:editorcore.frameId +'backcolor',
45238                     cls:'x-btn-icon x-edit-backcolor',
45239                     clickEvent:'mousedown',
45240                     tooltip: this.buttonTips['backcolor'] || undefined,
45241                     tabIndex:-1,
45242                     menu : new Roo.menu.ColorMenu({
45243                         focus: Roo.emptyFn,
45244                         value:'FFFFFF',
45245                         plain:true,
45246                         allowReselect: true,
45247                         selectHandler: function(cp, color){
45248                             if(Roo.isGecko){
45249                                 editorcore.execCmd('useCSS', false);
45250                                 editorcore.execCmd('hilitecolor', color);
45251                                 editorcore.execCmd('useCSS', true);
45252                                 editor.deferFocus();
45253                             }else{
45254                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45255                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45256                                 editor.deferFocus();
45257                             }
45258                         },
45259                         scope:editorcore,
45260                         clickEvent:'mousedown'
45261                     })
45262                 }
45263             );
45264         };
45265         // now add all the items...
45266         
45267
45268         if(!this.disable.alignments){
45269             tb.add(
45270                 '-',
45271                 btn('justifyleft'),
45272                 btn('justifycenter'),
45273                 btn('justifyright')
45274             );
45275         };
45276
45277         //if(!Roo.isSafari){
45278             if(!this.disable.links){
45279                 tb.add(
45280                     '-',
45281                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45282                 );
45283             };
45284
45285             if(!this.disable.lists){
45286                 tb.add(
45287                     '-',
45288                     btn('insertorderedlist'),
45289                     btn('insertunorderedlist')
45290                 );
45291             }
45292             if(!this.disable.sourceEdit){
45293                 tb.add(
45294                     '-',
45295                     btn('sourceedit', true, function(btn){
45296                         this.toggleSourceEdit(btn.pressed);
45297                     })
45298                 );
45299             }
45300         //}
45301         
45302         var smenu = { };
45303         // special menu.. - needs to be tidied up..
45304         if (!this.disable.special) {
45305             smenu = {
45306                 text: "&#169;",
45307                 cls: 'x-edit-none',
45308                 
45309                 menu : {
45310                     items : []
45311                 }
45312             };
45313             for (var i =0; i < this.specialChars.length; i++) {
45314                 smenu.menu.items.push({
45315                     
45316                     html: this.specialChars[i],
45317                     handler: function(a,b) {
45318                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45319                         //editor.insertAtCursor(a.html);
45320                         
45321                     },
45322                     tabIndex:-1
45323                 });
45324             }
45325             
45326             
45327             tb.add(smenu);
45328             
45329             
45330         }
45331         
45332         var cmenu = { };
45333         if (!this.disable.cleanStyles) {
45334             cmenu = {
45335                 cls: 'x-btn-icon x-btn-clear',
45336                 
45337                 menu : {
45338                     items : []
45339                 }
45340             };
45341             for (var i =0; i < this.cleanStyles.length; i++) {
45342                 cmenu.menu.items.push({
45343                     actiontype : this.cleanStyles[i],
45344                     html: 'Remove ' + this.cleanStyles[i],
45345                     handler: function(a,b) {
45346 //                        Roo.log(a);
45347 //                        Roo.log(b);
45348                         var c = Roo.get(editorcore.doc.body);
45349                         c.select('[style]').each(function(s) {
45350                             s.dom.style.removeProperty(a.actiontype);
45351                         });
45352                         editorcore.syncValue();
45353                     },
45354                     tabIndex:-1
45355                 });
45356             }
45357              cmenu.menu.items.push({
45358                 actiontype : 'tablewidths',
45359                 html: 'Remove Table Widths',
45360                 handler: function(a,b) {
45361                     editorcore.cleanTableWidths();
45362                     editorcore.syncValue();
45363                 },
45364                 tabIndex:-1
45365             });
45366             cmenu.menu.items.push({
45367                 actiontype : 'word',
45368                 html: 'Remove MS Word Formating',
45369                 handler: function(a,b) {
45370                     editorcore.cleanWord();
45371                     editorcore.syncValue();
45372                 },
45373                 tabIndex:-1
45374             });
45375             
45376             cmenu.menu.items.push({
45377                 actiontype : 'all',
45378                 html: 'Remove All Styles',
45379                 handler: function(a,b) {
45380                     
45381                     var c = Roo.get(editorcore.doc.body);
45382                     c.select('[style]').each(function(s) {
45383                         s.dom.removeAttribute('style');
45384                     });
45385                     editorcore.syncValue();
45386                 },
45387                 tabIndex:-1
45388             });
45389             
45390             cmenu.menu.items.push({
45391                 actiontype : 'all',
45392                 html: 'Remove All CSS Classes',
45393                 handler: function(a,b) {
45394                     
45395                     var c = Roo.get(editorcore.doc.body);
45396                     c.select('[class]').each(function(s) {
45397                         s.dom.className = '';
45398                     });
45399                     editorcore.syncValue();
45400                 },
45401                 tabIndex:-1
45402             });
45403             
45404              cmenu.menu.items.push({
45405                 actiontype : 'tidy',
45406                 html: 'Tidy HTML Source',
45407                 handler: function(a,b) {
45408                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45409                     editorcore.syncValue();
45410                 },
45411                 tabIndex:-1
45412             });
45413             
45414             
45415             tb.add(cmenu);
45416         }
45417          
45418         if (!this.disable.specialElements) {
45419             var semenu = {
45420                 text: "Other;",
45421                 cls: 'x-edit-none',
45422                 menu : {
45423                     items : []
45424                 }
45425             };
45426             for (var i =0; i < this.specialElements.length; i++) {
45427                 semenu.menu.items.push(
45428                     Roo.apply({ 
45429                         handler: function(a,b) {
45430                             editor.insertAtCursor(this.ihtml);
45431                         }
45432                     }, this.specialElements[i])
45433                 );
45434                     
45435             }
45436             
45437             tb.add(semenu);
45438             
45439             
45440         }
45441          
45442         
45443         if (this.btns) {
45444             for(var i =0; i< this.btns.length;i++) {
45445                 var b = Roo.factory(this.btns[i],Roo.form);
45446                 b.cls =  'x-edit-none';
45447                 
45448                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45449                     b.cls += ' x-init-enable';
45450                 }
45451                 
45452                 b.scope = editorcore;
45453                 tb.add(b);
45454             }
45455         
45456         }
45457         
45458         
45459         
45460         // disable everything...
45461         
45462         this.tb.items.each(function(item){
45463             
45464            if(
45465                 item.id != editorcore.frameId+ '-sourceedit' && 
45466                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45467             ){
45468                 
45469                 item.disable();
45470             }
45471         });
45472         this.rendered = true;
45473         
45474         // the all the btns;
45475         editor.on('editorevent', this.updateToolbar, this);
45476         // other toolbars need to implement this..
45477         //editor.on('editmodechange', this.updateToolbar, this);
45478     },
45479     
45480     
45481     relayBtnCmd : function(btn) {
45482         this.editorcore.relayCmd(btn.cmd);
45483     },
45484     // private used internally
45485     createLink : function(){
45486         Roo.log("create link?");
45487         var url = prompt(this.createLinkText, this.defaultLinkValue);
45488         if(url && url != 'http:/'+'/'){
45489             this.editorcore.relayCmd('createlink', url);
45490         }
45491     },
45492
45493     
45494     /**
45495      * Protected method that will not generally be called directly. It triggers
45496      * a toolbar update by reading the markup state of the current selection in the editor.
45497      */
45498     updateToolbar: function(){
45499
45500         if(!this.editorcore.activated){
45501             this.editor.onFirstFocus();
45502             return;
45503         }
45504
45505         var btns = this.tb.items.map, 
45506             doc = this.editorcore.doc,
45507             frameId = this.editorcore.frameId;
45508
45509         if(!this.disable.font && !Roo.isSafari){
45510             /*
45511             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45512             if(name != this.fontSelect.dom.value){
45513                 this.fontSelect.dom.value = name;
45514             }
45515             */
45516         }
45517         if(!this.disable.format){
45518             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45519             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45520             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45521             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45522         }
45523         if(!this.disable.alignments){
45524             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45525             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45526             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45527         }
45528         if(!Roo.isSafari && !this.disable.lists){
45529             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45530             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45531         }
45532         
45533         var ans = this.editorcore.getAllAncestors();
45534         if (this.formatCombo) {
45535             
45536             
45537             var store = this.formatCombo.store;
45538             this.formatCombo.setValue("");
45539             for (var i =0; i < ans.length;i++) {
45540                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45541                     // select it..
45542                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45543                     break;
45544                 }
45545             }
45546         }
45547         
45548         
45549         
45550         // hides menus... - so this cant be on a menu...
45551         Roo.menu.MenuMgr.hideAll();
45552
45553         //this.editorsyncValue();
45554     },
45555    
45556     
45557     createFontOptions : function(){
45558         var buf = [], fs = this.fontFamilies, ff, lc;
45559         
45560         
45561         
45562         for(var i = 0, len = fs.length; i< len; i++){
45563             ff = fs[i];
45564             lc = ff.toLowerCase();
45565             buf.push(
45566                 '<option value="',lc,'" style="font-family:',ff,';"',
45567                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45568                     ff,
45569                 '</option>'
45570             );
45571         }
45572         return buf.join('');
45573     },
45574     
45575     toggleSourceEdit : function(sourceEditMode){
45576         
45577         Roo.log("toolbar toogle");
45578         if(sourceEditMode === undefined){
45579             sourceEditMode = !this.sourceEditMode;
45580         }
45581         this.sourceEditMode = sourceEditMode === true;
45582         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45583         // just toggle the button?
45584         if(btn.pressed !== this.sourceEditMode){
45585             btn.toggle(this.sourceEditMode);
45586             return;
45587         }
45588         
45589         if(sourceEditMode){
45590             Roo.log("disabling buttons");
45591             this.tb.items.each(function(item){
45592                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45593                     item.disable();
45594                 }
45595             });
45596           
45597         }else{
45598             Roo.log("enabling buttons");
45599             if(this.editorcore.initialized){
45600                 this.tb.items.each(function(item){
45601                     item.enable();
45602                 });
45603             }
45604             
45605         }
45606         Roo.log("calling toggole on editor");
45607         // tell the editor that it's been pressed..
45608         this.editor.toggleSourceEdit(sourceEditMode);
45609        
45610     },
45611      /**
45612      * Object collection of toolbar tooltips for the buttons in the editor. The key
45613      * is the command id associated with that button and the value is a valid QuickTips object.
45614      * For example:
45615 <pre><code>
45616 {
45617     bold : {
45618         title: 'Bold (Ctrl+B)',
45619         text: 'Make the selected text bold.',
45620         cls: 'x-html-editor-tip'
45621     },
45622     italic : {
45623         title: 'Italic (Ctrl+I)',
45624         text: 'Make the selected text italic.',
45625         cls: 'x-html-editor-tip'
45626     },
45627     ...
45628 </code></pre>
45629     * @type Object
45630      */
45631     buttonTips : {
45632         bold : {
45633             title: 'Bold (Ctrl+B)',
45634             text: 'Make the selected text bold.',
45635             cls: 'x-html-editor-tip'
45636         },
45637         italic : {
45638             title: 'Italic (Ctrl+I)',
45639             text: 'Make the selected text italic.',
45640             cls: 'x-html-editor-tip'
45641         },
45642         underline : {
45643             title: 'Underline (Ctrl+U)',
45644             text: 'Underline the selected text.',
45645             cls: 'x-html-editor-tip'
45646         },
45647         strikethrough : {
45648             title: 'Strikethrough',
45649             text: 'Strikethrough the selected text.',
45650             cls: 'x-html-editor-tip'
45651         },
45652         increasefontsize : {
45653             title: 'Grow Text',
45654             text: 'Increase the font size.',
45655             cls: 'x-html-editor-tip'
45656         },
45657         decreasefontsize : {
45658             title: 'Shrink Text',
45659             text: 'Decrease the font size.',
45660             cls: 'x-html-editor-tip'
45661         },
45662         backcolor : {
45663             title: 'Text Highlight Color',
45664             text: 'Change the background color of the selected text.',
45665             cls: 'x-html-editor-tip'
45666         },
45667         forecolor : {
45668             title: 'Font Color',
45669             text: 'Change the color of the selected text.',
45670             cls: 'x-html-editor-tip'
45671         },
45672         justifyleft : {
45673             title: 'Align Text Left',
45674             text: 'Align text to the left.',
45675             cls: 'x-html-editor-tip'
45676         },
45677         justifycenter : {
45678             title: 'Center Text',
45679             text: 'Center text in the editor.',
45680             cls: 'x-html-editor-tip'
45681         },
45682         justifyright : {
45683             title: 'Align Text Right',
45684             text: 'Align text to the right.',
45685             cls: 'x-html-editor-tip'
45686         },
45687         insertunorderedlist : {
45688             title: 'Bullet List',
45689             text: 'Start a bulleted list.',
45690             cls: 'x-html-editor-tip'
45691         },
45692         insertorderedlist : {
45693             title: 'Numbered List',
45694             text: 'Start a numbered list.',
45695             cls: 'x-html-editor-tip'
45696         },
45697         createlink : {
45698             title: 'Hyperlink',
45699             text: 'Make the selected text a hyperlink.',
45700             cls: 'x-html-editor-tip'
45701         },
45702         sourceedit : {
45703             title: 'Source Edit',
45704             text: 'Switch to source editing mode.',
45705             cls: 'x-html-editor-tip'
45706         }
45707     },
45708     // private
45709     onDestroy : function(){
45710         if(this.rendered){
45711             
45712             this.tb.items.each(function(item){
45713                 if(item.menu){
45714                     item.menu.removeAll();
45715                     if(item.menu.el){
45716                         item.menu.el.destroy();
45717                     }
45718                 }
45719                 item.destroy();
45720             });
45721              
45722         }
45723     },
45724     onFirstFocus: function() {
45725         this.tb.items.each(function(item){
45726            item.enable();
45727         });
45728     }
45729 });
45730
45731
45732
45733
45734 // <script type="text/javascript">
45735 /*
45736  * Based on
45737  * Ext JS Library 1.1.1
45738  * Copyright(c) 2006-2007, Ext JS, LLC.
45739  *  
45740  
45741  */
45742
45743  
45744 /**
45745  * @class Roo.form.HtmlEditor.ToolbarContext
45746  * Context Toolbar
45747  * 
45748  * Usage:
45749  *
45750  new Roo.form.HtmlEditor({
45751     ....
45752     toolbars : [
45753         { xtype: 'ToolbarStandard', styles : {} }
45754         { xtype: 'ToolbarContext', disable : {} }
45755     ]
45756 })
45757
45758      
45759  * 
45760  * @config : {Object} disable List of elements to disable.. (not done yet.)
45761  * @config : {Object} styles  Map of styles available.
45762  * 
45763  */
45764
45765 Roo.form.HtmlEditor.ToolbarContext = function(config)
45766 {
45767     
45768     Roo.apply(this, config);
45769     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45770     // dont call parent... till later.
45771     this.styles = this.styles || {};
45772 }
45773
45774  
45775
45776 Roo.form.HtmlEditor.ToolbarContext.types = {
45777     'IMG' : {
45778         width : {
45779             title: "Width",
45780             width: 40
45781         },
45782         height:  {
45783             title: "Height",
45784             width: 40
45785         },
45786         align: {
45787             title: "Align",
45788             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45789             width : 80
45790             
45791         },
45792         border: {
45793             title: "Border",
45794             width: 40
45795         },
45796         alt: {
45797             title: "Alt",
45798             width: 120
45799         },
45800         src : {
45801             title: "Src",
45802             width: 220
45803         }
45804         
45805     },
45806     'A' : {
45807         name : {
45808             title: "Name",
45809             width: 50
45810         },
45811         target:  {
45812             title: "Target",
45813             width: 120
45814         },
45815         href:  {
45816             title: "Href",
45817             width: 220
45818         } // border?
45819         
45820     },
45821     'TABLE' : {
45822         rows : {
45823             title: "Rows",
45824             width: 20
45825         },
45826         cols : {
45827             title: "Cols",
45828             width: 20
45829         },
45830         width : {
45831             title: "Width",
45832             width: 40
45833         },
45834         height : {
45835             title: "Height",
45836             width: 40
45837         },
45838         border : {
45839             title: "Border",
45840             width: 20
45841         }
45842     },
45843     'TD' : {
45844         width : {
45845             title: "Width",
45846             width: 40
45847         },
45848         height : {
45849             title: "Height",
45850             width: 40
45851         },   
45852         align: {
45853             title: "Align",
45854             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45855             width: 80
45856         },
45857         valign: {
45858             title: "Valign",
45859             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45860             width: 80
45861         },
45862         colspan: {
45863             title: "Colspan",
45864             width: 20
45865             
45866         },
45867          'font-family'  : {
45868             title : "Font",
45869             style : 'fontFamily',
45870             displayField: 'display',
45871             optname : 'font-family',
45872             width: 140
45873         }
45874     },
45875     'INPUT' : {
45876         name : {
45877             title: "name",
45878             width: 120
45879         },
45880         value : {
45881             title: "Value",
45882             width: 120
45883         },
45884         width : {
45885             title: "Width",
45886             width: 40
45887         }
45888     },
45889     'LABEL' : {
45890         'for' : {
45891             title: "For",
45892             width: 120
45893         }
45894     },
45895     'TEXTAREA' : {
45896           name : {
45897             title: "name",
45898             width: 120
45899         },
45900         rows : {
45901             title: "Rows",
45902             width: 20
45903         },
45904         cols : {
45905             title: "Cols",
45906             width: 20
45907         }
45908     },
45909     'SELECT' : {
45910         name : {
45911             title: "name",
45912             width: 120
45913         },
45914         selectoptions : {
45915             title: "Options",
45916             width: 200
45917         }
45918     },
45919     
45920     // should we really allow this??
45921     // should this just be 
45922     'BODY' : {
45923         title : {
45924             title: "Title",
45925             width: 200,
45926             disabled : true
45927         }
45928     },
45929     'SPAN' : {
45930         'font-family'  : {
45931             title : "Font",
45932             style : 'fontFamily',
45933             displayField: 'display',
45934             optname : 'font-family',
45935             width: 140
45936         }
45937     },
45938     'DIV' : {
45939         'font-family'  : {
45940             title : "Font",
45941             style : 'fontFamily',
45942             displayField: 'display',
45943             optname : 'font-family',
45944             width: 140
45945         }
45946     },
45947      'P' : {
45948         'font-family'  : {
45949             title : "Font",
45950             style : 'fontFamily',
45951             displayField: 'display',
45952             optname : 'font-family',
45953             width: 140
45954         }
45955     },
45956     
45957     '*' : {
45958         // empty..
45959     }
45960
45961 };
45962
45963 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45964 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45965
45966 Roo.form.HtmlEditor.ToolbarContext.options = {
45967         'font-family'  : [ 
45968                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45969                 [ 'Courier New', 'Courier New'],
45970                 [ 'Tahoma', 'Tahoma'],
45971                 [ 'Times New Roman,serif', 'Times'],
45972                 [ 'Verdana','Verdana' ]
45973         ]
45974 };
45975
45976 // fixme - these need to be configurable..
45977  
45978
45979 //Roo.form.HtmlEditor.ToolbarContext.types
45980
45981
45982 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45983     
45984     tb: false,
45985     
45986     rendered: false,
45987     
45988     editor : false,
45989     editorcore : false,
45990     /**
45991      * @cfg {Object} disable  List of toolbar elements to disable
45992          
45993      */
45994     disable : false,
45995     /**
45996      * @cfg {Object} styles List of styles 
45997      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45998      *
45999      * These must be defined in the page, so they get rendered correctly..
46000      * .headline { }
46001      * TD.underline { }
46002      * 
46003      */
46004     styles : false,
46005     
46006     options: false,
46007     
46008     toolbars : false,
46009     
46010     init : function(editor)
46011     {
46012         this.editor = editor;
46013         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46014         var editorcore = this.editorcore;
46015         
46016         var fid = editorcore.frameId;
46017         var etb = this;
46018         function btn(id, toggle, handler){
46019             var xid = fid + '-'+ id ;
46020             return {
46021                 id : xid,
46022                 cmd : id,
46023                 cls : 'x-btn-icon x-edit-'+id,
46024                 enableToggle:toggle !== false,
46025                 scope: editorcore, // was editor...
46026                 handler:handler||editorcore.relayBtnCmd,
46027                 clickEvent:'mousedown',
46028                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46029                 tabIndex:-1
46030             };
46031         }
46032         // create a new element.
46033         var wdiv = editor.wrap.createChild({
46034                 tag: 'div'
46035             }, editor.wrap.dom.firstChild.nextSibling, true);
46036         
46037         // can we do this more than once??
46038         
46039          // stop form submits
46040       
46041  
46042         // disable everything...
46043         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46044         this.toolbars = {};
46045            
46046         for (var i in  ty) {
46047           
46048             this.toolbars[i] = this.buildToolbar(ty[i],i);
46049         }
46050         this.tb = this.toolbars.BODY;
46051         this.tb.el.show();
46052         this.buildFooter();
46053         this.footer.show();
46054         editor.on('hide', function( ) { this.footer.hide() }, this);
46055         editor.on('show', function( ) { this.footer.show() }, this);
46056         
46057          
46058         this.rendered = true;
46059         
46060         // the all the btns;
46061         editor.on('editorevent', this.updateToolbar, this);
46062         // other toolbars need to implement this..
46063         //editor.on('editmodechange', this.updateToolbar, this);
46064     },
46065     
46066     
46067     
46068     /**
46069      * Protected method that will not generally be called directly. It triggers
46070      * a toolbar update by reading the markup state of the current selection in the editor.
46071      *
46072      * Note you can force an update by calling on('editorevent', scope, false)
46073      */
46074     updateToolbar: function(editor,ev,sel){
46075
46076         //Roo.log(ev);
46077         // capture mouse up - this is handy for selecting images..
46078         // perhaps should go somewhere else...
46079         if(!this.editorcore.activated){
46080              this.editor.onFirstFocus();
46081             return;
46082         }
46083         
46084         
46085         
46086         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46087         // selectNode - might want to handle IE?
46088         if (ev &&
46089             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46090             ev.target && ev.target.tagName == 'IMG') {
46091             // they have click on an image...
46092             // let's see if we can change the selection...
46093             sel = ev.target;
46094          
46095               var nodeRange = sel.ownerDocument.createRange();
46096             try {
46097                 nodeRange.selectNode(sel);
46098             } catch (e) {
46099                 nodeRange.selectNodeContents(sel);
46100             }
46101             //nodeRange.collapse(true);
46102             var s = this.editorcore.win.getSelection();
46103             s.removeAllRanges();
46104             s.addRange(nodeRange);
46105         }  
46106         
46107       
46108         var updateFooter = sel ? false : true;
46109         
46110         
46111         var ans = this.editorcore.getAllAncestors();
46112         
46113         // pick
46114         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46115         
46116         if (!sel) { 
46117             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46118             sel = sel ? sel : this.editorcore.doc.body;
46119             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46120             
46121         }
46122         // pick a menu that exists..
46123         var tn = sel.tagName.toUpperCase();
46124         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46125         
46126         tn = sel.tagName.toUpperCase();
46127         
46128         var lastSel = this.tb.selectedNode;
46129         
46130         this.tb.selectedNode = sel;
46131         
46132         // if current menu does not match..
46133         
46134         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46135                 
46136             this.tb.el.hide();
46137             ///console.log("show: " + tn);
46138             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46139             this.tb.el.show();
46140             // update name
46141             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46142             
46143             
46144             // update attributes
46145             if (this.tb.fields) {
46146                 this.tb.fields.each(function(e) {
46147                     if (e.stylename) {
46148                         e.setValue(sel.style[e.stylename]);
46149                         return;
46150                     } 
46151                    e.setValue(sel.getAttribute(e.attrname));
46152                 });
46153             }
46154             
46155             var hasStyles = false;
46156             for(var i in this.styles) {
46157                 hasStyles = true;
46158                 break;
46159             }
46160             
46161             // update styles
46162             if (hasStyles) { 
46163                 var st = this.tb.fields.item(0);
46164                 
46165                 st.store.removeAll();
46166                
46167                 
46168                 var cn = sel.className.split(/\s+/);
46169                 
46170                 var avs = [];
46171                 if (this.styles['*']) {
46172                     
46173                     Roo.each(this.styles['*'], function(v) {
46174                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46175                     });
46176                 }
46177                 if (this.styles[tn]) { 
46178                     Roo.each(this.styles[tn], function(v) {
46179                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46180                     });
46181                 }
46182                 
46183                 st.store.loadData(avs);
46184                 st.collapse();
46185                 st.setValue(cn);
46186             }
46187             // flag our selected Node.
46188             this.tb.selectedNode = sel;
46189            
46190            
46191             Roo.menu.MenuMgr.hideAll();
46192
46193         }
46194         
46195         if (!updateFooter) {
46196             //this.footDisp.dom.innerHTML = ''; 
46197             return;
46198         }
46199         // update the footer
46200         //
46201         var html = '';
46202         
46203         this.footerEls = ans.reverse();
46204         Roo.each(this.footerEls, function(a,i) {
46205             if (!a) { return; }
46206             html += html.length ? ' &gt; '  :  '';
46207             
46208             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46209             
46210         });
46211        
46212         // 
46213         var sz = this.footDisp.up('td').getSize();
46214         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46215         this.footDisp.dom.style.marginLeft = '5px';
46216         
46217         this.footDisp.dom.style.overflow = 'hidden';
46218         
46219         this.footDisp.dom.innerHTML = html;
46220             
46221         //this.editorsyncValue();
46222     },
46223      
46224     
46225    
46226        
46227     // private
46228     onDestroy : function(){
46229         if(this.rendered){
46230             
46231             this.tb.items.each(function(item){
46232                 if(item.menu){
46233                     item.menu.removeAll();
46234                     if(item.menu.el){
46235                         item.menu.el.destroy();
46236                     }
46237                 }
46238                 item.destroy();
46239             });
46240              
46241         }
46242     },
46243     onFirstFocus: function() {
46244         // need to do this for all the toolbars..
46245         this.tb.items.each(function(item){
46246            item.enable();
46247         });
46248     },
46249     buildToolbar: function(tlist, nm)
46250     {
46251         var editor = this.editor;
46252         var editorcore = this.editorcore;
46253          // create a new element.
46254         var wdiv = editor.wrap.createChild({
46255                 tag: 'div'
46256             }, editor.wrap.dom.firstChild.nextSibling, true);
46257         
46258        
46259         var tb = new Roo.Toolbar(wdiv);
46260         // add the name..
46261         
46262         tb.add(nm+ ":&nbsp;");
46263         
46264         var styles = [];
46265         for(var i in this.styles) {
46266             styles.push(i);
46267         }
46268         
46269         // styles...
46270         if (styles && styles.length) {
46271             
46272             // this needs a multi-select checkbox...
46273             tb.addField( new Roo.form.ComboBox({
46274                 store: new Roo.data.SimpleStore({
46275                     id : 'val',
46276                     fields: ['val', 'selected'],
46277                     data : [] 
46278                 }),
46279                 name : '-roo-edit-className',
46280                 attrname : 'className',
46281                 displayField: 'val',
46282                 typeAhead: false,
46283                 mode: 'local',
46284                 editable : false,
46285                 triggerAction: 'all',
46286                 emptyText:'Select Style',
46287                 selectOnFocus:true,
46288                 width: 130,
46289                 listeners : {
46290                     'select': function(c, r, i) {
46291                         // initial support only for on class per el..
46292                         tb.selectedNode.className =  r ? r.get('val') : '';
46293                         editorcore.syncValue();
46294                     }
46295                 }
46296     
46297             }));
46298         }
46299         
46300         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46301         var tbops = tbc.options;
46302         
46303         for (var i in tlist) {
46304             
46305             var item = tlist[i];
46306             tb.add(item.title + ":&nbsp;");
46307             
46308             
46309             //optname == used so you can configure the options available..
46310             var opts = item.opts ? item.opts : false;
46311             if (item.optname) {
46312                 opts = tbops[item.optname];
46313            
46314             }
46315             
46316             if (opts) {
46317                 // opts == pulldown..
46318                 tb.addField( new Roo.form.ComboBox({
46319                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46320                         id : 'val',
46321                         fields: ['val', 'display'],
46322                         data : opts  
46323                     }),
46324                     name : '-roo-edit-' + i,
46325                     attrname : i,
46326                     stylename : item.style ? item.style : false,
46327                     displayField: item.displayField ? item.displayField : 'val',
46328                     valueField :  'val',
46329                     typeAhead: false,
46330                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46331                     editable : false,
46332                     triggerAction: 'all',
46333                     emptyText:'Select',
46334                     selectOnFocus:true,
46335                     width: item.width ? item.width  : 130,
46336                     listeners : {
46337                         'select': function(c, r, i) {
46338                             if (c.stylename) {
46339                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46340                                 return;
46341                             }
46342                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46343                         }
46344                     }
46345
46346                 }));
46347                 continue;
46348                     
46349                  
46350                 
46351                 tb.addField( new Roo.form.TextField({
46352                     name: i,
46353                     width: 100,
46354                     //allowBlank:false,
46355                     value: ''
46356                 }));
46357                 continue;
46358             }
46359             tb.addField( new Roo.form.TextField({
46360                 name: '-roo-edit-' + i,
46361                 attrname : i,
46362                 
46363                 width: item.width,
46364                 //allowBlank:true,
46365                 value: '',
46366                 listeners: {
46367                     'change' : function(f, nv, ov) {
46368                         tb.selectedNode.setAttribute(f.attrname, nv);
46369                         editorcore.syncValue();
46370                     }
46371                 }
46372             }));
46373              
46374         }
46375         
46376         var _this = this;
46377         
46378         if(nm == 'BODY'){
46379             tb.addSeparator();
46380         
46381             tb.addButton( {
46382                 text: 'Stylesheets',
46383
46384                 listeners : {
46385                     click : function ()
46386                     {
46387                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46388                     }
46389                 }
46390             });
46391         }
46392         
46393         tb.addFill();
46394         tb.addButton( {
46395             text: 'Remove Tag',
46396     
46397             listeners : {
46398                 click : function ()
46399                 {
46400                     // remove
46401                     // undo does not work.
46402                      
46403                     var sn = tb.selectedNode;
46404                     
46405                     var pn = sn.parentNode;
46406                     
46407                     var stn =  sn.childNodes[0];
46408                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46409                     while (sn.childNodes.length) {
46410                         var node = sn.childNodes[0];
46411                         sn.removeChild(node);
46412                         //Roo.log(node);
46413                         pn.insertBefore(node, sn);
46414                         
46415                     }
46416                     pn.removeChild(sn);
46417                     var range = editorcore.createRange();
46418         
46419                     range.setStart(stn,0);
46420                     range.setEnd(en,0); //????
46421                     //range.selectNode(sel);
46422                     
46423                     
46424                     var selection = editorcore.getSelection();
46425                     selection.removeAllRanges();
46426                     selection.addRange(range);
46427                     
46428                     
46429                     
46430                     //_this.updateToolbar(null, null, pn);
46431                     _this.updateToolbar(null, null, null);
46432                     _this.footDisp.dom.innerHTML = ''; 
46433                 }
46434             }
46435             
46436                     
46437                 
46438             
46439         });
46440         
46441         
46442         tb.el.on('click', function(e){
46443             e.preventDefault(); // what does this do?
46444         });
46445         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46446         tb.el.hide();
46447         tb.name = nm;
46448         // dont need to disable them... as they will get hidden
46449         return tb;
46450          
46451         
46452     },
46453     buildFooter : function()
46454     {
46455         
46456         var fel = this.editor.wrap.createChild();
46457         this.footer = new Roo.Toolbar(fel);
46458         // toolbar has scrolly on left / right?
46459         var footDisp= new Roo.Toolbar.Fill();
46460         var _t = this;
46461         this.footer.add(
46462             {
46463                 text : '&lt;',
46464                 xtype: 'Button',
46465                 handler : function() {
46466                     _t.footDisp.scrollTo('left',0,true)
46467                 }
46468             }
46469         );
46470         this.footer.add( footDisp );
46471         this.footer.add( 
46472             {
46473                 text : '&gt;',
46474                 xtype: 'Button',
46475                 handler : function() {
46476                     // no animation..
46477                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46478                 }
46479             }
46480         );
46481         var fel = Roo.get(footDisp.el);
46482         fel.addClass('x-editor-context');
46483         this.footDispWrap = fel; 
46484         this.footDispWrap.overflow  = 'hidden';
46485         
46486         this.footDisp = fel.createChild();
46487         this.footDispWrap.on('click', this.onContextClick, this)
46488         
46489         
46490     },
46491     onContextClick : function (ev,dom)
46492     {
46493         ev.preventDefault();
46494         var  cn = dom.className;
46495         //Roo.log(cn);
46496         if (!cn.match(/x-ed-loc-/)) {
46497             return;
46498         }
46499         var n = cn.split('-').pop();
46500         var ans = this.footerEls;
46501         var sel = ans[n];
46502         
46503          // pick
46504         var range = this.editorcore.createRange();
46505         
46506         range.selectNodeContents(sel);
46507         //range.selectNode(sel);
46508         
46509         
46510         var selection = this.editorcore.getSelection();
46511         selection.removeAllRanges();
46512         selection.addRange(range);
46513         
46514         
46515         
46516         this.updateToolbar(null, null, sel);
46517         
46518         
46519     }
46520     
46521     
46522     
46523     
46524     
46525 });
46526
46527
46528
46529
46530
46531 /*
46532  * Based on:
46533  * Ext JS Library 1.1.1
46534  * Copyright(c) 2006-2007, Ext JS, LLC.
46535  *
46536  * Originally Released Under LGPL - original licence link has changed is not relivant.
46537  *
46538  * Fork - LGPL
46539  * <script type="text/javascript">
46540  */
46541  
46542 /**
46543  * @class Roo.form.BasicForm
46544  * @extends Roo.util.Observable
46545  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46546  * @constructor
46547  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46548  * @param {Object} config Configuration options
46549  */
46550 Roo.form.BasicForm = function(el, config){
46551     this.allItems = [];
46552     this.childForms = [];
46553     Roo.apply(this, config);
46554     /*
46555      * The Roo.form.Field items in this form.
46556      * @type MixedCollection
46557      */
46558      
46559      
46560     this.items = new Roo.util.MixedCollection(false, function(o){
46561         return o.id || (o.id = Roo.id());
46562     });
46563     this.addEvents({
46564         /**
46565          * @event beforeaction
46566          * Fires before any action is performed. Return false to cancel the action.
46567          * @param {Form} this
46568          * @param {Action} action The action to be performed
46569          */
46570         beforeaction: true,
46571         /**
46572          * @event actionfailed
46573          * Fires when an action fails.
46574          * @param {Form} this
46575          * @param {Action} action The action that failed
46576          */
46577         actionfailed : true,
46578         /**
46579          * @event actioncomplete
46580          * Fires when an action is completed.
46581          * @param {Form} this
46582          * @param {Action} action The action that completed
46583          */
46584         actioncomplete : true
46585     });
46586     if(el){
46587         this.initEl(el);
46588     }
46589     Roo.form.BasicForm.superclass.constructor.call(this);
46590 };
46591
46592 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46593     /**
46594      * @cfg {String} method
46595      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46596      */
46597     /**
46598      * @cfg {DataReader} reader
46599      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46600      * This is optional as there is built-in support for processing JSON.
46601      */
46602     /**
46603      * @cfg {DataReader} errorReader
46604      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46605      * This is completely optional as there is built-in support for processing JSON.
46606      */
46607     /**
46608      * @cfg {String} url
46609      * The URL to use for form actions if one isn't supplied in the action options.
46610      */
46611     /**
46612      * @cfg {Boolean} fileUpload
46613      * Set to true if this form is a file upload.
46614      */
46615      
46616     /**
46617      * @cfg {Object} baseParams
46618      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46619      */
46620      /**
46621      
46622     /**
46623      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46624      */
46625     timeout: 30,
46626
46627     // private
46628     activeAction : null,
46629
46630     /**
46631      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46632      * or setValues() data instead of when the form was first created.
46633      */
46634     trackResetOnLoad : false,
46635     
46636     
46637     /**
46638      * childForms - used for multi-tab forms
46639      * @type {Array}
46640      */
46641     childForms : false,
46642     
46643     /**
46644      * allItems - full list of fields.
46645      * @type {Array}
46646      */
46647     allItems : false,
46648     
46649     /**
46650      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46651      * element by passing it or its id or mask the form itself by passing in true.
46652      * @type Mixed
46653      */
46654     waitMsgTarget : false,
46655
46656     // private
46657     initEl : function(el){
46658         this.el = Roo.get(el);
46659         this.id = this.el.id || Roo.id();
46660         this.el.on('submit', this.onSubmit, this);
46661         this.el.addClass('x-form');
46662     },
46663
46664     // private
46665     onSubmit : function(e){
46666         e.stopEvent();
46667     },
46668
46669     /**
46670      * Returns true if client-side validation on the form is successful.
46671      * @return Boolean
46672      */
46673     isValid : function(){
46674         var valid = true;
46675         this.items.each(function(f){
46676            if(!f.validate()){
46677                valid = false;
46678            }
46679         });
46680         return valid;
46681     },
46682
46683     /**
46684      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46685      * @return Boolean
46686      */
46687     isDirty : function(){
46688         var dirty = false;
46689         this.items.each(function(f){
46690            if(f.isDirty()){
46691                dirty = true;
46692                return false;
46693            }
46694         });
46695         return dirty;
46696     },
46697     
46698     /**
46699      * Returns true if any fields in this form have changed since their original load. (New version)
46700      * @return Boolean
46701      */
46702     
46703     hasChanged : function()
46704     {
46705         var dirty = false;
46706         this.items.each(function(f){
46707            if(f.hasChanged()){
46708                dirty = true;
46709                return false;
46710            }
46711         });
46712         return dirty;
46713         
46714     },
46715     /**
46716      * Resets all hasChanged to 'false' -
46717      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46718      * So hasChanged storage is only to be used for this purpose
46719      * @return Boolean
46720      */
46721     resetHasChanged : function()
46722     {
46723         this.items.each(function(f){
46724            f.resetHasChanged();
46725         });
46726         
46727     },
46728     
46729     
46730     /**
46731      * Performs a predefined action (submit or load) or custom actions you define on this form.
46732      * @param {String} actionName The name of the action type
46733      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46734      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46735      * accept other config options):
46736      * <pre>
46737 Property          Type             Description
46738 ----------------  ---------------  ----------------------------------------------------------------------------------
46739 url               String           The url for the action (defaults to the form's url)
46740 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46741 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46742 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46743                                    validate the form on the client (defaults to false)
46744      * </pre>
46745      * @return {BasicForm} this
46746      */
46747     doAction : function(action, options){
46748         if(typeof action == 'string'){
46749             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46750         }
46751         if(this.fireEvent('beforeaction', this, action) !== false){
46752             this.beforeAction(action);
46753             action.run.defer(100, action);
46754         }
46755         return this;
46756     },
46757
46758     /**
46759      * Shortcut to do a submit action.
46760      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46761      * @return {BasicForm} this
46762      */
46763     submit : function(options){
46764         this.doAction('submit', options);
46765         return this;
46766     },
46767
46768     /**
46769      * Shortcut to do a load action.
46770      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46771      * @return {BasicForm} this
46772      */
46773     load : function(options){
46774         this.doAction('load', options);
46775         return this;
46776     },
46777
46778     /**
46779      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46780      * @param {Record} record The record to edit
46781      * @return {BasicForm} this
46782      */
46783     updateRecord : function(record){
46784         record.beginEdit();
46785         var fs = record.fields;
46786         fs.each(function(f){
46787             var field = this.findField(f.name);
46788             if(field){
46789                 record.set(f.name, field.getValue());
46790             }
46791         }, this);
46792         record.endEdit();
46793         return this;
46794     },
46795
46796     /**
46797      * Loads an Roo.data.Record into this form.
46798      * @param {Record} record The record to load
46799      * @return {BasicForm} this
46800      */
46801     loadRecord : function(record){
46802         this.setValues(record.data);
46803         return this;
46804     },
46805
46806     // private
46807     beforeAction : function(action){
46808         var o = action.options;
46809         
46810        
46811         if(this.waitMsgTarget === true){
46812             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46813         }else if(this.waitMsgTarget){
46814             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46815             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46816         }else {
46817             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46818         }
46819          
46820     },
46821
46822     // private
46823     afterAction : function(action, success){
46824         this.activeAction = null;
46825         var o = action.options;
46826         
46827         if(this.waitMsgTarget === true){
46828             this.el.unmask();
46829         }else if(this.waitMsgTarget){
46830             this.waitMsgTarget.unmask();
46831         }else{
46832             Roo.MessageBox.updateProgress(1);
46833             Roo.MessageBox.hide();
46834         }
46835          
46836         if(success){
46837             if(o.reset){
46838                 this.reset();
46839             }
46840             Roo.callback(o.success, o.scope, [this, action]);
46841             this.fireEvent('actioncomplete', this, action);
46842             
46843         }else{
46844             
46845             // failure condition..
46846             // we have a scenario where updates need confirming.
46847             // eg. if a locking scenario exists..
46848             // we look for { errors : { needs_confirm : true }} in the response.
46849             if (
46850                 (typeof(action.result) != 'undefined')  &&
46851                 (typeof(action.result.errors) != 'undefined')  &&
46852                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46853            ){
46854                 var _t = this;
46855                 Roo.MessageBox.confirm(
46856                     "Change requires confirmation",
46857                     action.result.errorMsg,
46858                     function(r) {
46859                         if (r != 'yes') {
46860                             return;
46861                         }
46862                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46863                     }
46864                     
46865                 );
46866                 
46867                 
46868                 
46869                 return;
46870             }
46871             
46872             Roo.callback(o.failure, o.scope, [this, action]);
46873             // show an error message if no failed handler is set..
46874             if (!this.hasListener('actionfailed')) {
46875                 Roo.MessageBox.alert("Error",
46876                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46877                         action.result.errorMsg :
46878                         "Saving Failed, please check your entries or try again"
46879                 );
46880             }
46881             
46882             this.fireEvent('actionfailed', this, action);
46883         }
46884         
46885     },
46886
46887     /**
46888      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46889      * @param {String} id The value to search for
46890      * @return Field
46891      */
46892     findField : function(id){
46893         var field = this.items.get(id);
46894         if(!field){
46895             this.items.each(function(f){
46896                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46897                     field = f;
46898                     return false;
46899                 }
46900             });
46901         }
46902         return field || null;
46903     },
46904
46905     /**
46906      * Add a secondary form to this one, 
46907      * Used to provide tabbed forms. One form is primary, with hidden values 
46908      * which mirror the elements from the other forms.
46909      * 
46910      * @param {Roo.form.Form} form to add.
46911      * 
46912      */
46913     addForm : function(form)
46914     {
46915        
46916         if (this.childForms.indexOf(form) > -1) {
46917             // already added..
46918             return;
46919         }
46920         this.childForms.push(form);
46921         var n = '';
46922         Roo.each(form.allItems, function (fe) {
46923             
46924             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46925             if (this.findField(n)) { // already added..
46926                 return;
46927             }
46928             var add = new Roo.form.Hidden({
46929                 name : n
46930             });
46931             add.render(this.el);
46932             
46933             this.add( add );
46934         }, this);
46935         
46936     },
46937     /**
46938      * Mark fields in this form invalid in bulk.
46939      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46940      * @return {BasicForm} this
46941      */
46942     markInvalid : function(errors){
46943         if(errors instanceof Array){
46944             for(var i = 0, len = errors.length; i < len; i++){
46945                 var fieldError = errors[i];
46946                 var f = this.findField(fieldError.id);
46947                 if(f){
46948                     f.markInvalid(fieldError.msg);
46949                 }
46950             }
46951         }else{
46952             var field, id;
46953             for(id in errors){
46954                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46955                     field.markInvalid(errors[id]);
46956                 }
46957             }
46958         }
46959         Roo.each(this.childForms || [], function (f) {
46960             f.markInvalid(errors);
46961         });
46962         
46963         return this;
46964     },
46965
46966     /**
46967      * Set values for fields in this form in bulk.
46968      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46969      * @return {BasicForm} this
46970      */
46971     setValues : function(values){
46972         if(values instanceof Array){ // array of objects
46973             for(var i = 0, len = values.length; i < len; i++){
46974                 var v = values[i];
46975                 var f = this.findField(v.id);
46976                 if(f){
46977                     f.setValue(v.value);
46978                     if(this.trackResetOnLoad){
46979                         f.originalValue = f.getValue();
46980                     }
46981                 }
46982             }
46983         }else{ // object hash
46984             var field, id;
46985             for(id in values){
46986                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46987                     
46988                     if (field.setFromData && 
46989                         field.valueField && 
46990                         field.displayField &&
46991                         // combos' with local stores can 
46992                         // be queried via setValue()
46993                         // to set their value..
46994                         (field.store && !field.store.isLocal)
46995                         ) {
46996                         // it's a combo
46997                         var sd = { };
46998                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46999                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47000                         field.setFromData(sd);
47001                         
47002                     } else {
47003                         field.setValue(values[id]);
47004                     }
47005                     
47006                     
47007                     if(this.trackResetOnLoad){
47008                         field.originalValue = field.getValue();
47009                     }
47010                 }
47011             }
47012         }
47013         this.resetHasChanged();
47014         
47015         
47016         Roo.each(this.childForms || [], function (f) {
47017             f.setValues(values);
47018             f.resetHasChanged();
47019         });
47020                 
47021         return this;
47022     },
47023
47024     /**
47025      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47026      * they are returned as an array.
47027      * @param {Boolean} asString
47028      * @return {Object}
47029      */
47030     getValues : function(asString){
47031         if (this.childForms) {
47032             // copy values from the child forms
47033             Roo.each(this.childForms, function (f) {
47034                 this.setValues(f.getValues());
47035             }, this);
47036         }
47037         
47038         
47039         
47040         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47041         if(asString === true){
47042             return fs;
47043         }
47044         return Roo.urlDecode(fs);
47045     },
47046     
47047     /**
47048      * Returns the fields in this form as an object with key/value pairs. 
47049      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47050      * @return {Object}
47051      */
47052     getFieldValues : function(with_hidden)
47053     {
47054         if (this.childForms) {
47055             // copy values from the child forms
47056             // should this call getFieldValues - probably not as we do not currently copy
47057             // hidden fields when we generate..
47058             Roo.each(this.childForms, function (f) {
47059                 this.setValues(f.getValues());
47060             }, this);
47061         }
47062         
47063         var ret = {};
47064         this.items.each(function(f){
47065             if (!f.getName()) {
47066                 return;
47067             }
47068             var v = f.getValue();
47069             if (f.inputType =='radio') {
47070                 if (typeof(ret[f.getName()]) == 'undefined') {
47071                     ret[f.getName()] = ''; // empty..
47072                 }
47073                 
47074                 if (!f.el.dom.checked) {
47075                     return;
47076                     
47077                 }
47078                 v = f.el.dom.value;
47079                 
47080             }
47081             
47082             // not sure if this supported any more..
47083             if ((typeof(v) == 'object') && f.getRawValue) {
47084                 v = f.getRawValue() ; // dates..
47085             }
47086             // combo boxes where name != hiddenName...
47087             if (f.name != f.getName()) {
47088                 ret[f.name] = f.getRawValue();
47089             }
47090             ret[f.getName()] = v;
47091         });
47092         
47093         return ret;
47094     },
47095
47096     /**
47097      * Clears all invalid messages in this form.
47098      * @return {BasicForm} this
47099      */
47100     clearInvalid : function(){
47101         this.items.each(function(f){
47102            f.clearInvalid();
47103         });
47104         
47105         Roo.each(this.childForms || [], function (f) {
47106             f.clearInvalid();
47107         });
47108         
47109         
47110         return this;
47111     },
47112
47113     /**
47114      * Resets this form.
47115      * @return {BasicForm} this
47116      */
47117     reset : function(){
47118         this.items.each(function(f){
47119             f.reset();
47120         });
47121         
47122         Roo.each(this.childForms || [], function (f) {
47123             f.reset();
47124         });
47125         this.resetHasChanged();
47126         
47127         return this;
47128     },
47129
47130     /**
47131      * Add Roo.form components to this form.
47132      * @param {Field} field1
47133      * @param {Field} field2 (optional)
47134      * @param {Field} etc (optional)
47135      * @return {BasicForm} this
47136      */
47137     add : function(){
47138         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47139         return this;
47140     },
47141
47142
47143     /**
47144      * Removes a field from the items collection (does NOT remove its markup).
47145      * @param {Field} field
47146      * @return {BasicForm} this
47147      */
47148     remove : function(field){
47149         this.items.remove(field);
47150         return this;
47151     },
47152
47153     /**
47154      * Looks at the fields in this form, checks them for an id attribute,
47155      * and calls applyTo on the existing dom element with that id.
47156      * @return {BasicForm} this
47157      */
47158     render : function(){
47159         this.items.each(function(f){
47160             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47161                 f.applyTo(f.id);
47162             }
47163         });
47164         return this;
47165     },
47166
47167     /**
47168      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47169      * @param {Object} values
47170      * @return {BasicForm} this
47171      */
47172     applyToFields : function(o){
47173         this.items.each(function(f){
47174            Roo.apply(f, o);
47175         });
47176         return this;
47177     },
47178
47179     /**
47180      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47181      * @param {Object} values
47182      * @return {BasicForm} this
47183      */
47184     applyIfToFields : function(o){
47185         this.items.each(function(f){
47186            Roo.applyIf(f, o);
47187         });
47188         return this;
47189     }
47190 });
47191
47192 // back compat
47193 Roo.BasicForm = Roo.form.BasicForm;/*
47194  * Based on:
47195  * Ext JS Library 1.1.1
47196  * Copyright(c) 2006-2007, Ext JS, LLC.
47197  *
47198  * Originally Released Under LGPL - original licence link has changed is not relivant.
47199  *
47200  * Fork - LGPL
47201  * <script type="text/javascript">
47202  */
47203
47204 /**
47205  * @class Roo.form.Form
47206  * @extends Roo.form.BasicForm
47207  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47208  * @constructor
47209  * @param {Object} config Configuration options
47210  */
47211 Roo.form.Form = function(config){
47212     var xitems =  [];
47213     if (config.items) {
47214         xitems = config.items;
47215         delete config.items;
47216     }
47217    
47218     
47219     Roo.form.Form.superclass.constructor.call(this, null, config);
47220     this.url = this.url || this.action;
47221     if(!this.root){
47222         this.root = new Roo.form.Layout(Roo.applyIf({
47223             id: Roo.id()
47224         }, config));
47225     }
47226     this.active = this.root;
47227     /**
47228      * Array of all the buttons that have been added to this form via {@link addButton}
47229      * @type Array
47230      */
47231     this.buttons = [];
47232     this.allItems = [];
47233     this.addEvents({
47234         /**
47235          * @event clientvalidation
47236          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47237          * @param {Form} this
47238          * @param {Boolean} valid true if the form has passed client-side validation
47239          */
47240         clientvalidation: true,
47241         /**
47242          * @event rendered
47243          * Fires when the form is rendered
47244          * @param {Roo.form.Form} form
47245          */
47246         rendered : true
47247     });
47248     
47249     if (this.progressUrl) {
47250             // push a hidden field onto the list of fields..
47251             this.addxtype( {
47252                     xns: Roo.form, 
47253                     xtype : 'Hidden', 
47254                     name : 'UPLOAD_IDENTIFIER' 
47255             });
47256         }
47257         
47258     
47259     Roo.each(xitems, this.addxtype, this);
47260     
47261     
47262     
47263 };
47264
47265 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47266     /**
47267      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47268      */
47269     /**
47270      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47271      */
47272     /**
47273      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47274      */
47275     buttonAlign:'center',
47276
47277     /**
47278      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47279      */
47280     minButtonWidth:75,
47281
47282     /**
47283      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47284      * This property cascades to child containers if not set.
47285      */
47286     labelAlign:'left',
47287
47288     /**
47289      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47290      * fires a looping event with that state. This is required to bind buttons to the valid
47291      * state using the config value formBind:true on the button.
47292      */
47293     monitorValid : false,
47294
47295     /**
47296      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47297      */
47298     monitorPoll : 200,
47299     
47300     /**
47301      * @cfg {String} progressUrl - Url to return progress data 
47302      */
47303     
47304     progressUrl : false,
47305   
47306     /**
47307      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47308      * fields are added and the column is closed. If no fields are passed the column remains open
47309      * until end() is called.
47310      * @param {Object} config The config to pass to the column
47311      * @param {Field} field1 (optional)
47312      * @param {Field} field2 (optional)
47313      * @param {Field} etc (optional)
47314      * @return Column The column container object
47315      */
47316     column : function(c){
47317         var col = new Roo.form.Column(c);
47318         this.start(col);
47319         if(arguments.length > 1){ // duplicate code required because of Opera
47320             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47321             this.end();
47322         }
47323         return col;
47324     },
47325
47326     /**
47327      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47328      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47329      * until end() is called.
47330      * @param {Object} config The config to pass to the fieldset
47331      * @param {Field} field1 (optional)
47332      * @param {Field} field2 (optional)
47333      * @param {Field} etc (optional)
47334      * @return FieldSet The fieldset container object
47335      */
47336     fieldset : function(c){
47337         var fs = new Roo.form.FieldSet(c);
47338         this.start(fs);
47339         if(arguments.length > 1){ // duplicate code required because of Opera
47340             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47341             this.end();
47342         }
47343         return fs;
47344     },
47345
47346     /**
47347      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47348      * fields are added and the container is closed. If no fields are passed the container remains open
47349      * until end() is called.
47350      * @param {Object} config The config to pass to the Layout
47351      * @param {Field} field1 (optional)
47352      * @param {Field} field2 (optional)
47353      * @param {Field} etc (optional)
47354      * @return Layout The container object
47355      */
47356     container : function(c){
47357         var l = new Roo.form.Layout(c);
47358         this.start(l);
47359         if(arguments.length > 1){ // duplicate code required because of Opera
47360             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47361             this.end();
47362         }
47363         return l;
47364     },
47365
47366     /**
47367      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47368      * @param {Object} container A Roo.form.Layout or subclass of Layout
47369      * @return {Form} this
47370      */
47371     start : function(c){
47372         // cascade label info
47373         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47374         this.active.stack.push(c);
47375         c.ownerCt = this.active;
47376         this.active = c;
47377         return this;
47378     },
47379
47380     /**
47381      * Closes the current open container
47382      * @return {Form} this
47383      */
47384     end : function(){
47385         if(this.active == this.root){
47386             return this;
47387         }
47388         this.active = this.active.ownerCt;
47389         return this;
47390     },
47391
47392     /**
47393      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47394      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47395      * as the label of the field.
47396      * @param {Field} field1
47397      * @param {Field} field2 (optional)
47398      * @param {Field} etc. (optional)
47399      * @return {Form} this
47400      */
47401     add : function(){
47402         this.active.stack.push.apply(this.active.stack, arguments);
47403         this.allItems.push.apply(this.allItems,arguments);
47404         var r = [];
47405         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47406             if(a[i].isFormField){
47407                 r.push(a[i]);
47408             }
47409         }
47410         if(r.length > 0){
47411             Roo.form.Form.superclass.add.apply(this, r);
47412         }
47413         return this;
47414     },
47415     
47416
47417     
47418     
47419     
47420      /**
47421      * Find any element that has been added to a form, using it's ID or name
47422      * This can include framesets, columns etc. along with regular fields..
47423      * @param {String} id - id or name to find.
47424      
47425      * @return {Element} e - or false if nothing found.
47426      */
47427     findbyId : function(id)
47428     {
47429         var ret = false;
47430         if (!id) {
47431             return ret;
47432         }
47433         Roo.each(this.allItems, function(f){
47434             if (f.id == id || f.name == id ){
47435                 ret = f;
47436                 return false;
47437             }
47438         });
47439         return ret;
47440     },
47441
47442     
47443     
47444     /**
47445      * Render this form into the passed container. This should only be called once!
47446      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47447      * @return {Form} this
47448      */
47449     render : function(ct)
47450     {
47451         
47452         
47453         
47454         ct = Roo.get(ct);
47455         var o = this.autoCreate || {
47456             tag: 'form',
47457             method : this.method || 'POST',
47458             id : this.id || Roo.id()
47459         };
47460         this.initEl(ct.createChild(o));
47461
47462         this.root.render(this.el);
47463         
47464        
47465              
47466         this.items.each(function(f){
47467             f.render('x-form-el-'+f.id);
47468         });
47469
47470         if(this.buttons.length > 0){
47471             // tables are required to maintain order and for correct IE layout
47472             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47473                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47474                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47475             }}, null, true);
47476             var tr = tb.getElementsByTagName('tr')[0];
47477             for(var i = 0, len = this.buttons.length; i < len; i++) {
47478                 var b = this.buttons[i];
47479                 var td = document.createElement('td');
47480                 td.className = 'x-form-btn-td';
47481                 b.render(tr.appendChild(td));
47482             }
47483         }
47484         if(this.monitorValid){ // initialize after render
47485             this.startMonitoring();
47486         }
47487         this.fireEvent('rendered', this);
47488         return this;
47489     },
47490
47491     /**
47492      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47493      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47494      * object or a valid Roo.DomHelper element config
47495      * @param {Function} handler The function called when the button is clicked
47496      * @param {Object} scope (optional) The scope of the handler function
47497      * @return {Roo.Button}
47498      */
47499     addButton : function(config, handler, scope){
47500         var bc = {
47501             handler: handler,
47502             scope: scope,
47503             minWidth: this.minButtonWidth,
47504             hideParent:true
47505         };
47506         if(typeof config == "string"){
47507             bc.text = config;
47508         }else{
47509             Roo.apply(bc, config);
47510         }
47511         var btn = new Roo.Button(null, bc);
47512         this.buttons.push(btn);
47513         return btn;
47514     },
47515
47516      /**
47517      * Adds a series of form elements (using the xtype property as the factory method.
47518      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47519      * @param {Object} config 
47520      */
47521     
47522     addxtype : function()
47523     {
47524         var ar = Array.prototype.slice.call(arguments, 0);
47525         var ret = false;
47526         for(var i = 0; i < ar.length; i++) {
47527             if (!ar[i]) {
47528                 continue; // skip -- if this happends something invalid got sent, we 
47529                 // should ignore it, as basically that interface element will not show up
47530                 // and that should be pretty obvious!!
47531             }
47532             
47533             if (Roo.form[ar[i].xtype]) {
47534                 ar[i].form = this;
47535                 var fe = Roo.factory(ar[i], Roo.form);
47536                 if (!ret) {
47537                     ret = fe;
47538                 }
47539                 fe.form = this;
47540                 if (fe.store) {
47541                     fe.store.form = this;
47542                 }
47543                 if (fe.isLayout) {  
47544                          
47545                     this.start(fe);
47546                     this.allItems.push(fe);
47547                     if (fe.items && fe.addxtype) {
47548                         fe.addxtype.apply(fe, fe.items);
47549                         delete fe.items;
47550                     }
47551                      this.end();
47552                     continue;
47553                 }
47554                 
47555                 
47556                  
47557                 this.add(fe);
47558               //  console.log('adding ' + ar[i].xtype);
47559             }
47560             if (ar[i].xtype == 'Button') {  
47561                 //console.log('adding button');
47562                 //console.log(ar[i]);
47563                 this.addButton(ar[i]);
47564                 this.allItems.push(fe);
47565                 continue;
47566             }
47567             
47568             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47569                 alert('end is not supported on xtype any more, use items');
47570             //    this.end();
47571             //    //console.log('adding end');
47572             }
47573             
47574         }
47575         return ret;
47576     },
47577     
47578     /**
47579      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47580      * option "monitorValid"
47581      */
47582     startMonitoring : function(){
47583         if(!this.bound){
47584             this.bound = true;
47585             Roo.TaskMgr.start({
47586                 run : this.bindHandler,
47587                 interval : this.monitorPoll || 200,
47588                 scope: this
47589             });
47590         }
47591     },
47592
47593     /**
47594      * Stops monitoring of the valid state of this form
47595      */
47596     stopMonitoring : function(){
47597         this.bound = false;
47598     },
47599
47600     // private
47601     bindHandler : function(){
47602         if(!this.bound){
47603             return false; // stops binding
47604         }
47605         var valid = true;
47606         this.items.each(function(f){
47607             if(!f.isValid(true)){
47608                 valid = false;
47609                 return false;
47610             }
47611         });
47612         for(var i = 0, len = this.buttons.length; i < len; i++){
47613             var btn = this.buttons[i];
47614             if(btn.formBind === true && btn.disabled === valid){
47615                 btn.setDisabled(!valid);
47616             }
47617         }
47618         this.fireEvent('clientvalidation', this, valid);
47619     }
47620     
47621     
47622     
47623     
47624     
47625     
47626     
47627     
47628 });
47629
47630
47631 // back compat
47632 Roo.Form = Roo.form.Form;
47633 /*
47634  * Based on:
47635  * Ext JS Library 1.1.1
47636  * Copyright(c) 2006-2007, Ext JS, LLC.
47637  *
47638  * Originally Released Under LGPL - original licence link has changed is not relivant.
47639  *
47640  * Fork - LGPL
47641  * <script type="text/javascript">
47642  */
47643
47644 // as we use this in bootstrap.
47645 Roo.namespace('Roo.form');
47646  /**
47647  * @class Roo.form.Action
47648  * Internal Class used to handle form actions
47649  * @constructor
47650  * @param {Roo.form.BasicForm} el The form element or its id
47651  * @param {Object} config Configuration options
47652  */
47653
47654  
47655  
47656 // define the action interface
47657 Roo.form.Action = function(form, options){
47658     this.form = form;
47659     this.options = options || {};
47660 };
47661 /**
47662  * Client Validation Failed
47663  * @const 
47664  */
47665 Roo.form.Action.CLIENT_INVALID = 'client';
47666 /**
47667  * Server Validation Failed
47668  * @const 
47669  */
47670 Roo.form.Action.SERVER_INVALID = 'server';
47671  /**
47672  * Connect to Server Failed
47673  * @const 
47674  */
47675 Roo.form.Action.CONNECT_FAILURE = 'connect';
47676 /**
47677  * Reading Data from Server Failed
47678  * @const 
47679  */
47680 Roo.form.Action.LOAD_FAILURE = 'load';
47681
47682 Roo.form.Action.prototype = {
47683     type : 'default',
47684     failureType : undefined,
47685     response : undefined,
47686     result : undefined,
47687
47688     // interface method
47689     run : function(options){
47690
47691     },
47692
47693     // interface method
47694     success : function(response){
47695
47696     },
47697
47698     // interface method
47699     handleResponse : function(response){
47700
47701     },
47702
47703     // default connection failure
47704     failure : function(response){
47705         
47706         this.response = response;
47707         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47708         this.form.afterAction(this, false);
47709     },
47710
47711     processResponse : function(response){
47712         this.response = response;
47713         if(!response.responseText){
47714             return true;
47715         }
47716         this.result = this.handleResponse(response);
47717         return this.result;
47718     },
47719
47720     // utility functions used internally
47721     getUrl : function(appendParams){
47722         var url = this.options.url || this.form.url || this.form.el.dom.action;
47723         if(appendParams){
47724             var p = this.getParams();
47725             if(p){
47726                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47727             }
47728         }
47729         return url;
47730     },
47731
47732     getMethod : function(){
47733         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47734     },
47735
47736     getParams : function(){
47737         var bp = this.form.baseParams;
47738         var p = this.options.params;
47739         if(p){
47740             if(typeof p == "object"){
47741                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47742             }else if(typeof p == 'string' && bp){
47743                 p += '&' + Roo.urlEncode(bp);
47744             }
47745         }else if(bp){
47746             p = Roo.urlEncode(bp);
47747         }
47748         return p;
47749     },
47750
47751     createCallback : function(){
47752         return {
47753             success: this.success,
47754             failure: this.failure,
47755             scope: this,
47756             timeout: (this.form.timeout*1000),
47757             upload: this.form.fileUpload ? this.success : undefined
47758         };
47759     }
47760 };
47761
47762 Roo.form.Action.Submit = function(form, options){
47763     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47764 };
47765
47766 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47767     type : 'submit',
47768
47769     haveProgress : false,
47770     uploadComplete : false,
47771     
47772     // uploadProgress indicator.
47773     uploadProgress : function()
47774     {
47775         if (!this.form.progressUrl) {
47776             return;
47777         }
47778         
47779         if (!this.haveProgress) {
47780             Roo.MessageBox.progress("Uploading", "Uploading");
47781         }
47782         if (this.uploadComplete) {
47783            Roo.MessageBox.hide();
47784            return;
47785         }
47786         
47787         this.haveProgress = true;
47788    
47789         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47790         
47791         var c = new Roo.data.Connection();
47792         c.request({
47793             url : this.form.progressUrl,
47794             params: {
47795                 id : uid
47796             },
47797             method: 'GET',
47798             success : function(req){
47799                //console.log(data);
47800                 var rdata = false;
47801                 var edata;
47802                 try  {
47803                    rdata = Roo.decode(req.responseText)
47804                 } catch (e) {
47805                     Roo.log("Invalid data from server..");
47806                     Roo.log(edata);
47807                     return;
47808                 }
47809                 if (!rdata || !rdata.success) {
47810                     Roo.log(rdata);
47811                     Roo.MessageBox.alert(Roo.encode(rdata));
47812                     return;
47813                 }
47814                 var data = rdata.data;
47815                 
47816                 if (this.uploadComplete) {
47817                    Roo.MessageBox.hide();
47818                    return;
47819                 }
47820                    
47821                 if (data){
47822                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47823                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47824                     );
47825                 }
47826                 this.uploadProgress.defer(2000,this);
47827             },
47828        
47829             failure: function(data) {
47830                 Roo.log('progress url failed ');
47831                 Roo.log(data);
47832             },
47833             scope : this
47834         });
47835            
47836     },
47837     
47838     
47839     run : function()
47840     {
47841         // run get Values on the form, so it syncs any secondary forms.
47842         this.form.getValues();
47843         
47844         var o = this.options;
47845         var method = this.getMethod();
47846         var isPost = method == 'POST';
47847         if(o.clientValidation === false || this.form.isValid()){
47848             
47849             if (this.form.progressUrl) {
47850                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47851                     (new Date() * 1) + '' + Math.random());
47852                     
47853             } 
47854             
47855             
47856             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47857                 form:this.form.el.dom,
47858                 url:this.getUrl(!isPost),
47859                 method: method,
47860                 params:isPost ? this.getParams() : null,
47861                 isUpload: this.form.fileUpload
47862             }));
47863             
47864             this.uploadProgress();
47865
47866         }else if (o.clientValidation !== false){ // client validation failed
47867             this.failureType = Roo.form.Action.CLIENT_INVALID;
47868             this.form.afterAction(this, false);
47869         }
47870     },
47871
47872     success : function(response)
47873     {
47874         this.uploadComplete= true;
47875         if (this.haveProgress) {
47876             Roo.MessageBox.hide();
47877         }
47878         
47879         
47880         var result = this.processResponse(response);
47881         if(result === true || result.success){
47882             this.form.afterAction(this, true);
47883             return;
47884         }
47885         if(result.errors){
47886             this.form.markInvalid(result.errors);
47887             this.failureType = Roo.form.Action.SERVER_INVALID;
47888         }
47889         this.form.afterAction(this, false);
47890     },
47891     failure : function(response)
47892     {
47893         this.uploadComplete= true;
47894         if (this.haveProgress) {
47895             Roo.MessageBox.hide();
47896         }
47897         
47898         this.response = response;
47899         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47900         this.form.afterAction(this, false);
47901     },
47902     
47903     handleResponse : function(response){
47904         if(this.form.errorReader){
47905             var rs = this.form.errorReader.read(response);
47906             var errors = [];
47907             if(rs.records){
47908                 for(var i = 0, len = rs.records.length; i < len; i++) {
47909                     var r = rs.records[i];
47910                     errors[i] = r.data;
47911                 }
47912             }
47913             if(errors.length < 1){
47914                 errors = null;
47915             }
47916             return {
47917                 success : rs.success,
47918                 errors : errors
47919             };
47920         }
47921         var ret = false;
47922         try {
47923             ret = Roo.decode(response.responseText);
47924         } catch (e) {
47925             ret = {
47926                 success: false,
47927                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47928                 errors : []
47929             };
47930         }
47931         return ret;
47932         
47933     }
47934 });
47935
47936
47937 Roo.form.Action.Load = function(form, options){
47938     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47939     this.reader = this.form.reader;
47940 };
47941
47942 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47943     type : 'load',
47944
47945     run : function(){
47946         
47947         Roo.Ajax.request(Roo.apply(
47948                 this.createCallback(), {
47949                     method:this.getMethod(),
47950                     url:this.getUrl(false),
47951                     params:this.getParams()
47952         }));
47953     },
47954
47955     success : function(response){
47956         
47957         var result = this.processResponse(response);
47958         if(result === true || !result.success || !result.data){
47959             this.failureType = Roo.form.Action.LOAD_FAILURE;
47960             this.form.afterAction(this, false);
47961             return;
47962         }
47963         this.form.clearInvalid();
47964         this.form.setValues(result.data);
47965         this.form.afterAction(this, true);
47966     },
47967
47968     handleResponse : function(response){
47969         if(this.form.reader){
47970             var rs = this.form.reader.read(response);
47971             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47972             return {
47973                 success : rs.success,
47974                 data : data
47975             };
47976         }
47977         return Roo.decode(response.responseText);
47978     }
47979 });
47980
47981 Roo.form.Action.ACTION_TYPES = {
47982     'load' : Roo.form.Action.Load,
47983     'submit' : Roo.form.Action.Submit
47984 };/*
47985  * Based on:
47986  * Ext JS Library 1.1.1
47987  * Copyright(c) 2006-2007, Ext JS, LLC.
47988  *
47989  * Originally Released Under LGPL - original licence link has changed is not relivant.
47990  *
47991  * Fork - LGPL
47992  * <script type="text/javascript">
47993  */
47994  
47995 /**
47996  * @class Roo.form.Layout
47997  * @extends Roo.Component
47998  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47999  * @constructor
48000  * @param {Object} config Configuration options
48001  */
48002 Roo.form.Layout = function(config){
48003     var xitems = [];
48004     if (config.items) {
48005         xitems = config.items;
48006         delete config.items;
48007     }
48008     Roo.form.Layout.superclass.constructor.call(this, config);
48009     this.stack = [];
48010     Roo.each(xitems, this.addxtype, this);
48011      
48012 };
48013
48014 Roo.extend(Roo.form.Layout, Roo.Component, {
48015     /**
48016      * @cfg {String/Object} autoCreate
48017      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48018      */
48019     /**
48020      * @cfg {String/Object/Function} style
48021      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48022      * a function which returns such a specification.
48023      */
48024     /**
48025      * @cfg {String} labelAlign
48026      * Valid values are "left," "top" and "right" (defaults to "left")
48027      */
48028     /**
48029      * @cfg {Number} labelWidth
48030      * Fixed width in pixels of all field labels (defaults to undefined)
48031      */
48032     /**
48033      * @cfg {Boolean} clear
48034      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48035      */
48036     clear : true,
48037     /**
48038      * @cfg {String} labelSeparator
48039      * The separator to use after field labels (defaults to ':')
48040      */
48041     labelSeparator : ':',
48042     /**
48043      * @cfg {Boolean} hideLabels
48044      * True to suppress the display of field labels in this layout (defaults to false)
48045      */
48046     hideLabels : false,
48047
48048     // private
48049     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48050     
48051     isLayout : true,
48052     
48053     // private
48054     onRender : function(ct, position){
48055         if(this.el){ // from markup
48056             this.el = Roo.get(this.el);
48057         }else {  // generate
48058             var cfg = this.getAutoCreate();
48059             this.el = ct.createChild(cfg, position);
48060         }
48061         if(this.style){
48062             this.el.applyStyles(this.style);
48063         }
48064         if(this.labelAlign){
48065             this.el.addClass('x-form-label-'+this.labelAlign);
48066         }
48067         if(this.hideLabels){
48068             this.labelStyle = "display:none";
48069             this.elementStyle = "padding-left:0;";
48070         }else{
48071             if(typeof this.labelWidth == 'number'){
48072                 this.labelStyle = "width:"+this.labelWidth+"px;";
48073                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48074             }
48075             if(this.labelAlign == 'top'){
48076                 this.labelStyle = "width:auto;";
48077                 this.elementStyle = "padding-left:0;";
48078             }
48079         }
48080         var stack = this.stack;
48081         var slen = stack.length;
48082         if(slen > 0){
48083             if(!this.fieldTpl){
48084                 var t = new Roo.Template(
48085                     '<div class="x-form-item {5}">',
48086                         '<label for="{0}" style="{2}">{1}{4}</label>',
48087                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48088                         '</div>',
48089                     '</div><div class="x-form-clear-left"></div>'
48090                 );
48091                 t.disableFormats = true;
48092                 t.compile();
48093                 Roo.form.Layout.prototype.fieldTpl = t;
48094             }
48095             for(var i = 0; i < slen; i++) {
48096                 if(stack[i].isFormField){
48097                     this.renderField(stack[i]);
48098                 }else{
48099                     this.renderComponent(stack[i]);
48100                 }
48101             }
48102         }
48103         if(this.clear){
48104             this.el.createChild({cls:'x-form-clear'});
48105         }
48106     },
48107
48108     // private
48109     renderField : function(f){
48110         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48111                f.id, //0
48112                f.fieldLabel, //1
48113                f.labelStyle||this.labelStyle||'', //2
48114                this.elementStyle||'', //3
48115                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48116                f.itemCls||this.itemCls||''  //5
48117        ], true).getPrevSibling());
48118     },
48119
48120     // private
48121     renderComponent : function(c){
48122         c.render(c.isLayout ? this.el : this.el.createChild());    
48123     },
48124     /**
48125      * Adds a object form elements (using the xtype property as the factory method.)
48126      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48127      * @param {Object} config 
48128      */
48129     addxtype : function(o)
48130     {
48131         // create the lement.
48132         o.form = this.form;
48133         var fe = Roo.factory(o, Roo.form);
48134         this.form.allItems.push(fe);
48135         this.stack.push(fe);
48136         
48137         if (fe.isFormField) {
48138             this.form.items.add(fe);
48139         }
48140          
48141         return fe;
48142     }
48143 });
48144
48145 /**
48146  * @class Roo.form.Column
48147  * @extends Roo.form.Layout
48148  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48149  * @constructor
48150  * @param {Object} config Configuration options
48151  */
48152 Roo.form.Column = function(config){
48153     Roo.form.Column.superclass.constructor.call(this, config);
48154 };
48155
48156 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48157     /**
48158      * @cfg {Number/String} width
48159      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48160      */
48161     /**
48162      * @cfg {String/Object} autoCreate
48163      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48164      */
48165
48166     // private
48167     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48168
48169     // private
48170     onRender : function(ct, position){
48171         Roo.form.Column.superclass.onRender.call(this, ct, position);
48172         if(this.width){
48173             this.el.setWidth(this.width);
48174         }
48175     }
48176 });
48177
48178
48179 /**
48180  * @class Roo.form.Row
48181  * @extends Roo.form.Layout
48182  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48183  * @constructor
48184  * @param {Object} config Configuration options
48185  */
48186
48187  
48188 Roo.form.Row = function(config){
48189     Roo.form.Row.superclass.constructor.call(this, config);
48190 };
48191  
48192 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48193       /**
48194      * @cfg {Number/String} width
48195      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48196      */
48197     /**
48198      * @cfg {Number/String} height
48199      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48200      */
48201     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48202     
48203     padWidth : 20,
48204     // private
48205     onRender : function(ct, position){
48206         //console.log('row render');
48207         if(!this.rowTpl){
48208             var t = new Roo.Template(
48209                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48210                     '<label for="{0}" style="{2}">{1}{4}</label>',
48211                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48212                     '</div>',
48213                 '</div>'
48214             );
48215             t.disableFormats = true;
48216             t.compile();
48217             Roo.form.Layout.prototype.rowTpl = t;
48218         }
48219         this.fieldTpl = this.rowTpl;
48220         
48221         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48222         var labelWidth = 100;
48223         
48224         if ((this.labelAlign != 'top')) {
48225             if (typeof this.labelWidth == 'number') {
48226                 labelWidth = this.labelWidth
48227             }
48228             this.padWidth =  20 + labelWidth;
48229             
48230         }
48231         
48232         Roo.form.Column.superclass.onRender.call(this, ct, position);
48233         if(this.width){
48234             this.el.setWidth(this.width);
48235         }
48236         if(this.height){
48237             this.el.setHeight(this.height);
48238         }
48239     },
48240     
48241     // private
48242     renderField : function(f){
48243         f.fieldEl = this.fieldTpl.append(this.el, [
48244                f.id, f.fieldLabel,
48245                f.labelStyle||this.labelStyle||'',
48246                this.elementStyle||'',
48247                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48248                f.itemCls||this.itemCls||'',
48249                f.width ? f.width + this.padWidth : 160 + this.padWidth
48250        ],true);
48251     }
48252 });
48253  
48254
48255 /**
48256  * @class Roo.form.FieldSet
48257  * @extends Roo.form.Layout
48258  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48259  * @constructor
48260  * @param {Object} config Configuration options
48261  */
48262 Roo.form.FieldSet = function(config){
48263     Roo.form.FieldSet.superclass.constructor.call(this, config);
48264 };
48265
48266 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48267     /**
48268      * @cfg {String} legend
48269      * The text to display as the legend for the FieldSet (defaults to '')
48270      */
48271     /**
48272      * @cfg {String/Object} autoCreate
48273      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48274      */
48275
48276     // private
48277     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48278
48279     // private
48280     onRender : function(ct, position){
48281         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48282         if(this.legend){
48283             this.setLegend(this.legend);
48284         }
48285     },
48286
48287     // private
48288     setLegend : function(text){
48289         if(this.rendered){
48290             this.el.child('legend').update(text);
48291         }
48292     }
48293 });/*
48294  * Based on:
48295  * Ext JS Library 1.1.1
48296  * Copyright(c) 2006-2007, Ext JS, LLC.
48297  *
48298  * Originally Released Under LGPL - original licence link has changed is not relivant.
48299  *
48300  * Fork - LGPL
48301  * <script type="text/javascript">
48302  */
48303 /**
48304  * @class Roo.form.VTypes
48305  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48306  * @singleton
48307  */
48308 Roo.form.VTypes = function(){
48309     // closure these in so they are only created once.
48310     var alpha = /^[a-zA-Z_]+$/;
48311     var alphanum = /^[a-zA-Z0-9_]+$/;
48312     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48313     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48314
48315     // All these messages and functions are configurable
48316     return {
48317         /**
48318          * The function used to validate email addresses
48319          * @param {String} value The email address
48320          */
48321         'email' : function(v){
48322             return email.test(v);
48323         },
48324         /**
48325          * The error text to display when the email validation function returns false
48326          * @type String
48327          */
48328         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48329         /**
48330          * The keystroke filter mask to be applied on email input
48331          * @type RegExp
48332          */
48333         'emailMask' : /[a-z0-9_\.\-@]/i,
48334
48335         /**
48336          * The function used to validate URLs
48337          * @param {String} value The URL
48338          */
48339         'url' : function(v){
48340             return url.test(v);
48341         },
48342         /**
48343          * The error text to display when the url validation function returns false
48344          * @type String
48345          */
48346         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48347         
48348         /**
48349          * The function used to validate alpha values
48350          * @param {String} value The value
48351          */
48352         'alpha' : function(v){
48353             return alpha.test(v);
48354         },
48355         /**
48356          * The error text to display when the alpha validation function returns false
48357          * @type String
48358          */
48359         'alphaText' : 'This field should only contain letters and _',
48360         /**
48361          * The keystroke filter mask to be applied on alpha input
48362          * @type RegExp
48363          */
48364         'alphaMask' : /[a-z_]/i,
48365
48366         /**
48367          * The function used to validate alphanumeric values
48368          * @param {String} value The value
48369          */
48370         'alphanum' : function(v){
48371             return alphanum.test(v);
48372         },
48373         /**
48374          * The error text to display when the alphanumeric validation function returns false
48375          * @type String
48376          */
48377         'alphanumText' : 'This field should only contain letters, numbers and _',
48378         /**
48379          * The keystroke filter mask to be applied on alphanumeric input
48380          * @type RegExp
48381          */
48382         'alphanumMask' : /[a-z0-9_]/i
48383     };
48384 }();//<script type="text/javascript">
48385
48386 /**
48387  * @class Roo.form.FCKeditor
48388  * @extends Roo.form.TextArea
48389  * Wrapper around the FCKEditor http://www.fckeditor.net
48390  * @constructor
48391  * Creates a new FCKeditor
48392  * @param {Object} config Configuration options
48393  */
48394 Roo.form.FCKeditor = function(config){
48395     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48396     this.addEvents({
48397          /**
48398          * @event editorinit
48399          * Fired when the editor is initialized - you can add extra handlers here..
48400          * @param {FCKeditor} this
48401          * @param {Object} the FCK object.
48402          */
48403         editorinit : true
48404     });
48405     
48406     
48407 };
48408 Roo.form.FCKeditor.editors = { };
48409 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48410 {
48411     //defaultAutoCreate : {
48412     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48413     //},
48414     // private
48415     /**
48416      * @cfg {Object} fck options - see fck manual for details.
48417      */
48418     fckconfig : false,
48419     
48420     /**
48421      * @cfg {Object} fck toolbar set (Basic or Default)
48422      */
48423     toolbarSet : 'Basic',
48424     /**
48425      * @cfg {Object} fck BasePath
48426      */ 
48427     basePath : '/fckeditor/',
48428     
48429     
48430     frame : false,
48431     
48432     value : '',
48433     
48434    
48435     onRender : function(ct, position)
48436     {
48437         if(!this.el){
48438             this.defaultAutoCreate = {
48439                 tag: "textarea",
48440                 style:"width:300px;height:60px;",
48441                 autocomplete: "new-password"
48442             };
48443         }
48444         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48445         /*
48446         if(this.grow){
48447             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48448             if(this.preventScrollbars){
48449                 this.el.setStyle("overflow", "hidden");
48450             }
48451             this.el.setHeight(this.growMin);
48452         }
48453         */
48454         //console.log('onrender' + this.getId() );
48455         Roo.form.FCKeditor.editors[this.getId()] = this;
48456          
48457
48458         this.replaceTextarea() ;
48459         
48460     },
48461     
48462     getEditor : function() {
48463         return this.fckEditor;
48464     },
48465     /**
48466      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48467      * @param {Mixed} value The value to set
48468      */
48469     
48470     
48471     setValue : function(value)
48472     {
48473         //console.log('setValue: ' + value);
48474         
48475         if(typeof(value) == 'undefined') { // not sure why this is happending...
48476             return;
48477         }
48478         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48479         
48480         //if(!this.el || !this.getEditor()) {
48481         //    this.value = value;
48482             //this.setValue.defer(100,this,[value]);    
48483         //    return;
48484         //} 
48485         
48486         if(!this.getEditor()) {
48487             return;
48488         }
48489         
48490         this.getEditor().SetData(value);
48491         
48492         //
48493
48494     },
48495
48496     /**
48497      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48498      * @return {Mixed} value The field value
48499      */
48500     getValue : function()
48501     {
48502         
48503         if (this.frame && this.frame.dom.style.display == 'none') {
48504             return Roo.form.FCKeditor.superclass.getValue.call(this);
48505         }
48506         
48507         if(!this.el || !this.getEditor()) {
48508            
48509            // this.getValue.defer(100,this); 
48510             return this.value;
48511         }
48512        
48513         
48514         var value=this.getEditor().GetData();
48515         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48516         return Roo.form.FCKeditor.superclass.getValue.call(this);
48517         
48518
48519     },
48520
48521     /**
48522      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48523      * @return {Mixed} value The field value
48524      */
48525     getRawValue : function()
48526     {
48527         if (this.frame && this.frame.dom.style.display == 'none') {
48528             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48529         }
48530         
48531         if(!this.el || !this.getEditor()) {
48532             //this.getRawValue.defer(100,this); 
48533             return this.value;
48534             return;
48535         }
48536         
48537         
48538         
48539         var value=this.getEditor().GetData();
48540         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48541         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48542          
48543     },
48544     
48545     setSize : function(w,h) {
48546         
48547         
48548         
48549         //if (this.frame && this.frame.dom.style.display == 'none') {
48550         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48551         //    return;
48552         //}
48553         //if(!this.el || !this.getEditor()) {
48554         //    this.setSize.defer(100,this, [w,h]); 
48555         //    return;
48556         //}
48557         
48558         
48559         
48560         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48561         
48562         this.frame.dom.setAttribute('width', w);
48563         this.frame.dom.setAttribute('height', h);
48564         this.frame.setSize(w,h);
48565         
48566     },
48567     
48568     toggleSourceEdit : function(value) {
48569         
48570       
48571          
48572         this.el.dom.style.display = value ? '' : 'none';
48573         this.frame.dom.style.display = value ?  'none' : '';
48574         
48575     },
48576     
48577     
48578     focus: function(tag)
48579     {
48580         if (this.frame.dom.style.display == 'none') {
48581             return Roo.form.FCKeditor.superclass.focus.call(this);
48582         }
48583         if(!this.el || !this.getEditor()) {
48584             this.focus.defer(100,this, [tag]); 
48585             return;
48586         }
48587         
48588         
48589         
48590         
48591         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48592         this.getEditor().Focus();
48593         if (tgs.length) {
48594             if (!this.getEditor().Selection.GetSelection()) {
48595                 this.focus.defer(100,this, [tag]); 
48596                 return;
48597             }
48598             
48599             
48600             var r = this.getEditor().EditorDocument.createRange();
48601             r.setStart(tgs[0],0);
48602             r.setEnd(tgs[0],0);
48603             this.getEditor().Selection.GetSelection().removeAllRanges();
48604             this.getEditor().Selection.GetSelection().addRange(r);
48605             this.getEditor().Focus();
48606         }
48607         
48608     },
48609     
48610     
48611     
48612     replaceTextarea : function()
48613     {
48614         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48615             return ;
48616         }
48617         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48618         //{
48619             // We must check the elements firstly using the Id and then the name.
48620         var oTextarea = document.getElementById( this.getId() );
48621         
48622         var colElementsByName = document.getElementsByName( this.getId() ) ;
48623          
48624         oTextarea.style.display = 'none' ;
48625
48626         if ( oTextarea.tabIndex ) {            
48627             this.TabIndex = oTextarea.tabIndex ;
48628         }
48629         
48630         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48631         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48632         this.frame = Roo.get(this.getId() + '___Frame')
48633     },
48634     
48635     _getConfigHtml : function()
48636     {
48637         var sConfig = '' ;
48638
48639         for ( var o in this.fckconfig ) {
48640             sConfig += sConfig.length > 0  ? '&amp;' : '';
48641             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48642         }
48643
48644         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48645     },
48646     
48647     
48648     _getIFrameHtml : function()
48649     {
48650         var sFile = 'fckeditor.html' ;
48651         /* no idea what this is about..
48652         try
48653         {
48654             if ( (/fcksource=true/i).test( window.top.location.search ) )
48655                 sFile = 'fckeditor.original.html' ;
48656         }
48657         catch (e) { 
48658         */
48659
48660         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48661         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48662         
48663         
48664         var html = '<iframe id="' + this.getId() +
48665             '___Frame" src="' + sLink +
48666             '" width="' + this.width +
48667             '" height="' + this.height + '"' +
48668             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48669             ' frameborder="0" scrolling="no"></iframe>' ;
48670
48671         return html ;
48672     },
48673     
48674     _insertHtmlBefore : function( html, element )
48675     {
48676         if ( element.insertAdjacentHTML )       {
48677             // IE
48678             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48679         } else { // Gecko
48680             var oRange = document.createRange() ;
48681             oRange.setStartBefore( element ) ;
48682             var oFragment = oRange.createContextualFragment( html );
48683             element.parentNode.insertBefore( oFragment, element ) ;
48684         }
48685     }
48686     
48687     
48688   
48689     
48690     
48691     
48692     
48693
48694 });
48695
48696 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48697
48698 function FCKeditor_OnComplete(editorInstance){
48699     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48700     f.fckEditor = editorInstance;
48701     //console.log("loaded");
48702     f.fireEvent('editorinit', f, editorInstance);
48703
48704   
48705
48706  
48707
48708
48709
48710
48711
48712
48713
48714
48715
48716
48717
48718
48719
48720
48721
48722 //<script type="text/javascript">
48723 /**
48724  * @class Roo.form.GridField
48725  * @extends Roo.form.Field
48726  * Embed a grid (or editable grid into a form)
48727  * STATUS ALPHA
48728  * 
48729  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48730  * it needs 
48731  * xgrid.store = Roo.data.Store
48732  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48733  * xgrid.store.reader = Roo.data.JsonReader 
48734  * 
48735  * 
48736  * @constructor
48737  * Creates a new GridField
48738  * @param {Object} config Configuration options
48739  */
48740 Roo.form.GridField = function(config){
48741     Roo.form.GridField.superclass.constructor.call(this, config);
48742      
48743 };
48744
48745 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48746     /**
48747      * @cfg {Number} width  - used to restrict width of grid..
48748      */
48749     width : 100,
48750     /**
48751      * @cfg {Number} height - used to restrict height of grid..
48752      */
48753     height : 50,
48754      /**
48755      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48756          * 
48757          *}
48758      */
48759     xgrid : false, 
48760     /**
48761      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48762      * {tag: "input", type: "checkbox", autocomplete: "off"})
48763      */
48764    // defaultAutoCreate : { tag: 'div' },
48765     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48766     /**
48767      * @cfg {String} addTitle Text to include for adding a title.
48768      */
48769     addTitle : false,
48770     //
48771     onResize : function(){
48772         Roo.form.Field.superclass.onResize.apply(this, arguments);
48773     },
48774
48775     initEvents : function(){
48776         // Roo.form.Checkbox.superclass.initEvents.call(this);
48777         // has no events...
48778        
48779     },
48780
48781
48782     getResizeEl : function(){
48783         return this.wrap;
48784     },
48785
48786     getPositionEl : function(){
48787         return this.wrap;
48788     },
48789
48790     // private
48791     onRender : function(ct, position){
48792         
48793         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48794         var style = this.style;
48795         delete this.style;
48796         
48797         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48798         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48799         this.viewEl = this.wrap.createChild({ tag: 'div' });
48800         if (style) {
48801             this.viewEl.applyStyles(style);
48802         }
48803         if (this.width) {
48804             this.viewEl.setWidth(this.width);
48805         }
48806         if (this.height) {
48807             this.viewEl.setHeight(this.height);
48808         }
48809         //if(this.inputValue !== undefined){
48810         //this.setValue(this.value);
48811         
48812         
48813         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48814         
48815         
48816         this.grid.render();
48817         this.grid.getDataSource().on('remove', this.refreshValue, this);
48818         this.grid.getDataSource().on('update', this.refreshValue, this);
48819         this.grid.on('afteredit', this.refreshValue, this);
48820  
48821     },
48822      
48823     
48824     /**
48825      * Sets the value of the item. 
48826      * @param {String} either an object  or a string..
48827      */
48828     setValue : function(v){
48829         //this.value = v;
48830         v = v || []; // empty set..
48831         // this does not seem smart - it really only affects memoryproxy grids..
48832         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48833             var ds = this.grid.getDataSource();
48834             // assumes a json reader..
48835             var data = {}
48836             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48837             ds.loadData( data);
48838         }
48839         // clear selection so it does not get stale.
48840         if (this.grid.sm) { 
48841             this.grid.sm.clearSelections();
48842         }
48843         
48844         Roo.form.GridField.superclass.setValue.call(this, v);
48845         this.refreshValue();
48846         // should load data in the grid really....
48847     },
48848     
48849     // private
48850     refreshValue: function() {
48851          var val = [];
48852         this.grid.getDataSource().each(function(r) {
48853             val.push(r.data);
48854         });
48855         this.el.dom.value = Roo.encode(val);
48856     }
48857     
48858      
48859     
48860     
48861 });/*
48862  * Based on:
48863  * Ext JS Library 1.1.1
48864  * Copyright(c) 2006-2007, Ext JS, LLC.
48865  *
48866  * Originally Released Under LGPL - original licence link has changed is not relivant.
48867  *
48868  * Fork - LGPL
48869  * <script type="text/javascript">
48870  */
48871 /**
48872  * @class Roo.form.DisplayField
48873  * @extends Roo.form.Field
48874  * A generic Field to display non-editable data.
48875  * @cfg {Boolean} closable (true|false) default false
48876  * @constructor
48877  * Creates a new Display Field item.
48878  * @param {Object} config Configuration options
48879  */
48880 Roo.form.DisplayField = function(config){
48881     Roo.form.DisplayField.superclass.constructor.call(this, config);
48882     
48883     this.addEvents({
48884         /**
48885          * @event close
48886          * Fires after the click the close btn
48887              * @param {Roo.form.DisplayField} this
48888              */
48889         close : true
48890     });
48891 };
48892
48893 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48894     inputType:      'hidden',
48895     allowBlank:     true,
48896     readOnly:         true,
48897     
48898  
48899     /**
48900      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48901      */
48902     focusClass : undefined,
48903     /**
48904      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48905      */
48906     fieldClass: 'x-form-field',
48907     
48908      /**
48909      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48910      */
48911     valueRenderer: undefined,
48912     
48913     width: 100,
48914     /**
48915      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48916      * {tag: "input", type: "checkbox", autocomplete: "off"})
48917      */
48918      
48919  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48920  
48921     closable : false,
48922     
48923     onResize : function(){
48924         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48925         
48926     },
48927
48928     initEvents : function(){
48929         // Roo.form.Checkbox.superclass.initEvents.call(this);
48930         // has no events...
48931         
48932         if(this.closable){
48933             this.closeEl.on('click', this.onClose, this);
48934         }
48935        
48936     },
48937
48938
48939     getResizeEl : function(){
48940         return this.wrap;
48941     },
48942
48943     getPositionEl : function(){
48944         return this.wrap;
48945     },
48946
48947     // private
48948     onRender : function(ct, position){
48949         
48950         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48951         //if(this.inputValue !== undefined){
48952         this.wrap = this.el.wrap();
48953         
48954         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48955         
48956         if(this.closable){
48957             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48958         }
48959         
48960         if (this.bodyStyle) {
48961             this.viewEl.applyStyles(this.bodyStyle);
48962         }
48963         //this.viewEl.setStyle('padding', '2px');
48964         
48965         this.setValue(this.value);
48966         
48967     },
48968 /*
48969     // private
48970     initValue : Roo.emptyFn,
48971
48972   */
48973
48974         // private
48975     onClick : function(){
48976         
48977     },
48978
48979     /**
48980      * Sets the checked state of the checkbox.
48981      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48982      */
48983     setValue : function(v){
48984         this.value = v;
48985         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48986         // this might be called before we have a dom element..
48987         if (!this.viewEl) {
48988             return;
48989         }
48990         this.viewEl.dom.innerHTML = html;
48991         Roo.form.DisplayField.superclass.setValue.call(this, v);
48992
48993     },
48994     
48995     onClose : function(e)
48996     {
48997         e.preventDefault();
48998         
48999         this.fireEvent('close', this);
49000     }
49001 });/*
49002  * 
49003  * Licence- LGPL
49004  * 
49005  */
49006
49007 /**
49008  * @class Roo.form.DayPicker
49009  * @extends Roo.form.Field
49010  * A Day picker show [M] [T] [W] ....
49011  * @constructor
49012  * Creates a new Day Picker
49013  * @param {Object} config Configuration options
49014  */
49015 Roo.form.DayPicker= function(config){
49016     Roo.form.DayPicker.superclass.constructor.call(this, config);
49017      
49018 };
49019
49020 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49021     /**
49022      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49023      */
49024     focusClass : undefined,
49025     /**
49026      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49027      */
49028     fieldClass: "x-form-field",
49029    
49030     /**
49031      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49032      * {tag: "input", type: "checkbox", autocomplete: "off"})
49033      */
49034     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49035     
49036    
49037     actionMode : 'viewEl', 
49038     //
49039     // private
49040  
49041     inputType : 'hidden',
49042     
49043      
49044     inputElement: false, // real input element?
49045     basedOn: false, // ????
49046     
49047     isFormField: true, // not sure where this is needed!!!!
49048
49049     onResize : function(){
49050         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49051         if(!this.boxLabel){
49052             this.el.alignTo(this.wrap, 'c-c');
49053         }
49054     },
49055
49056     initEvents : function(){
49057         Roo.form.Checkbox.superclass.initEvents.call(this);
49058         this.el.on("click", this.onClick,  this);
49059         this.el.on("change", this.onClick,  this);
49060     },
49061
49062
49063     getResizeEl : function(){
49064         return this.wrap;
49065     },
49066
49067     getPositionEl : function(){
49068         return this.wrap;
49069     },
49070
49071     
49072     // private
49073     onRender : function(ct, position){
49074         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49075        
49076         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49077         
49078         var r1 = '<table><tr>';
49079         var r2 = '<tr class="x-form-daypick-icons">';
49080         for (var i=0; i < 7; i++) {
49081             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49082             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49083         }
49084         
49085         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49086         viewEl.select('img').on('click', this.onClick, this);
49087         this.viewEl = viewEl;   
49088         
49089         
49090         // this will not work on Chrome!!!
49091         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49092         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49093         
49094         
49095           
49096
49097     },
49098
49099     // private
49100     initValue : Roo.emptyFn,
49101
49102     /**
49103      * Returns the checked state of the checkbox.
49104      * @return {Boolean} True if checked, else false
49105      */
49106     getValue : function(){
49107         return this.el.dom.value;
49108         
49109     },
49110
49111         // private
49112     onClick : function(e){ 
49113         //this.setChecked(!this.checked);
49114         Roo.get(e.target).toggleClass('x-menu-item-checked');
49115         this.refreshValue();
49116         //if(this.el.dom.checked != this.checked){
49117         //    this.setValue(this.el.dom.checked);
49118        // }
49119     },
49120     
49121     // private
49122     refreshValue : function()
49123     {
49124         var val = '';
49125         this.viewEl.select('img',true).each(function(e,i,n)  {
49126             val += e.is(".x-menu-item-checked") ? String(n) : '';
49127         });
49128         this.setValue(val, true);
49129     },
49130
49131     /**
49132      * Sets the checked state of the checkbox.
49133      * On is always based on a string comparison between inputValue and the param.
49134      * @param {Boolean/String} value - the value to set 
49135      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49136      */
49137     setValue : function(v,suppressEvent){
49138         if (!this.el.dom) {
49139             return;
49140         }
49141         var old = this.el.dom.value ;
49142         this.el.dom.value = v;
49143         if (suppressEvent) {
49144             return ;
49145         }
49146          
49147         // update display..
49148         this.viewEl.select('img',true).each(function(e,i,n)  {
49149             
49150             var on = e.is(".x-menu-item-checked");
49151             var newv = v.indexOf(String(n)) > -1;
49152             if (on != newv) {
49153                 e.toggleClass('x-menu-item-checked');
49154             }
49155             
49156         });
49157         
49158         
49159         this.fireEvent('change', this, v, old);
49160         
49161         
49162     },
49163    
49164     // handle setting of hidden value by some other method!!?!?
49165     setFromHidden: function()
49166     {
49167         if(!this.el){
49168             return;
49169         }
49170         //console.log("SET FROM HIDDEN");
49171         //alert('setFrom hidden');
49172         this.setValue(this.el.dom.value);
49173     },
49174     
49175     onDestroy : function()
49176     {
49177         if(this.viewEl){
49178             Roo.get(this.viewEl).remove();
49179         }
49180          
49181         Roo.form.DayPicker.superclass.onDestroy.call(this);
49182     }
49183
49184 });/*
49185  * RooJS Library 1.1.1
49186  * Copyright(c) 2008-2011  Alan Knowles
49187  *
49188  * License - LGPL
49189  */
49190  
49191
49192 /**
49193  * @class Roo.form.ComboCheck
49194  * @extends Roo.form.ComboBox
49195  * A combobox for multiple select items.
49196  *
49197  * FIXME - could do with a reset button..
49198  * 
49199  * @constructor
49200  * Create a new ComboCheck
49201  * @param {Object} config Configuration options
49202  */
49203 Roo.form.ComboCheck = function(config){
49204     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49205     // should verify some data...
49206     // like
49207     // hiddenName = required..
49208     // displayField = required
49209     // valudField == required
49210     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49211     var _t = this;
49212     Roo.each(req, function(e) {
49213         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49214             throw "Roo.form.ComboCheck : missing value for: " + e;
49215         }
49216     });
49217     
49218     
49219 };
49220
49221 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49222      
49223      
49224     editable : false,
49225      
49226     selectedClass: 'x-menu-item-checked', 
49227     
49228     // private
49229     onRender : function(ct, position){
49230         var _t = this;
49231         
49232         
49233         
49234         if(!this.tpl){
49235             var cls = 'x-combo-list';
49236
49237             
49238             this.tpl =  new Roo.Template({
49239                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49240                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49241                    '<span>{' + this.displayField + '}</span>' +
49242                     '</div>' 
49243                 
49244             });
49245         }
49246  
49247         
49248         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49249         this.view.singleSelect = false;
49250         this.view.multiSelect = true;
49251         this.view.toggleSelect = true;
49252         this.pageTb.add(new Roo.Toolbar.Fill(), {
49253             
49254             text: 'Done',
49255             handler: function()
49256             {
49257                 _t.collapse();
49258             }
49259         });
49260     },
49261     
49262     onViewOver : function(e, t){
49263         // do nothing...
49264         return;
49265         
49266     },
49267     
49268     onViewClick : function(doFocus,index){
49269         return;
49270         
49271     },
49272     select: function () {
49273         //Roo.log("SELECT CALLED");
49274     },
49275      
49276     selectByValue : function(xv, scrollIntoView){
49277         var ar = this.getValueArray();
49278         var sels = [];
49279         
49280         Roo.each(ar, function(v) {
49281             if(v === undefined || v === null){
49282                 return;
49283             }
49284             var r = this.findRecord(this.valueField, v);
49285             if(r){
49286                 sels.push(this.store.indexOf(r))
49287                 
49288             }
49289         },this);
49290         this.view.select(sels);
49291         return false;
49292     },
49293     
49294     
49295     
49296     onSelect : function(record, index){
49297        // Roo.log("onselect Called");
49298        // this is only called by the clear button now..
49299         this.view.clearSelections();
49300         this.setValue('[]');
49301         if (this.value != this.valueBefore) {
49302             this.fireEvent('change', this, this.value, this.valueBefore);
49303             this.valueBefore = this.value;
49304         }
49305     },
49306     getValueArray : function()
49307     {
49308         var ar = [] ;
49309         
49310         try {
49311             //Roo.log(this.value);
49312             if (typeof(this.value) == 'undefined') {
49313                 return [];
49314             }
49315             var ar = Roo.decode(this.value);
49316             return  ar instanceof Array ? ar : []; //?? valid?
49317             
49318         } catch(e) {
49319             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49320             return [];
49321         }
49322          
49323     },
49324     expand : function ()
49325     {
49326         
49327         Roo.form.ComboCheck.superclass.expand.call(this);
49328         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49329         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49330         
49331
49332     },
49333     
49334     collapse : function(){
49335         Roo.form.ComboCheck.superclass.collapse.call(this);
49336         var sl = this.view.getSelectedIndexes();
49337         var st = this.store;
49338         var nv = [];
49339         var tv = [];
49340         var r;
49341         Roo.each(sl, function(i) {
49342             r = st.getAt(i);
49343             nv.push(r.get(this.valueField));
49344         },this);
49345         this.setValue(Roo.encode(nv));
49346         if (this.value != this.valueBefore) {
49347
49348             this.fireEvent('change', this, this.value, this.valueBefore);
49349             this.valueBefore = this.value;
49350         }
49351         
49352     },
49353     
49354     setValue : function(v){
49355         // Roo.log(v);
49356         this.value = v;
49357         
49358         var vals = this.getValueArray();
49359         var tv = [];
49360         Roo.each(vals, function(k) {
49361             var r = this.findRecord(this.valueField, k);
49362             if(r){
49363                 tv.push(r.data[this.displayField]);
49364             }else if(this.valueNotFoundText !== undefined){
49365                 tv.push( this.valueNotFoundText );
49366             }
49367         },this);
49368        // Roo.log(tv);
49369         
49370         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49371         this.hiddenField.value = v;
49372         this.value = v;
49373     }
49374     
49375 });/*
49376  * Based on:
49377  * Ext JS Library 1.1.1
49378  * Copyright(c) 2006-2007, Ext JS, LLC.
49379  *
49380  * Originally Released Under LGPL - original licence link has changed is not relivant.
49381  *
49382  * Fork - LGPL
49383  * <script type="text/javascript">
49384  */
49385  
49386 /**
49387  * @class Roo.form.Signature
49388  * @extends Roo.form.Field
49389  * Signature field.  
49390  * @constructor
49391  * 
49392  * @param {Object} config Configuration options
49393  */
49394
49395 Roo.form.Signature = function(config){
49396     Roo.form.Signature.superclass.constructor.call(this, config);
49397     
49398     this.addEvents({// not in used??
49399          /**
49400          * @event confirm
49401          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49402              * @param {Roo.form.Signature} combo This combo box
49403              */
49404         'confirm' : true,
49405         /**
49406          * @event reset
49407          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49408              * @param {Roo.form.ComboBox} combo This combo box
49409              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49410              */
49411         'reset' : true
49412     });
49413 };
49414
49415 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49416     /**
49417      * @cfg {Object} labels Label to use when rendering a form.
49418      * defaults to 
49419      * labels : { 
49420      *      clear : "Clear",
49421      *      confirm : "Confirm"
49422      *  }
49423      */
49424     labels : { 
49425         clear : "Clear",
49426         confirm : "Confirm"
49427     },
49428     /**
49429      * @cfg {Number} width The signature panel width (defaults to 300)
49430      */
49431     width: 300,
49432     /**
49433      * @cfg {Number} height The signature panel height (defaults to 100)
49434      */
49435     height : 100,
49436     /**
49437      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49438      */
49439     allowBlank : false,
49440     
49441     //private
49442     // {Object} signPanel The signature SVG panel element (defaults to {})
49443     signPanel : {},
49444     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49445     isMouseDown : false,
49446     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49447     isConfirmed : false,
49448     // {String} signatureTmp SVG mapping string (defaults to empty string)
49449     signatureTmp : '',
49450     
49451     
49452     defaultAutoCreate : { // modified by initCompnoent..
49453         tag: "input",
49454         type:"hidden"
49455     },
49456
49457     // private
49458     onRender : function(ct, position){
49459         
49460         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49461         
49462         this.wrap = this.el.wrap({
49463             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49464         });
49465         
49466         this.createToolbar(this);
49467         this.signPanel = this.wrap.createChild({
49468                 tag: 'div',
49469                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49470             }, this.el
49471         );
49472             
49473         this.svgID = Roo.id();
49474         this.svgEl = this.signPanel.createChild({
49475               xmlns : 'http://www.w3.org/2000/svg',
49476               tag : 'svg',
49477               id : this.svgID + "-svg",
49478               width: this.width,
49479               height: this.height,
49480               viewBox: '0 0 '+this.width+' '+this.height,
49481               cn : [
49482                 {
49483                     tag: "rect",
49484                     id: this.svgID + "-svg-r",
49485                     width: this.width,
49486                     height: this.height,
49487                     fill: "#ffa"
49488                 },
49489                 {
49490                     tag: "line",
49491                     id: this.svgID + "-svg-l",
49492                     x1: "0", // start
49493                     y1: (this.height*0.8), // start set the line in 80% of height
49494                     x2: this.width, // end
49495                     y2: (this.height*0.8), // end set the line in 80% of height
49496                     'stroke': "#666",
49497                     'stroke-width': "1",
49498                     'stroke-dasharray': "3",
49499                     'shape-rendering': "crispEdges",
49500                     'pointer-events': "none"
49501                 },
49502                 {
49503                     tag: "path",
49504                     id: this.svgID + "-svg-p",
49505                     'stroke': "navy",
49506                     'stroke-width': "3",
49507                     'fill': "none",
49508                     'pointer-events': 'none'
49509                 }
49510               ]
49511         });
49512         this.createSVG();
49513         this.svgBox = this.svgEl.dom.getScreenCTM();
49514     },
49515     createSVG : function(){ 
49516         var svg = this.signPanel;
49517         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49518         var t = this;
49519
49520         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49521         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49522         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49523         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49524         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49525         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49526         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49527         
49528     },
49529     isTouchEvent : function(e){
49530         return e.type.match(/^touch/);
49531     },
49532     getCoords : function (e) {
49533         var pt    = this.svgEl.dom.createSVGPoint();
49534         pt.x = e.clientX; 
49535         pt.y = e.clientY;
49536         if (this.isTouchEvent(e)) {
49537             pt.x =  e.targetTouches[0].clientX;
49538             pt.y = e.targetTouches[0].clientY;
49539         }
49540         var a = this.svgEl.dom.getScreenCTM();
49541         var b = a.inverse();
49542         var mx = pt.matrixTransform(b);
49543         return mx.x + ',' + mx.y;
49544     },
49545     //mouse event headler 
49546     down : function (e) {
49547         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49548         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49549         
49550         this.isMouseDown = true;
49551         
49552         e.preventDefault();
49553     },
49554     move : function (e) {
49555         if (this.isMouseDown) {
49556             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49557             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49558         }
49559         
49560         e.preventDefault();
49561     },
49562     up : function (e) {
49563         this.isMouseDown = false;
49564         var sp = this.signatureTmp.split(' ');
49565         
49566         if(sp.length > 1){
49567             if(!sp[sp.length-2].match(/^L/)){
49568                 sp.pop();
49569                 sp.pop();
49570                 sp.push("");
49571                 this.signatureTmp = sp.join(" ");
49572             }
49573         }
49574         if(this.getValue() != this.signatureTmp){
49575             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49576             this.isConfirmed = false;
49577         }
49578         e.preventDefault();
49579     },
49580     
49581     /**
49582      * Protected method that will not generally be called directly. It
49583      * is called when the editor creates its toolbar. Override this method if you need to
49584      * add custom toolbar buttons.
49585      * @param {HtmlEditor} editor
49586      */
49587     createToolbar : function(editor){
49588          function btn(id, toggle, handler){
49589             var xid = fid + '-'+ id ;
49590             return {
49591                 id : xid,
49592                 cmd : id,
49593                 cls : 'x-btn-icon x-edit-'+id,
49594                 enableToggle:toggle !== false,
49595                 scope: editor, // was editor...
49596                 handler:handler||editor.relayBtnCmd,
49597                 clickEvent:'mousedown',
49598                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49599                 tabIndex:-1
49600             };
49601         }
49602         
49603         
49604         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49605         this.tb = tb;
49606         this.tb.add(
49607            {
49608                 cls : ' x-signature-btn x-signature-'+id,
49609                 scope: editor, // was editor...
49610                 handler: this.reset,
49611                 clickEvent:'mousedown',
49612                 text: this.labels.clear
49613             },
49614             {
49615                  xtype : 'Fill',
49616                  xns: Roo.Toolbar
49617             }, 
49618             {
49619                 cls : '  x-signature-btn x-signature-'+id,
49620                 scope: editor, // was editor...
49621                 handler: this.confirmHandler,
49622                 clickEvent:'mousedown',
49623                 text: this.labels.confirm
49624             }
49625         );
49626     
49627     },
49628     //public
49629     /**
49630      * when user is clicked confirm then show this image.....
49631      * 
49632      * @return {String} Image Data URI
49633      */
49634     getImageDataURI : function(){
49635         var svg = this.svgEl.dom.parentNode.innerHTML;
49636         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49637         return src; 
49638     },
49639     /**
49640      * 
49641      * @return {Boolean} this.isConfirmed
49642      */
49643     getConfirmed : function(){
49644         return this.isConfirmed;
49645     },
49646     /**
49647      * 
49648      * @return {Number} this.width
49649      */
49650     getWidth : function(){
49651         return this.width;
49652     },
49653     /**
49654      * 
49655      * @return {Number} this.height
49656      */
49657     getHeight : function(){
49658         return this.height;
49659     },
49660     // private
49661     getSignature : function(){
49662         return this.signatureTmp;
49663     },
49664     // private
49665     reset : function(){
49666         this.signatureTmp = '';
49667         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49668         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49669         this.isConfirmed = false;
49670         Roo.form.Signature.superclass.reset.call(this);
49671     },
49672     setSignature : function(s){
49673         this.signatureTmp = s;
49674         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49675         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49676         this.setValue(s);
49677         this.isConfirmed = false;
49678         Roo.form.Signature.superclass.reset.call(this);
49679     }, 
49680     test : function(){
49681 //        Roo.log(this.signPanel.dom.contentWindow.up())
49682     },
49683     //private
49684     setConfirmed : function(){
49685         
49686         
49687         
49688 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49689     },
49690     // private
49691     confirmHandler : function(){
49692         if(!this.getSignature()){
49693             return;
49694         }
49695         
49696         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49697         this.setValue(this.getSignature());
49698         this.isConfirmed = true;
49699         
49700         this.fireEvent('confirm', this);
49701     },
49702     // private
49703     // Subclasses should provide the validation implementation by overriding this
49704     validateValue : function(value){
49705         if(this.allowBlank){
49706             return true;
49707         }
49708         
49709         if(this.isConfirmed){
49710             return true;
49711         }
49712         return false;
49713     }
49714 });/*
49715  * Based on:
49716  * Ext JS Library 1.1.1
49717  * Copyright(c) 2006-2007, Ext JS, LLC.
49718  *
49719  * Originally Released Under LGPL - original licence link has changed is not relivant.
49720  *
49721  * Fork - LGPL
49722  * <script type="text/javascript">
49723  */
49724  
49725
49726 /**
49727  * @class Roo.form.ComboBox
49728  * @extends Roo.form.TriggerField
49729  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49730  * @constructor
49731  * Create a new ComboBox.
49732  * @param {Object} config Configuration options
49733  */
49734 Roo.form.Select = function(config){
49735     Roo.form.Select.superclass.constructor.call(this, config);
49736      
49737 };
49738
49739 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49740     /**
49741      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49742      */
49743     /**
49744      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49745      * rendering into an Roo.Editor, defaults to false)
49746      */
49747     /**
49748      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49749      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49750      */
49751     /**
49752      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49753      */
49754     /**
49755      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49756      * the dropdown list (defaults to undefined, with no header element)
49757      */
49758
49759      /**
49760      * @cfg {String/Roo.Template} tpl The template to use to render the output
49761      */
49762      
49763     // private
49764     defaultAutoCreate : {tag: "select"  },
49765     /**
49766      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49767      */
49768     listWidth: undefined,
49769     /**
49770      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49771      * mode = 'remote' or 'text' if mode = 'local')
49772      */
49773     displayField: undefined,
49774     /**
49775      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49776      * mode = 'remote' or 'value' if mode = 'local'). 
49777      * Note: use of a valueField requires the user make a selection
49778      * in order for a value to be mapped.
49779      */
49780     valueField: undefined,
49781     
49782     
49783     /**
49784      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49785      * field's data value (defaults to the underlying DOM element's name)
49786      */
49787     hiddenName: undefined,
49788     /**
49789      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49790      */
49791     listClass: '',
49792     /**
49793      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49794      */
49795     selectedClass: 'x-combo-selected',
49796     /**
49797      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49798      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49799      * which displays a downward arrow icon).
49800      */
49801     triggerClass : 'x-form-arrow-trigger',
49802     /**
49803      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49804      */
49805     shadow:'sides',
49806     /**
49807      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49808      * anchor positions (defaults to 'tl-bl')
49809      */
49810     listAlign: 'tl-bl?',
49811     /**
49812      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49813      */
49814     maxHeight: 300,
49815     /**
49816      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49817      * query specified by the allQuery config option (defaults to 'query')
49818      */
49819     triggerAction: 'query',
49820     /**
49821      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49822      * (defaults to 4, does not apply if editable = false)
49823      */
49824     minChars : 4,
49825     /**
49826      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49827      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49828      */
49829     typeAhead: false,
49830     /**
49831      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49832      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49833      */
49834     queryDelay: 500,
49835     /**
49836      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49837      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49838      */
49839     pageSize: 0,
49840     /**
49841      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49842      * when editable = true (defaults to false)
49843      */
49844     selectOnFocus:false,
49845     /**
49846      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49847      */
49848     queryParam: 'query',
49849     /**
49850      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49851      * when mode = 'remote' (defaults to 'Loading...')
49852      */
49853     loadingText: 'Loading...',
49854     /**
49855      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49856      */
49857     resizable: false,
49858     /**
49859      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49860      */
49861     handleHeight : 8,
49862     /**
49863      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49864      * traditional select (defaults to true)
49865      */
49866     editable: true,
49867     /**
49868      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49869      */
49870     allQuery: '',
49871     /**
49872      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49873      */
49874     mode: 'remote',
49875     /**
49876      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49877      * listWidth has a higher value)
49878      */
49879     minListWidth : 70,
49880     /**
49881      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49882      * allow the user to set arbitrary text into the field (defaults to false)
49883      */
49884     forceSelection:false,
49885     /**
49886      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49887      * if typeAhead = true (defaults to 250)
49888      */
49889     typeAheadDelay : 250,
49890     /**
49891      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49892      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49893      */
49894     valueNotFoundText : undefined,
49895     
49896     /**
49897      * @cfg {String} defaultValue The value displayed after loading the store.
49898      */
49899     defaultValue: '',
49900     
49901     /**
49902      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49903      */
49904     blockFocus : false,
49905     
49906     /**
49907      * @cfg {Boolean} disableClear Disable showing of clear button.
49908      */
49909     disableClear : false,
49910     /**
49911      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49912      */
49913     alwaysQuery : false,
49914     
49915     //private
49916     addicon : false,
49917     editicon: false,
49918     
49919     // element that contains real text value.. (when hidden is used..)
49920      
49921     // private
49922     onRender : function(ct, position){
49923         Roo.form.Field.prototype.onRender.call(this, ct, position);
49924         
49925         if(this.store){
49926             this.store.on('beforeload', this.onBeforeLoad, this);
49927             this.store.on('load', this.onLoad, this);
49928             this.store.on('loadexception', this.onLoadException, this);
49929             this.store.load({});
49930         }
49931         
49932         
49933         
49934     },
49935
49936     // private
49937     initEvents : function(){
49938         //Roo.form.ComboBox.superclass.initEvents.call(this);
49939  
49940     },
49941
49942     onDestroy : function(){
49943        
49944         if(this.store){
49945             this.store.un('beforeload', this.onBeforeLoad, this);
49946             this.store.un('load', this.onLoad, this);
49947             this.store.un('loadexception', this.onLoadException, this);
49948         }
49949         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49950     },
49951
49952     // private
49953     fireKey : function(e){
49954         if(e.isNavKeyPress() && !this.list.isVisible()){
49955             this.fireEvent("specialkey", this, e);
49956         }
49957     },
49958
49959     // private
49960     onResize: function(w, h){
49961         
49962         return; 
49963     
49964         
49965     },
49966
49967     /**
49968      * Allow or prevent the user from directly editing the field text.  If false is passed,
49969      * the user will only be able to select from the items defined in the dropdown list.  This method
49970      * is the runtime equivalent of setting the 'editable' config option at config time.
49971      * @param {Boolean} value True to allow the user to directly edit the field text
49972      */
49973     setEditable : function(value){
49974          
49975     },
49976
49977     // private
49978     onBeforeLoad : function(){
49979         
49980         Roo.log("Select before load");
49981         return;
49982     
49983         this.innerList.update(this.loadingText ?
49984                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49985         //this.restrictHeight();
49986         this.selectedIndex = -1;
49987     },
49988
49989     // private
49990     onLoad : function(){
49991
49992     
49993         var dom = this.el.dom;
49994         dom.innerHTML = '';
49995          var od = dom.ownerDocument;
49996          
49997         if (this.emptyText) {
49998             var op = od.createElement('option');
49999             op.setAttribute('value', '');
50000             op.innerHTML = String.format('{0}', this.emptyText);
50001             dom.appendChild(op);
50002         }
50003         if(this.store.getCount() > 0){
50004            
50005             var vf = this.valueField;
50006             var df = this.displayField;
50007             this.store.data.each(function(r) {
50008                 // which colmsn to use... testing - cdoe / title..
50009                 var op = od.createElement('option');
50010                 op.setAttribute('value', r.data[vf]);
50011                 op.innerHTML = String.format('{0}', r.data[df]);
50012                 dom.appendChild(op);
50013             });
50014             if (typeof(this.defaultValue != 'undefined')) {
50015                 this.setValue(this.defaultValue);
50016             }
50017             
50018              
50019         }else{
50020             //this.onEmptyResults();
50021         }
50022         //this.el.focus();
50023     },
50024     // private
50025     onLoadException : function()
50026     {
50027         dom.innerHTML = '';
50028             
50029         Roo.log("Select on load exception");
50030         return;
50031     
50032         this.collapse();
50033         Roo.log(this.store.reader.jsonData);
50034         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50035             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50036         }
50037         
50038         
50039     },
50040     // private
50041     onTypeAhead : function(){
50042          
50043     },
50044
50045     // private
50046     onSelect : function(record, index){
50047         Roo.log('on select?');
50048         return;
50049         if(this.fireEvent('beforeselect', this, record, index) !== false){
50050             this.setFromData(index > -1 ? record.data : false);
50051             this.collapse();
50052             this.fireEvent('select', this, record, index);
50053         }
50054     },
50055
50056     /**
50057      * Returns the currently selected field value or empty string if no value is set.
50058      * @return {String} value The selected value
50059      */
50060     getValue : function(){
50061         var dom = this.el.dom;
50062         this.value = dom.options[dom.selectedIndex].value;
50063         return this.value;
50064         
50065     },
50066
50067     /**
50068      * Clears any text/value currently set in the field
50069      */
50070     clearValue : function(){
50071         this.value = '';
50072         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50073         
50074     },
50075
50076     /**
50077      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50078      * will be displayed in the field.  If the value does not match the data value of an existing item,
50079      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50080      * Otherwise the field will be blank (although the value will still be set).
50081      * @param {String} value The value to match
50082      */
50083     setValue : function(v){
50084         var d = this.el.dom;
50085         for (var i =0; i < d.options.length;i++) {
50086             if (v == d.options[i].value) {
50087                 d.selectedIndex = i;
50088                 this.value = v;
50089                 return;
50090             }
50091         }
50092         this.clearValue();
50093     },
50094     /**
50095      * @property {Object} the last set data for the element
50096      */
50097     
50098     lastData : false,
50099     /**
50100      * Sets the value of the field based on a object which is related to the record format for the store.
50101      * @param {Object} value the value to set as. or false on reset?
50102      */
50103     setFromData : function(o){
50104         Roo.log('setfrom data?');
50105          
50106         
50107         
50108     },
50109     // private
50110     reset : function(){
50111         this.clearValue();
50112     },
50113     // private
50114     findRecord : function(prop, value){
50115         
50116         return false;
50117     
50118         var record;
50119         if(this.store.getCount() > 0){
50120             this.store.each(function(r){
50121                 if(r.data[prop] == value){
50122                     record = r;
50123                     return false;
50124                 }
50125                 return true;
50126             });
50127         }
50128         return record;
50129     },
50130     
50131     getName: function()
50132     {
50133         // returns hidden if it's set..
50134         if (!this.rendered) {return ''};
50135         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50136         
50137     },
50138      
50139
50140     
50141
50142     // private
50143     onEmptyResults : function(){
50144         Roo.log('empty results');
50145         //this.collapse();
50146     },
50147
50148     /**
50149      * Returns true if the dropdown list is expanded, else false.
50150      */
50151     isExpanded : function(){
50152         return false;
50153     },
50154
50155     /**
50156      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50157      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50158      * @param {String} value The data value of the item to select
50159      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50160      * selected item if it is not currently in view (defaults to true)
50161      * @return {Boolean} True if the value matched an item in the list, else false
50162      */
50163     selectByValue : function(v, scrollIntoView){
50164         Roo.log('select By Value');
50165         return false;
50166     
50167         if(v !== undefined && v !== null){
50168             var r = this.findRecord(this.valueField || this.displayField, v);
50169             if(r){
50170                 this.select(this.store.indexOf(r), scrollIntoView);
50171                 return true;
50172             }
50173         }
50174         return false;
50175     },
50176
50177     /**
50178      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50179      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50180      * @param {Number} index The zero-based index of the list item to select
50181      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50182      * selected item if it is not currently in view (defaults to true)
50183      */
50184     select : function(index, scrollIntoView){
50185         Roo.log('select ');
50186         return  ;
50187         
50188         this.selectedIndex = index;
50189         this.view.select(index);
50190         if(scrollIntoView !== false){
50191             var el = this.view.getNode(index);
50192             if(el){
50193                 this.innerList.scrollChildIntoView(el, false);
50194             }
50195         }
50196     },
50197
50198       
50199
50200     // private
50201     validateBlur : function(){
50202         
50203         return;
50204         
50205     },
50206
50207     // private
50208     initQuery : function(){
50209         this.doQuery(this.getRawValue());
50210     },
50211
50212     // private
50213     doForce : function(){
50214         if(this.el.dom.value.length > 0){
50215             this.el.dom.value =
50216                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50217              
50218         }
50219     },
50220
50221     /**
50222      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50223      * query allowing the query action to be canceled if needed.
50224      * @param {String} query The SQL query to execute
50225      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50226      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50227      * saved in the current store (defaults to false)
50228      */
50229     doQuery : function(q, forceAll){
50230         
50231         Roo.log('doQuery?');
50232         if(q === undefined || q === null){
50233             q = '';
50234         }
50235         var qe = {
50236             query: q,
50237             forceAll: forceAll,
50238             combo: this,
50239             cancel:false
50240         };
50241         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50242             return false;
50243         }
50244         q = qe.query;
50245         forceAll = qe.forceAll;
50246         if(forceAll === true || (q.length >= this.minChars)){
50247             if(this.lastQuery != q || this.alwaysQuery){
50248                 this.lastQuery = q;
50249                 if(this.mode == 'local'){
50250                     this.selectedIndex = -1;
50251                     if(forceAll){
50252                         this.store.clearFilter();
50253                     }else{
50254                         this.store.filter(this.displayField, q);
50255                     }
50256                     this.onLoad();
50257                 }else{
50258                     this.store.baseParams[this.queryParam] = q;
50259                     this.store.load({
50260                         params: this.getParams(q)
50261                     });
50262                     this.expand();
50263                 }
50264             }else{
50265                 this.selectedIndex = -1;
50266                 this.onLoad();   
50267             }
50268         }
50269     },
50270
50271     // private
50272     getParams : function(q){
50273         var p = {};
50274         //p[this.queryParam] = q;
50275         if(this.pageSize){
50276             p.start = 0;
50277             p.limit = this.pageSize;
50278         }
50279         return p;
50280     },
50281
50282     /**
50283      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50284      */
50285     collapse : function(){
50286         
50287     },
50288
50289     // private
50290     collapseIf : function(e){
50291         
50292     },
50293
50294     /**
50295      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50296      */
50297     expand : function(){
50298         
50299     } ,
50300
50301     // private
50302      
50303
50304     /** 
50305     * @cfg {Boolean} grow 
50306     * @hide 
50307     */
50308     /** 
50309     * @cfg {Number} growMin 
50310     * @hide 
50311     */
50312     /** 
50313     * @cfg {Number} growMax 
50314     * @hide 
50315     */
50316     /**
50317      * @hide
50318      * @method autoSize
50319      */
50320     
50321     setWidth : function()
50322     {
50323         
50324     },
50325     getResizeEl : function(){
50326         return this.el;
50327     }
50328 });//<script type="text/javasscript">
50329  
50330
50331 /**
50332  * @class Roo.DDView
50333  * A DnD enabled version of Roo.View.
50334  * @param {Element/String} container The Element in which to create the View.
50335  * @param {String} tpl The template string used to create the markup for each element of the View
50336  * @param {Object} config The configuration properties. These include all the config options of
50337  * {@link Roo.View} plus some specific to this class.<br>
50338  * <p>
50339  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50340  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50341  * <p>
50342  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50343 .x-view-drag-insert-above {
50344         border-top:1px dotted #3366cc;
50345 }
50346 .x-view-drag-insert-below {
50347         border-bottom:1px dotted #3366cc;
50348 }
50349 </code></pre>
50350  * 
50351  */
50352  
50353 Roo.DDView = function(container, tpl, config) {
50354     Roo.DDView.superclass.constructor.apply(this, arguments);
50355     this.getEl().setStyle("outline", "0px none");
50356     this.getEl().unselectable();
50357     if (this.dragGroup) {
50358                 this.setDraggable(this.dragGroup.split(","));
50359     }
50360     if (this.dropGroup) {
50361                 this.setDroppable(this.dropGroup.split(","));
50362     }
50363     if (this.deletable) {
50364         this.setDeletable();
50365     }
50366     this.isDirtyFlag = false;
50367         this.addEvents({
50368                 "drop" : true
50369         });
50370 };
50371
50372 Roo.extend(Roo.DDView, Roo.View, {
50373 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50374 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50375 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50376 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50377
50378         isFormField: true,
50379
50380         reset: Roo.emptyFn,
50381         
50382         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50383
50384         validate: function() {
50385                 return true;
50386         },
50387         
50388         destroy: function() {
50389                 this.purgeListeners();
50390                 this.getEl.removeAllListeners();
50391                 this.getEl().remove();
50392                 if (this.dragZone) {
50393                         if (this.dragZone.destroy) {
50394                                 this.dragZone.destroy();
50395                         }
50396                 }
50397                 if (this.dropZone) {
50398                         if (this.dropZone.destroy) {
50399                                 this.dropZone.destroy();
50400                         }
50401                 }
50402         },
50403
50404 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50405         getName: function() {
50406                 return this.name;
50407         },
50408
50409 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50410         setValue: function(v) {
50411                 if (!this.store) {
50412                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50413                 }
50414                 var data = {};
50415                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50416                 this.store.proxy = new Roo.data.MemoryProxy(data);
50417                 this.store.load();
50418         },
50419
50420 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50421         getValue: function() {
50422                 var result = '(';
50423                 this.store.each(function(rec) {
50424                         result += rec.id + ',';
50425                 });
50426                 return result.substr(0, result.length - 1) + ')';
50427         },
50428         
50429         getIds: function() {
50430                 var i = 0, result = new Array(this.store.getCount());
50431                 this.store.each(function(rec) {
50432                         result[i++] = rec.id;
50433                 });
50434                 return result;
50435         },
50436         
50437         isDirty: function() {
50438                 return this.isDirtyFlag;
50439         },
50440
50441 /**
50442  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50443  *      whole Element becomes the target, and this causes the drop gesture to append.
50444  */
50445     getTargetFromEvent : function(e) {
50446                 var target = e.getTarget();
50447                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50448                 target = target.parentNode;
50449                 }
50450                 if (!target) {
50451                         target = this.el.dom.lastChild || this.el.dom;
50452                 }
50453                 return target;
50454     },
50455
50456 /**
50457  *      Create the drag data which consists of an object which has the property "ddel" as
50458  *      the drag proxy element. 
50459  */
50460     getDragData : function(e) {
50461         var target = this.findItemFromChild(e.getTarget());
50462                 if(target) {
50463                         this.handleSelection(e);
50464                         var selNodes = this.getSelectedNodes();
50465             var dragData = {
50466                 source: this,
50467                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50468                 nodes: selNodes,
50469                 records: []
50470                         };
50471                         var selectedIndices = this.getSelectedIndexes();
50472                         for (var i = 0; i < selectedIndices.length; i++) {
50473                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50474                         }
50475                         if (selNodes.length == 1) {
50476                                 dragData.ddel = target.cloneNode(true); // the div element
50477                         } else {
50478                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50479                                 div.className = 'multi-proxy';
50480                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50481                                         div.appendChild(selNodes[i].cloneNode(true));
50482                                 }
50483                                 dragData.ddel = div;
50484                         }
50485             //console.log(dragData)
50486             //console.log(dragData.ddel.innerHTML)
50487                         return dragData;
50488                 }
50489         //console.log('nodragData')
50490                 return false;
50491     },
50492     
50493 /**     Specify to which ddGroup items in this DDView may be dragged. */
50494     setDraggable: function(ddGroup) {
50495         if (ddGroup instanceof Array) {
50496                 Roo.each(ddGroup, this.setDraggable, this);
50497                 return;
50498         }
50499         if (this.dragZone) {
50500                 this.dragZone.addToGroup(ddGroup);
50501         } else {
50502                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50503                                 containerScroll: true,
50504                                 ddGroup: ddGroup 
50505
50506                         });
50507 //                      Draggability implies selection. DragZone's mousedown selects the element.
50508                         if (!this.multiSelect) { this.singleSelect = true; }
50509
50510 //                      Wire the DragZone's handlers up to methods in *this*
50511                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50512                 }
50513     },
50514
50515 /**     Specify from which ddGroup this DDView accepts drops. */
50516     setDroppable: function(ddGroup) {
50517         if (ddGroup instanceof Array) {
50518                 Roo.each(ddGroup, this.setDroppable, this);
50519                 return;
50520         }
50521         if (this.dropZone) {
50522                 this.dropZone.addToGroup(ddGroup);
50523         } else {
50524                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50525                                 containerScroll: true,
50526                                 ddGroup: ddGroup
50527                         });
50528
50529 //                      Wire the DropZone's handlers up to methods in *this*
50530                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50531                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50532                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50533                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50534                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50535                 }
50536     },
50537
50538 /**     Decide whether to drop above or below a View node. */
50539     getDropPoint : function(e, n, dd){
50540         if (n == this.el.dom) { return "above"; }
50541                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50542                 var c = t + (b - t) / 2;
50543                 var y = Roo.lib.Event.getPageY(e);
50544                 if(y <= c) {
50545                         return "above";
50546                 }else{
50547                         return "below";
50548                 }
50549     },
50550
50551     onNodeEnter : function(n, dd, e, data){
50552                 return false;
50553     },
50554     
50555     onNodeOver : function(n, dd, e, data){
50556                 var pt = this.getDropPoint(e, n, dd);
50557                 // set the insert point style on the target node
50558                 var dragElClass = this.dropNotAllowed;
50559                 if (pt) {
50560                         var targetElClass;
50561                         if (pt == "above"){
50562                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50563                                 targetElClass = "x-view-drag-insert-above";
50564                         } else {
50565                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50566                                 targetElClass = "x-view-drag-insert-below";
50567                         }
50568                         if (this.lastInsertClass != targetElClass){
50569                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50570                                 this.lastInsertClass = targetElClass;
50571                         }
50572                 }
50573                 return dragElClass;
50574         },
50575
50576     onNodeOut : function(n, dd, e, data){
50577                 this.removeDropIndicators(n);
50578     },
50579
50580     onNodeDrop : function(n, dd, e, data){
50581         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50582                 return false;
50583         }
50584         var pt = this.getDropPoint(e, n, dd);
50585                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50586                 if (pt == "below") { insertAt++; }
50587                 for (var i = 0; i < data.records.length; i++) {
50588                         var r = data.records[i];
50589                         var dup = this.store.getById(r.id);
50590                         if (dup && (dd != this.dragZone)) {
50591                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50592                         } else {
50593                                 if (data.copy) {
50594                                         this.store.insert(insertAt++, r.copy());
50595                                 } else {
50596                                         data.source.isDirtyFlag = true;
50597                                         r.store.remove(r);
50598                                         this.store.insert(insertAt++, r);
50599                                 }
50600                                 this.isDirtyFlag = true;
50601                         }
50602                 }
50603                 this.dragZone.cachedTarget = null;
50604                 return true;
50605     },
50606
50607     removeDropIndicators : function(n){
50608                 if(n){
50609                         Roo.fly(n).removeClass([
50610                                 "x-view-drag-insert-above",
50611                                 "x-view-drag-insert-below"]);
50612                         this.lastInsertClass = "_noclass";
50613                 }
50614     },
50615
50616 /**
50617  *      Utility method. Add a delete option to the DDView's context menu.
50618  *      @param {String} imageUrl The URL of the "delete" icon image.
50619  */
50620         setDeletable: function(imageUrl) {
50621                 if (!this.singleSelect && !this.multiSelect) {
50622                         this.singleSelect = true;
50623                 }
50624                 var c = this.getContextMenu();
50625                 this.contextMenu.on("itemclick", function(item) {
50626                         switch (item.id) {
50627                                 case "delete":
50628                                         this.remove(this.getSelectedIndexes());
50629                                         break;
50630                         }
50631                 }, this);
50632                 this.contextMenu.add({
50633                         icon: imageUrl,
50634                         id: "delete",
50635                         text: 'Delete'
50636                 });
50637         },
50638         
50639 /**     Return the context menu for this DDView. */
50640         getContextMenu: function() {
50641                 if (!this.contextMenu) {
50642 //                      Create the View's context menu
50643                         this.contextMenu = new Roo.menu.Menu({
50644                                 id: this.id + "-contextmenu"
50645                         });
50646                         this.el.on("contextmenu", this.showContextMenu, this);
50647                 }
50648                 return this.contextMenu;
50649         },
50650         
50651         disableContextMenu: function() {
50652                 if (this.contextMenu) {
50653                         this.el.un("contextmenu", this.showContextMenu, this);
50654                 }
50655         },
50656
50657         showContextMenu: function(e, item) {
50658         item = this.findItemFromChild(e.getTarget());
50659                 if (item) {
50660                         e.stopEvent();
50661                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50662                         this.contextMenu.showAt(e.getXY());
50663             }
50664     },
50665
50666 /**
50667  *      Remove {@link Roo.data.Record}s at the specified indices.
50668  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50669  */
50670     remove: function(selectedIndices) {
50671                 selectedIndices = [].concat(selectedIndices);
50672                 for (var i = 0; i < selectedIndices.length; i++) {
50673                         var rec = this.store.getAt(selectedIndices[i]);
50674                         this.store.remove(rec);
50675                 }
50676     },
50677
50678 /**
50679  *      Double click fires the event, but also, if this is draggable, and there is only one other
50680  *      related DropZone, it transfers the selected node.
50681  */
50682     onDblClick : function(e){
50683         var item = this.findItemFromChild(e.getTarget());
50684         if(item){
50685             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50686                 return false;
50687             }
50688             if (this.dragGroup) {
50689                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50690                     while (targets.indexOf(this.dropZone) > -1) {
50691                             targets.remove(this.dropZone);
50692                                 }
50693                     if (targets.length == 1) {
50694                                         this.dragZone.cachedTarget = null;
50695                         var el = Roo.get(targets[0].getEl());
50696                         var box = el.getBox(true);
50697                         targets[0].onNodeDrop(el.dom, {
50698                                 target: el.dom,
50699                                 xy: [box.x, box.y + box.height - 1]
50700                         }, null, this.getDragData(e));
50701                     }
50702                 }
50703         }
50704     },
50705     
50706     handleSelection: function(e) {
50707                 this.dragZone.cachedTarget = null;
50708         var item = this.findItemFromChild(e.getTarget());
50709         if (!item) {
50710                 this.clearSelections(true);
50711                 return;
50712         }
50713                 if (item && (this.multiSelect || this.singleSelect)){
50714                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50715                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50716                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50717                                 this.unselect(item);
50718                         } else {
50719                                 this.select(item, this.multiSelect && e.ctrlKey);
50720                                 this.lastSelection = item;
50721                         }
50722                 }
50723     },
50724
50725     onItemClick : function(item, index, e){
50726                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50727                         return false;
50728                 }
50729                 return true;
50730     },
50731
50732     unselect : function(nodeInfo, suppressEvent){
50733                 var node = this.getNode(nodeInfo);
50734                 if(node && this.isSelected(node)){
50735                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50736                                 Roo.fly(node).removeClass(this.selectedClass);
50737                                 this.selections.remove(node);
50738                                 if(!suppressEvent){
50739                                         this.fireEvent("selectionchange", this, this.selections);
50740                                 }
50741                         }
50742                 }
50743     }
50744 });
50745 /*
50746  * Based on:
50747  * Ext JS Library 1.1.1
50748  * Copyright(c) 2006-2007, Ext JS, LLC.
50749  *
50750  * Originally Released Under LGPL - original licence link has changed is not relivant.
50751  *
50752  * Fork - LGPL
50753  * <script type="text/javascript">
50754  */
50755  
50756 /**
50757  * @class Roo.LayoutManager
50758  * @extends Roo.util.Observable
50759  * Base class for layout managers.
50760  */
50761 Roo.LayoutManager = function(container, config){
50762     Roo.LayoutManager.superclass.constructor.call(this);
50763     this.el = Roo.get(container);
50764     // ie scrollbar fix
50765     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50766         document.body.scroll = "no";
50767     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50768         this.el.position('relative');
50769     }
50770     this.id = this.el.id;
50771     this.el.addClass("x-layout-container");
50772     /** false to disable window resize monitoring @type Boolean */
50773     this.monitorWindowResize = true;
50774     this.regions = {};
50775     this.addEvents({
50776         /**
50777          * @event layout
50778          * Fires when a layout is performed. 
50779          * @param {Roo.LayoutManager} this
50780          */
50781         "layout" : true,
50782         /**
50783          * @event regionresized
50784          * Fires when the user resizes a region. 
50785          * @param {Roo.LayoutRegion} region The resized region
50786          * @param {Number} newSize The new size (width for east/west, height for north/south)
50787          */
50788         "regionresized" : true,
50789         /**
50790          * @event regioncollapsed
50791          * Fires when a region is collapsed. 
50792          * @param {Roo.LayoutRegion} region The collapsed region
50793          */
50794         "regioncollapsed" : true,
50795         /**
50796          * @event regionexpanded
50797          * Fires when a region is expanded.  
50798          * @param {Roo.LayoutRegion} region The expanded region
50799          */
50800         "regionexpanded" : true
50801     });
50802     this.updating = false;
50803     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50804 };
50805
50806 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50807     /**
50808      * Returns true if this layout is currently being updated
50809      * @return {Boolean}
50810      */
50811     isUpdating : function(){
50812         return this.updating; 
50813     },
50814     
50815     /**
50816      * Suspend the LayoutManager from doing auto-layouts while
50817      * making multiple add or remove calls
50818      */
50819     beginUpdate : function(){
50820         this.updating = true;    
50821     },
50822     
50823     /**
50824      * Restore auto-layouts and optionally disable the manager from performing a layout
50825      * @param {Boolean} noLayout true to disable a layout update 
50826      */
50827     endUpdate : function(noLayout){
50828         this.updating = false;
50829         if(!noLayout){
50830             this.layout();
50831         }    
50832     },
50833     
50834     layout: function(){
50835         
50836     },
50837     
50838     onRegionResized : function(region, newSize){
50839         this.fireEvent("regionresized", region, newSize);
50840         this.layout();
50841     },
50842     
50843     onRegionCollapsed : function(region){
50844         this.fireEvent("regioncollapsed", region);
50845     },
50846     
50847     onRegionExpanded : function(region){
50848         this.fireEvent("regionexpanded", region);
50849     },
50850         
50851     /**
50852      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50853      * performs box-model adjustments.
50854      * @return {Object} The size as an object {width: (the width), height: (the height)}
50855      */
50856     getViewSize : function(){
50857         var size;
50858         if(this.el.dom != document.body){
50859             size = this.el.getSize();
50860         }else{
50861             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50862         }
50863         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50864         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50865         return size;
50866     },
50867     
50868     /**
50869      * Returns the Element this layout is bound to.
50870      * @return {Roo.Element}
50871      */
50872     getEl : function(){
50873         return this.el;
50874     },
50875     
50876     /**
50877      * Returns the specified region.
50878      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50879      * @return {Roo.LayoutRegion}
50880      */
50881     getRegion : function(target){
50882         return this.regions[target.toLowerCase()];
50883     },
50884     
50885     onWindowResize : function(){
50886         if(this.monitorWindowResize){
50887             this.layout();
50888         }
50889     }
50890 });/*
50891  * Based on:
50892  * Ext JS Library 1.1.1
50893  * Copyright(c) 2006-2007, Ext JS, LLC.
50894  *
50895  * Originally Released Under LGPL - original licence link has changed is not relivant.
50896  *
50897  * Fork - LGPL
50898  * <script type="text/javascript">
50899  */
50900 /**
50901  * @class Roo.BorderLayout
50902  * @extends Roo.LayoutManager
50903  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50904  * please see: <br><br>
50905  * <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>
50906  * <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>
50907  * Example:
50908  <pre><code>
50909  var layout = new Roo.BorderLayout(document.body, {
50910     north: {
50911         initialSize: 25,
50912         titlebar: false
50913     },
50914     west: {
50915         split:true,
50916         initialSize: 200,
50917         minSize: 175,
50918         maxSize: 400,
50919         titlebar: true,
50920         collapsible: true
50921     },
50922     east: {
50923         split:true,
50924         initialSize: 202,
50925         minSize: 175,
50926         maxSize: 400,
50927         titlebar: true,
50928         collapsible: true
50929     },
50930     south: {
50931         split:true,
50932         initialSize: 100,
50933         minSize: 100,
50934         maxSize: 200,
50935         titlebar: true,
50936         collapsible: true
50937     },
50938     center: {
50939         titlebar: true,
50940         autoScroll:true,
50941         resizeTabs: true,
50942         minTabWidth: 50,
50943         preferredTabWidth: 150
50944     }
50945 });
50946
50947 // shorthand
50948 var CP = Roo.ContentPanel;
50949
50950 layout.beginUpdate();
50951 layout.add("north", new CP("north", "North"));
50952 layout.add("south", new CP("south", {title: "South", closable: true}));
50953 layout.add("west", new CP("west", {title: "West"}));
50954 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50955 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50956 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50957 layout.getRegion("center").showPanel("center1");
50958 layout.endUpdate();
50959 </code></pre>
50960
50961 <b>The container the layout is rendered into can be either the body element or any other element.
50962 If it is not the body element, the container needs to either be an absolute positioned element,
50963 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50964 the container size if it is not the body element.</b>
50965
50966 * @constructor
50967 * Create a new BorderLayout
50968 * @param {String/HTMLElement/Element} container The container this layout is bound to
50969 * @param {Object} config Configuration options
50970  */
50971 Roo.BorderLayout = function(container, config){
50972     config = config || {};
50973     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50974     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50975     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50976         var target = this.factory.validRegions[i];
50977         if(config[target]){
50978             this.addRegion(target, config[target]);
50979         }
50980     }
50981 };
50982
50983 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50984     /**
50985      * Creates and adds a new region if it doesn't already exist.
50986      * @param {String} target The target region key (north, south, east, west or center).
50987      * @param {Object} config The regions config object
50988      * @return {BorderLayoutRegion} The new region
50989      */
50990     addRegion : function(target, config){
50991         if(!this.regions[target]){
50992             var r = this.factory.create(target, this, config);
50993             this.bindRegion(target, r);
50994         }
50995         return this.regions[target];
50996     },
50997
50998     // private (kinda)
50999     bindRegion : function(name, r){
51000         this.regions[name] = r;
51001         r.on("visibilitychange", this.layout, this);
51002         r.on("paneladded", this.layout, this);
51003         r.on("panelremoved", this.layout, this);
51004         r.on("invalidated", this.layout, this);
51005         r.on("resized", this.onRegionResized, this);
51006         r.on("collapsed", this.onRegionCollapsed, this);
51007         r.on("expanded", this.onRegionExpanded, this);
51008     },
51009
51010     /**
51011      * Performs a layout update.
51012      */
51013     layout : function(){
51014         if(this.updating) {
51015             return;
51016         }
51017         var size = this.getViewSize();
51018         var w = size.width;
51019         var h = size.height;
51020         var centerW = w;
51021         var centerH = h;
51022         var centerY = 0;
51023         var centerX = 0;
51024         //var x = 0, y = 0;
51025
51026         var rs = this.regions;
51027         var north = rs["north"];
51028         var south = rs["south"]; 
51029         var west = rs["west"];
51030         var east = rs["east"];
51031         var center = rs["center"];
51032         //if(this.hideOnLayout){ // not supported anymore
51033             //c.el.setStyle("display", "none");
51034         //}
51035         if(north && north.isVisible()){
51036             var b = north.getBox();
51037             var m = north.getMargins();
51038             b.width = w - (m.left+m.right);
51039             b.x = m.left;
51040             b.y = m.top;
51041             centerY = b.height + b.y + m.bottom;
51042             centerH -= centerY;
51043             north.updateBox(this.safeBox(b));
51044         }
51045         if(south && south.isVisible()){
51046             var b = south.getBox();
51047             var m = south.getMargins();
51048             b.width = w - (m.left+m.right);
51049             b.x = m.left;
51050             var totalHeight = (b.height + m.top + m.bottom);
51051             b.y = h - totalHeight + m.top;
51052             centerH -= totalHeight;
51053             south.updateBox(this.safeBox(b));
51054         }
51055         if(west && west.isVisible()){
51056             var b = west.getBox();
51057             var m = west.getMargins();
51058             b.height = centerH - (m.top+m.bottom);
51059             b.x = m.left;
51060             b.y = centerY + m.top;
51061             var totalWidth = (b.width + m.left + m.right);
51062             centerX += totalWidth;
51063             centerW -= totalWidth;
51064             west.updateBox(this.safeBox(b));
51065         }
51066         if(east && east.isVisible()){
51067             var b = east.getBox();
51068             var m = east.getMargins();
51069             b.height = centerH - (m.top+m.bottom);
51070             var totalWidth = (b.width + m.left + m.right);
51071             b.x = w - totalWidth + m.left;
51072             b.y = centerY + m.top;
51073             centerW -= totalWidth;
51074             east.updateBox(this.safeBox(b));
51075         }
51076         if(center){
51077             var m = center.getMargins();
51078             var centerBox = {
51079                 x: centerX + m.left,
51080                 y: centerY + m.top,
51081                 width: centerW - (m.left+m.right),
51082                 height: centerH - (m.top+m.bottom)
51083             };
51084             //if(this.hideOnLayout){
51085                 //center.el.setStyle("display", "block");
51086             //}
51087             center.updateBox(this.safeBox(centerBox));
51088         }
51089         this.el.repaint();
51090         this.fireEvent("layout", this);
51091     },
51092
51093     // private
51094     safeBox : function(box){
51095         box.width = Math.max(0, box.width);
51096         box.height = Math.max(0, box.height);
51097         return box;
51098     },
51099
51100     /**
51101      * Adds a ContentPanel (or subclass) to this layout.
51102      * @param {String} target The target region key (north, south, east, west or center).
51103      * @param {Roo.ContentPanel} panel The panel to add
51104      * @return {Roo.ContentPanel} The added panel
51105      */
51106     add : function(target, panel){
51107          
51108         target = target.toLowerCase();
51109         return this.regions[target].add(panel);
51110     },
51111
51112     /**
51113      * Remove a ContentPanel (or subclass) to this layout.
51114      * @param {String} target The target region key (north, south, east, west or center).
51115      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51116      * @return {Roo.ContentPanel} The removed panel
51117      */
51118     remove : function(target, panel){
51119         target = target.toLowerCase();
51120         return this.regions[target].remove(panel);
51121     },
51122
51123     /**
51124      * Searches all regions for a panel with the specified id
51125      * @param {String} panelId
51126      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51127      */
51128     findPanel : function(panelId){
51129         var rs = this.regions;
51130         for(var target in rs){
51131             if(typeof rs[target] != "function"){
51132                 var p = rs[target].getPanel(panelId);
51133                 if(p){
51134                     return p;
51135                 }
51136             }
51137         }
51138         return null;
51139     },
51140
51141     /**
51142      * Searches all regions for a panel with the specified id and activates (shows) it.
51143      * @param {String/ContentPanel} panelId The panels id or the panel itself
51144      * @return {Roo.ContentPanel} The shown panel or null
51145      */
51146     showPanel : function(panelId) {
51147       var rs = this.regions;
51148       for(var target in rs){
51149          var r = rs[target];
51150          if(typeof r != "function"){
51151             if(r.hasPanel(panelId)){
51152                return r.showPanel(panelId);
51153             }
51154          }
51155       }
51156       return null;
51157    },
51158
51159    /**
51160      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51161      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51162      */
51163     restoreState : function(provider){
51164         if(!provider){
51165             provider = Roo.state.Manager;
51166         }
51167         var sm = new Roo.LayoutStateManager();
51168         sm.init(this, provider);
51169     },
51170
51171     /**
51172      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51173      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51174      * a valid ContentPanel config object.  Example:
51175      * <pre><code>
51176 // Create the main layout
51177 var layout = new Roo.BorderLayout('main-ct', {
51178     west: {
51179         split:true,
51180         minSize: 175,
51181         titlebar: true
51182     },
51183     center: {
51184         title:'Components'
51185     }
51186 }, 'main-ct');
51187
51188 // Create and add multiple ContentPanels at once via configs
51189 layout.batchAdd({
51190    west: {
51191        id: 'source-files',
51192        autoCreate:true,
51193        title:'Ext Source Files',
51194        autoScroll:true,
51195        fitToFrame:true
51196    },
51197    center : {
51198        el: cview,
51199        autoScroll:true,
51200        fitToFrame:true,
51201        toolbar: tb,
51202        resizeEl:'cbody'
51203    }
51204 });
51205 </code></pre>
51206      * @param {Object} regions An object containing ContentPanel configs by region name
51207      */
51208     batchAdd : function(regions){
51209         this.beginUpdate();
51210         for(var rname in regions){
51211             var lr = this.regions[rname];
51212             if(lr){
51213                 this.addTypedPanels(lr, regions[rname]);
51214             }
51215         }
51216         this.endUpdate();
51217     },
51218
51219     // private
51220     addTypedPanels : function(lr, ps){
51221         if(typeof ps == 'string'){
51222             lr.add(new Roo.ContentPanel(ps));
51223         }
51224         else if(ps instanceof Array){
51225             for(var i =0, len = ps.length; i < len; i++){
51226                 this.addTypedPanels(lr, ps[i]);
51227             }
51228         }
51229         else if(!ps.events){ // raw config?
51230             var el = ps.el;
51231             delete ps.el; // prevent conflict
51232             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51233         }
51234         else {  // panel object assumed!
51235             lr.add(ps);
51236         }
51237     },
51238     /**
51239      * Adds a xtype elements to the layout.
51240      * <pre><code>
51241
51242 layout.addxtype({
51243        xtype : 'ContentPanel',
51244        region: 'west',
51245        items: [ .... ]
51246    }
51247 );
51248
51249 layout.addxtype({
51250         xtype : 'NestedLayoutPanel',
51251         region: 'west',
51252         layout: {
51253            center: { },
51254            west: { }   
51255         },
51256         items : [ ... list of content panels or nested layout panels.. ]
51257    }
51258 );
51259 </code></pre>
51260      * @param {Object} cfg Xtype definition of item to add.
51261      */
51262     addxtype : function(cfg)
51263     {
51264         // basically accepts a pannel...
51265         // can accept a layout region..!?!?
51266         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51267         
51268         if (!cfg.xtype.match(/Panel$/)) {
51269             return false;
51270         }
51271         var ret = false;
51272         
51273         if (typeof(cfg.region) == 'undefined') {
51274             Roo.log("Failed to add Panel, region was not set");
51275             Roo.log(cfg);
51276             return false;
51277         }
51278         var region = cfg.region;
51279         delete cfg.region;
51280         
51281           
51282         var xitems = [];
51283         if (cfg.items) {
51284             xitems = cfg.items;
51285             delete cfg.items;
51286         }
51287         var nb = false;
51288         
51289         switch(cfg.xtype) 
51290         {
51291             case 'ContentPanel':  // ContentPanel (el, cfg)
51292             case 'ScrollPanel':  // ContentPanel (el, cfg)
51293             case 'ViewPanel': 
51294                 if(cfg.autoCreate) {
51295                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51296                 } else {
51297                     var el = this.el.createChild();
51298                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51299                 }
51300                 
51301                 this.add(region, ret);
51302                 break;
51303             
51304             
51305             case 'TreePanel': // our new panel!
51306                 cfg.el = this.el.createChild();
51307                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51308                 this.add(region, ret);
51309                 break;
51310             
51311             case 'NestedLayoutPanel': 
51312                 // create a new Layout (which is  a Border Layout...
51313                 var el = this.el.createChild();
51314                 var clayout = cfg.layout;
51315                 delete cfg.layout;
51316                 clayout.items   = clayout.items  || [];
51317                 // replace this exitems with the clayout ones..
51318                 xitems = clayout.items;
51319                  
51320                 
51321                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51322                     cfg.background = false;
51323                 }
51324                 var layout = new Roo.BorderLayout(el, clayout);
51325                 
51326                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51327                 //console.log('adding nested layout panel '  + cfg.toSource());
51328                 this.add(region, ret);
51329                 nb = {}; /// find first...
51330                 break;
51331                 
51332             case 'GridPanel': 
51333             
51334                 // needs grid and region
51335                 
51336                 //var el = this.getRegion(region).el.createChild();
51337                 var el = this.el.createChild();
51338                 // create the grid first...
51339                 
51340                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51341                 delete cfg.grid;
51342                 if (region == 'center' && this.active ) {
51343                     cfg.background = false;
51344                 }
51345                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51346                 
51347                 this.add(region, ret);
51348                 if (cfg.background) {
51349                     ret.on('activate', function(gp) {
51350                         if (!gp.grid.rendered) {
51351                             gp.grid.render();
51352                         }
51353                     });
51354                 } else {
51355                     grid.render();
51356                 }
51357                 break;
51358            
51359            
51360            
51361                 
51362                 
51363                 
51364             default:
51365                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51366                     
51367                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51368                     this.add(region, ret);
51369                 } else {
51370                 
51371                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51372                     return null;
51373                 }
51374                 
51375              // GridPanel (grid, cfg)
51376             
51377         }
51378         this.beginUpdate();
51379         // add children..
51380         var region = '';
51381         var abn = {};
51382         Roo.each(xitems, function(i)  {
51383             region = nb && i.region ? i.region : false;
51384             
51385             var add = ret.addxtype(i);
51386            
51387             if (region) {
51388                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51389                 if (!i.background) {
51390                     abn[region] = nb[region] ;
51391                 }
51392             }
51393             
51394         });
51395         this.endUpdate();
51396
51397         // make the last non-background panel active..
51398         //if (nb) { Roo.log(abn); }
51399         if (nb) {
51400             
51401             for(var r in abn) {
51402                 region = this.getRegion(r);
51403                 if (region) {
51404                     // tried using nb[r], but it does not work..
51405                      
51406                     region.showPanel(abn[r]);
51407                    
51408                 }
51409             }
51410         }
51411         return ret;
51412         
51413     }
51414 });
51415
51416 /**
51417  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51418  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51419  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51420  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51421  * <pre><code>
51422 // shorthand
51423 var CP = Roo.ContentPanel;
51424
51425 var layout = Roo.BorderLayout.create({
51426     north: {
51427         initialSize: 25,
51428         titlebar: false,
51429         panels: [new CP("north", "North")]
51430     },
51431     west: {
51432         split:true,
51433         initialSize: 200,
51434         minSize: 175,
51435         maxSize: 400,
51436         titlebar: true,
51437         collapsible: true,
51438         panels: [new CP("west", {title: "West"})]
51439     },
51440     east: {
51441         split:true,
51442         initialSize: 202,
51443         minSize: 175,
51444         maxSize: 400,
51445         titlebar: true,
51446         collapsible: true,
51447         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51448     },
51449     south: {
51450         split:true,
51451         initialSize: 100,
51452         minSize: 100,
51453         maxSize: 200,
51454         titlebar: true,
51455         collapsible: true,
51456         panels: [new CP("south", {title: "South", closable: true})]
51457     },
51458     center: {
51459         titlebar: true,
51460         autoScroll:true,
51461         resizeTabs: true,
51462         minTabWidth: 50,
51463         preferredTabWidth: 150,
51464         panels: [
51465             new CP("center1", {title: "Close Me", closable: true}),
51466             new CP("center2", {title: "Center Panel", closable: false})
51467         ]
51468     }
51469 }, document.body);
51470
51471 layout.getRegion("center").showPanel("center1");
51472 </code></pre>
51473  * @param config
51474  * @param targetEl
51475  */
51476 Roo.BorderLayout.create = function(config, targetEl){
51477     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51478     layout.beginUpdate();
51479     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51480     for(var j = 0, jlen = regions.length; j < jlen; j++){
51481         var lr = regions[j];
51482         if(layout.regions[lr] && config[lr].panels){
51483             var r = layout.regions[lr];
51484             var ps = config[lr].panels;
51485             layout.addTypedPanels(r, ps);
51486         }
51487     }
51488     layout.endUpdate();
51489     return layout;
51490 };
51491
51492 // private
51493 Roo.BorderLayout.RegionFactory = {
51494     // private
51495     validRegions : ["north","south","east","west","center"],
51496
51497     // private
51498     create : function(target, mgr, config){
51499         target = target.toLowerCase();
51500         if(config.lightweight || config.basic){
51501             return new Roo.BasicLayoutRegion(mgr, config, target);
51502         }
51503         switch(target){
51504             case "north":
51505                 return new Roo.NorthLayoutRegion(mgr, config);
51506             case "south":
51507                 return new Roo.SouthLayoutRegion(mgr, config);
51508             case "east":
51509                 return new Roo.EastLayoutRegion(mgr, config);
51510             case "west":
51511                 return new Roo.WestLayoutRegion(mgr, config);
51512             case "center":
51513                 return new Roo.CenterLayoutRegion(mgr, config);
51514         }
51515         throw 'Layout region "'+target+'" not supported.';
51516     }
51517 };/*
51518  * Based on:
51519  * Ext JS Library 1.1.1
51520  * Copyright(c) 2006-2007, Ext JS, LLC.
51521  *
51522  * Originally Released Under LGPL - original licence link has changed is not relivant.
51523  *
51524  * Fork - LGPL
51525  * <script type="text/javascript">
51526  */
51527  
51528 /**
51529  * @class Roo.BasicLayoutRegion
51530  * @extends Roo.util.Observable
51531  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51532  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51533  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51534  */
51535 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51536     this.mgr = mgr;
51537     this.position  = pos;
51538     this.events = {
51539         /**
51540          * @scope Roo.BasicLayoutRegion
51541          */
51542         
51543         /**
51544          * @event beforeremove
51545          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51546          * @param {Roo.LayoutRegion} this
51547          * @param {Roo.ContentPanel} panel The panel
51548          * @param {Object} e The cancel event object
51549          */
51550         "beforeremove" : true,
51551         /**
51552          * @event invalidated
51553          * Fires when the layout for this region is changed.
51554          * @param {Roo.LayoutRegion} this
51555          */
51556         "invalidated" : true,
51557         /**
51558          * @event visibilitychange
51559          * Fires when this region is shown or hidden 
51560          * @param {Roo.LayoutRegion} this
51561          * @param {Boolean} visibility true or false
51562          */
51563         "visibilitychange" : true,
51564         /**
51565          * @event paneladded
51566          * Fires when a panel is added. 
51567          * @param {Roo.LayoutRegion} this
51568          * @param {Roo.ContentPanel} panel The panel
51569          */
51570         "paneladded" : true,
51571         /**
51572          * @event panelremoved
51573          * Fires when a panel is removed. 
51574          * @param {Roo.LayoutRegion} this
51575          * @param {Roo.ContentPanel} panel The panel
51576          */
51577         "panelremoved" : true,
51578         /**
51579          * @event beforecollapse
51580          * Fires when this region before collapse.
51581          * @param {Roo.LayoutRegion} this
51582          */
51583         "beforecollapse" : true,
51584         /**
51585          * @event collapsed
51586          * Fires when this region is collapsed.
51587          * @param {Roo.LayoutRegion} this
51588          */
51589         "collapsed" : true,
51590         /**
51591          * @event expanded
51592          * Fires when this region is expanded.
51593          * @param {Roo.LayoutRegion} this
51594          */
51595         "expanded" : true,
51596         /**
51597          * @event slideshow
51598          * Fires when this region is slid into view.
51599          * @param {Roo.LayoutRegion} this
51600          */
51601         "slideshow" : true,
51602         /**
51603          * @event slidehide
51604          * Fires when this region slides out of view. 
51605          * @param {Roo.LayoutRegion} this
51606          */
51607         "slidehide" : true,
51608         /**
51609          * @event panelactivated
51610          * Fires when a panel is activated. 
51611          * @param {Roo.LayoutRegion} this
51612          * @param {Roo.ContentPanel} panel The activated panel
51613          */
51614         "panelactivated" : true,
51615         /**
51616          * @event resized
51617          * Fires when the user resizes this region. 
51618          * @param {Roo.LayoutRegion} this
51619          * @param {Number} newSize The new size (width for east/west, height for north/south)
51620          */
51621         "resized" : true
51622     };
51623     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51624     this.panels = new Roo.util.MixedCollection();
51625     this.panels.getKey = this.getPanelId.createDelegate(this);
51626     this.box = null;
51627     this.activePanel = null;
51628     // ensure listeners are added...
51629     
51630     if (config.listeners || config.events) {
51631         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51632             listeners : config.listeners || {},
51633             events : config.events || {}
51634         });
51635     }
51636     
51637     if(skipConfig !== true){
51638         this.applyConfig(config);
51639     }
51640 };
51641
51642 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51643     getPanelId : function(p){
51644         return p.getId();
51645     },
51646     
51647     applyConfig : function(config){
51648         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51649         this.config = config;
51650         
51651     },
51652     
51653     /**
51654      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51655      * the width, for horizontal (north, south) the height.
51656      * @param {Number} newSize The new width or height
51657      */
51658     resizeTo : function(newSize){
51659         var el = this.el ? this.el :
51660                  (this.activePanel ? this.activePanel.getEl() : null);
51661         if(el){
51662             switch(this.position){
51663                 case "east":
51664                 case "west":
51665                     el.setWidth(newSize);
51666                     this.fireEvent("resized", this, newSize);
51667                 break;
51668                 case "north":
51669                 case "south":
51670                     el.setHeight(newSize);
51671                     this.fireEvent("resized", this, newSize);
51672                 break;                
51673             }
51674         }
51675     },
51676     
51677     getBox : function(){
51678         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51679     },
51680     
51681     getMargins : function(){
51682         return this.margins;
51683     },
51684     
51685     updateBox : function(box){
51686         this.box = box;
51687         var el = this.activePanel.getEl();
51688         el.dom.style.left = box.x + "px";
51689         el.dom.style.top = box.y + "px";
51690         this.activePanel.setSize(box.width, box.height);
51691     },
51692     
51693     /**
51694      * Returns the container element for this region.
51695      * @return {Roo.Element}
51696      */
51697     getEl : function(){
51698         return this.activePanel;
51699     },
51700     
51701     /**
51702      * Returns true if this region is currently visible.
51703      * @return {Boolean}
51704      */
51705     isVisible : function(){
51706         return this.activePanel ? true : false;
51707     },
51708     
51709     setActivePanel : function(panel){
51710         panel = this.getPanel(panel);
51711         if(this.activePanel && this.activePanel != panel){
51712             this.activePanel.setActiveState(false);
51713             this.activePanel.getEl().setLeftTop(-10000,-10000);
51714         }
51715         this.activePanel = panel;
51716         panel.setActiveState(true);
51717         if(this.box){
51718             panel.setSize(this.box.width, this.box.height);
51719         }
51720         this.fireEvent("panelactivated", this, panel);
51721         this.fireEvent("invalidated");
51722     },
51723     
51724     /**
51725      * Show the specified panel.
51726      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51727      * @return {Roo.ContentPanel} The shown panel or null
51728      */
51729     showPanel : function(panel){
51730         if(panel = this.getPanel(panel)){
51731             this.setActivePanel(panel);
51732         }
51733         return panel;
51734     },
51735     
51736     /**
51737      * Get the active panel for this region.
51738      * @return {Roo.ContentPanel} The active panel or null
51739      */
51740     getActivePanel : function(){
51741         return this.activePanel;
51742     },
51743     
51744     /**
51745      * Add the passed ContentPanel(s)
51746      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51747      * @return {Roo.ContentPanel} The panel added (if only one was added)
51748      */
51749     add : function(panel){
51750         if(arguments.length > 1){
51751             for(var i = 0, len = arguments.length; i < len; i++) {
51752                 this.add(arguments[i]);
51753             }
51754             return null;
51755         }
51756         if(this.hasPanel(panel)){
51757             this.showPanel(panel);
51758             return panel;
51759         }
51760         var el = panel.getEl();
51761         if(el.dom.parentNode != this.mgr.el.dom){
51762             this.mgr.el.dom.appendChild(el.dom);
51763         }
51764         if(panel.setRegion){
51765             panel.setRegion(this);
51766         }
51767         this.panels.add(panel);
51768         el.setStyle("position", "absolute");
51769         if(!panel.background){
51770             this.setActivePanel(panel);
51771             if(this.config.initialSize && this.panels.getCount()==1){
51772                 this.resizeTo(this.config.initialSize);
51773             }
51774         }
51775         this.fireEvent("paneladded", this, panel);
51776         return panel;
51777     },
51778     
51779     /**
51780      * Returns true if the panel is in this region.
51781      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51782      * @return {Boolean}
51783      */
51784     hasPanel : function(panel){
51785         if(typeof panel == "object"){ // must be panel obj
51786             panel = panel.getId();
51787         }
51788         return this.getPanel(panel) ? true : false;
51789     },
51790     
51791     /**
51792      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51793      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51794      * @param {Boolean} preservePanel Overrides the config preservePanel option
51795      * @return {Roo.ContentPanel} The panel that was removed
51796      */
51797     remove : function(panel, preservePanel){
51798         panel = this.getPanel(panel);
51799         if(!panel){
51800             return null;
51801         }
51802         var e = {};
51803         this.fireEvent("beforeremove", this, panel, e);
51804         if(e.cancel === true){
51805             return null;
51806         }
51807         var panelId = panel.getId();
51808         this.panels.removeKey(panelId);
51809         return panel;
51810     },
51811     
51812     /**
51813      * Returns the panel specified or null if it's not in this region.
51814      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51815      * @return {Roo.ContentPanel}
51816      */
51817     getPanel : function(id){
51818         if(typeof id == "object"){ // must be panel obj
51819             return id;
51820         }
51821         return this.panels.get(id);
51822     },
51823     
51824     /**
51825      * Returns this regions position (north/south/east/west/center).
51826      * @return {String} 
51827      */
51828     getPosition: function(){
51829         return this.position;    
51830     }
51831 });/*
51832  * Based on:
51833  * Ext JS Library 1.1.1
51834  * Copyright(c) 2006-2007, Ext JS, LLC.
51835  *
51836  * Originally Released Under LGPL - original licence link has changed is not relivant.
51837  *
51838  * Fork - LGPL
51839  * <script type="text/javascript">
51840  */
51841  
51842 /**
51843  * @class Roo.LayoutRegion
51844  * @extends Roo.BasicLayoutRegion
51845  * This class represents a region in a layout manager.
51846  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51847  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51848  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51849  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51850  * @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})
51851  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51852  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51853  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51854  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51855  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51856  * @cfg {String}    title           The title for the region (overrides panel titles)
51857  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51858  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51859  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51860  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51861  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51862  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51863  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51864  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51865  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51866  * @cfg {Boolean}   showPin         True to show a pin button
51867  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51868  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51869  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51870  * @cfg {Number}    width           For East/West panels
51871  * @cfg {Number}    height          For North/South panels
51872  * @cfg {Boolean}   split           To show the splitter
51873  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51874  */
51875 Roo.LayoutRegion = function(mgr, config, pos){
51876     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51877     var dh = Roo.DomHelper;
51878     /** This region's container element 
51879     * @type Roo.Element */
51880     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51881     /** This region's title element 
51882     * @type Roo.Element */
51883
51884     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51885         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51886         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51887     ]}, true);
51888     this.titleEl.enableDisplayMode();
51889     /** This region's title text element 
51890     * @type HTMLElement */
51891     this.titleTextEl = this.titleEl.dom.firstChild;
51892     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51893     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51894     this.closeBtn.enableDisplayMode();
51895     this.closeBtn.on("click", this.closeClicked, this);
51896     this.closeBtn.hide();
51897
51898     this.createBody(config);
51899     this.visible = true;
51900     this.collapsed = false;
51901
51902     if(config.hideWhenEmpty){
51903         this.hide();
51904         this.on("paneladded", this.validateVisibility, this);
51905         this.on("panelremoved", this.validateVisibility, this);
51906     }
51907     this.applyConfig(config);
51908 };
51909
51910 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51911
51912     createBody : function(){
51913         /** This region's body element 
51914         * @type Roo.Element */
51915         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51916     },
51917
51918     applyConfig : function(c){
51919         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51920             var dh = Roo.DomHelper;
51921             if(c.titlebar !== false){
51922                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51923                 this.collapseBtn.on("click", this.collapse, this);
51924                 this.collapseBtn.enableDisplayMode();
51925
51926                 if(c.showPin === true || this.showPin){
51927                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51928                     this.stickBtn.enableDisplayMode();
51929                     this.stickBtn.on("click", this.expand, this);
51930                     this.stickBtn.hide();
51931                 }
51932             }
51933             /** This region's collapsed element
51934             * @type Roo.Element */
51935             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51936                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51937             ]}, true);
51938             if(c.floatable !== false){
51939                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51940                this.collapsedEl.on("click", this.collapseClick, this);
51941             }
51942
51943             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51944                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51945                    id: "message", unselectable: "on", style:{"float":"left"}});
51946                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51947              }
51948             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51949             this.expandBtn.on("click", this.expand, this);
51950         }
51951         if(this.collapseBtn){
51952             this.collapseBtn.setVisible(c.collapsible == true);
51953         }
51954         this.cmargins = c.cmargins || this.cmargins ||
51955                          (this.position == "west" || this.position == "east" ?
51956                              {top: 0, left: 2, right:2, bottom: 0} :
51957                              {top: 2, left: 0, right:0, bottom: 2});
51958         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51959         this.bottomTabs = c.tabPosition != "top";
51960         this.autoScroll = c.autoScroll || false;
51961         if(this.autoScroll){
51962             this.bodyEl.setStyle("overflow", "auto");
51963         }else{
51964             this.bodyEl.setStyle("overflow", "hidden");
51965         }
51966         //if(c.titlebar !== false){
51967             if((!c.titlebar && !c.title) || c.titlebar === false){
51968                 this.titleEl.hide();
51969             }else{
51970                 this.titleEl.show();
51971                 if(c.title){
51972                     this.titleTextEl.innerHTML = c.title;
51973                 }
51974             }
51975         //}
51976         this.duration = c.duration || .30;
51977         this.slideDuration = c.slideDuration || .45;
51978         this.config = c;
51979         if(c.collapsed){
51980             this.collapse(true);
51981         }
51982         if(c.hidden){
51983             this.hide();
51984         }
51985     },
51986     /**
51987      * Returns true if this region is currently visible.
51988      * @return {Boolean}
51989      */
51990     isVisible : function(){
51991         return this.visible;
51992     },
51993
51994     /**
51995      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51996      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51997      */
51998     setCollapsedTitle : function(title){
51999         title = title || "&#160;";
52000         if(this.collapsedTitleTextEl){
52001             this.collapsedTitleTextEl.innerHTML = title;
52002         }
52003     },
52004
52005     getBox : function(){
52006         var b;
52007         if(!this.collapsed){
52008             b = this.el.getBox(false, true);
52009         }else{
52010             b = this.collapsedEl.getBox(false, true);
52011         }
52012         return b;
52013     },
52014
52015     getMargins : function(){
52016         return this.collapsed ? this.cmargins : this.margins;
52017     },
52018
52019     highlight : function(){
52020         this.el.addClass("x-layout-panel-dragover");
52021     },
52022
52023     unhighlight : function(){
52024         this.el.removeClass("x-layout-panel-dragover");
52025     },
52026
52027     updateBox : function(box){
52028         this.box = box;
52029         if(!this.collapsed){
52030             this.el.dom.style.left = box.x + "px";
52031             this.el.dom.style.top = box.y + "px";
52032             this.updateBody(box.width, box.height);
52033         }else{
52034             this.collapsedEl.dom.style.left = box.x + "px";
52035             this.collapsedEl.dom.style.top = box.y + "px";
52036             this.collapsedEl.setSize(box.width, box.height);
52037         }
52038         if(this.tabs){
52039             this.tabs.autoSizeTabs();
52040         }
52041     },
52042
52043     updateBody : function(w, h){
52044         if(w !== null){
52045             this.el.setWidth(w);
52046             w -= this.el.getBorderWidth("rl");
52047             if(this.config.adjustments){
52048                 w += this.config.adjustments[0];
52049             }
52050         }
52051         if(h !== null){
52052             this.el.setHeight(h);
52053             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52054             h -= this.el.getBorderWidth("tb");
52055             if(this.config.adjustments){
52056                 h += this.config.adjustments[1];
52057             }
52058             this.bodyEl.setHeight(h);
52059             if(this.tabs){
52060                 h = this.tabs.syncHeight(h);
52061             }
52062         }
52063         if(this.panelSize){
52064             w = w !== null ? w : this.panelSize.width;
52065             h = h !== null ? h : this.panelSize.height;
52066         }
52067         if(this.activePanel){
52068             var el = this.activePanel.getEl();
52069             w = w !== null ? w : el.getWidth();
52070             h = h !== null ? h : el.getHeight();
52071             this.panelSize = {width: w, height: h};
52072             this.activePanel.setSize(w, h);
52073         }
52074         if(Roo.isIE && this.tabs){
52075             this.tabs.el.repaint();
52076         }
52077     },
52078
52079     /**
52080      * Returns the container element for this region.
52081      * @return {Roo.Element}
52082      */
52083     getEl : function(){
52084         return this.el;
52085     },
52086
52087     /**
52088      * Hides this region.
52089      */
52090     hide : function(){
52091         if(!this.collapsed){
52092             this.el.dom.style.left = "-2000px";
52093             this.el.hide();
52094         }else{
52095             this.collapsedEl.dom.style.left = "-2000px";
52096             this.collapsedEl.hide();
52097         }
52098         this.visible = false;
52099         this.fireEvent("visibilitychange", this, false);
52100     },
52101
52102     /**
52103      * Shows this region if it was previously hidden.
52104      */
52105     show : function(){
52106         if(!this.collapsed){
52107             this.el.show();
52108         }else{
52109             this.collapsedEl.show();
52110         }
52111         this.visible = true;
52112         this.fireEvent("visibilitychange", this, true);
52113     },
52114
52115     closeClicked : function(){
52116         if(this.activePanel){
52117             this.remove(this.activePanel);
52118         }
52119     },
52120
52121     collapseClick : function(e){
52122         if(this.isSlid){
52123            e.stopPropagation();
52124            this.slideIn();
52125         }else{
52126            e.stopPropagation();
52127            this.slideOut();
52128         }
52129     },
52130
52131     /**
52132      * Collapses this region.
52133      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52134      */
52135     collapse : function(skipAnim, skipCheck = false){
52136         if(this.collapsed) {
52137             return;
52138         }
52139         
52140         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52141             
52142             this.collapsed = true;
52143             if(this.split){
52144                 this.split.el.hide();
52145             }
52146             if(this.config.animate && skipAnim !== true){
52147                 this.fireEvent("invalidated", this);
52148                 this.animateCollapse();
52149             }else{
52150                 this.el.setLocation(-20000,-20000);
52151                 this.el.hide();
52152                 this.collapsedEl.show();
52153                 this.fireEvent("collapsed", this);
52154                 this.fireEvent("invalidated", this);
52155             }
52156         }
52157         
52158     },
52159
52160     animateCollapse : function(){
52161         // overridden
52162     },
52163
52164     /**
52165      * Expands this region if it was previously collapsed.
52166      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52167      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52168      */
52169     expand : function(e, skipAnim){
52170         if(e) {
52171             e.stopPropagation();
52172         }
52173         if(!this.collapsed || this.el.hasActiveFx()) {
52174             return;
52175         }
52176         if(this.isSlid){
52177             this.afterSlideIn();
52178             skipAnim = true;
52179         }
52180         this.collapsed = false;
52181         if(this.config.animate && skipAnim !== true){
52182             this.animateExpand();
52183         }else{
52184             this.el.show();
52185             if(this.split){
52186                 this.split.el.show();
52187             }
52188             this.collapsedEl.setLocation(-2000,-2000);
52189             this.collapsedEl.hide();
52190             this.fireEvent("invalidated", this);
52191             this.fireEvent("expanded", this);
52192         }
52193     },
52194
52195     animateExpand : function(){
52196         // overridden
52197     },
52198
52199     initTabs : function()
52200     {
52201         this.bodyEl.setStyle("overflow", "hidden");
52202         var ts = new Roo.TabPanel(
52203                 this.bodyEl.dom,
52204                 {
52205                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52206                     disableTooltips: this.config.disableTabTips,
52207                     toolbar : this.config.toolbar
52208                 }
52209         );
52210         if(this.config.hideTabs){
52211             ts.stripWrap.setDisplayed(false);
52212         }
52213         this.tabs = ts;
52214         ts.resizeTabs = this.config.resizeTabs === true;
52215         ts.minTabWidth = this.config.minTabWidth || 40;
52216         ts.maxTabWidth = this.config.maxTabWidth || 250;
52217         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52218         ts.monitorResize = false;
52219         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52220         ts.bodyEl.addClass('x-layout-tabs-body');
52221         this.panels.each(this.initPanelAsTab, this);
52222     },
52223
52224     initPanelAsTab : function(panel){
52225         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52226                     this.config.closeOnTab && panel.isClosable());
52227         if(panel.tabTip !== undefined){
52228             ti.setTooltip(panel.tabTip);
52229         }
52230         ti.on("activate", function(){
52231               this.setActivePanel(panel);
52232         }, this);
52233         if(this.config.closeOnTab){
52234             ti.on("beforeclose", function(t, e){
52235                 e.cancel = true;
52236                 this.remove(panel);
52237             }, this);
52238         }
52239         return ti;
52240     },
52241
52242     updatePanelTitle : function(panel, title){
52243         if(this.activePanel == panel){
52244             this.updateTitle(title);
52245         }
52246         if(this.tabs){
52247             var ti = this.tabs.getTab(panel.getEl().id);
52248             ti.setText(title);
52249             if(panel.tabTip !== undefined){
52250                 ti.setTooltip(panel.tabTip);
52251             }
52252         }
52253     },
52254
52255     updateTitle : function(title){
52256         if(this.titleTextEl && !this.config.title){
52257             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52258         }
52259     },
52260
52261     setActivePanel : function(panel){
52262         panel = this.getPanel(panel);
52263         if(this.activePanel && this.activePanel != panel){
52264             this.activePanel.setActiveState(false);
52265         }
52266         this.activePanel = panel;
52267         panel.setActiveState(true);
52268         if(this.panelSize){
52269             panel.setSize(this.panelSize.width, this.panelSize.height);
52270         }
52271         if(this.closeBtn){
52272             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52273         }
52274         this.updateTitle(panel.getTitle());
52275         if(this.tabs){
52276             this.fireEvent("invalidated", this);
52277         }
52278         this.fireEvent("panelactivated", this, panel);
52279     },
52280
52281     /**
52282      * Shows the specified panel.
52283      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52284      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52285      */
52286     showPanel : function(panel)
52287     {
52288         panel = this.getPanel(panel);
52289         if(panel){
52290             if(this.tabs){
52291                 var tab = this.tabs.getTab(panel.getEl().id);
52292                 if(tab.isHidden()){
52293                     this.tabs.unhideTab(tab.id);
52294                 }
52295                 tab.activate();
52296             }else{
52297                 this.setActivePanel(panel);
52298             }
52299         }
52300         return panel;
52301     },
52302
52303     /**
52304      * Get the active panel for this region.
52305      * @return {Roo.ContentPanel} The active panel or null
52306      */
52307     getActivePanel : function(){
52308         return this.activePanel;
52309     },
52310
52311     validateVisibility : function(){
52312         if(this.panels.getCount() < 1){
52313             this.updateTitle("&#160;");
52314             this.closeBtn.hide();
52315             this.hide();
52316         }else{
52317             if(!this.isVisible()){
52318                 this.show();
52319             }
52320         }
52321     },
52322
52323     /**
52324      * Adds the passed ContentPanel(s) to this region.
52325      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52326      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52327      */
52328     add : function(panel){
52329         if(arguments.length > 1){
52330             for(var i = 0, len = arguments.length; i < len; i++) {
52331                 this.add(arguments[i]);
52332             }
52333             return null;
52334         }
52335         if(this.hasPanel(panel)){
52336             this.showPanel(panel);
52337             return panel;
52338         }
52339         panel.setRegion(this);
52340         this.panels.add(panel);
52341         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52342             this.bodyEl.dom.appendChild(panel.getEl().dom);
52343             if(panel.background !== true){
52344                 this.setActivePanel(panel);
52345             }
52346             this.fireEvent("paneladded", this, panel);
52347             return panel;
52348         }
52349         if(!this.tabs){
52350             this.initTabs();
52351         }else{
52352             this.initPanelAsTab(panel);
52353         }
52354         if(panel.background !== true){
52355             this.tabs.activate(panel.getEl().id);
52356         }
52357         this.fireEvent("paneladded", this, panel);
52358         return panel;
52359     },
52360
52361     /**
52362      * Hides the tab for the specified panel.
52363      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52364      */
52365     hidePanel : function(panel){
52366         if(this.tabs && (panel = this.getPanel(panel))){
52367             this.tabs.hideTab(panel.getEl().id);
52368         }
52369     },
52370
52371     /**
52372      * Unhides the tab for a previously hidden panel.
52373      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52374      */
52375     unhidePanel : function(panel){
52376         if(this.tabs && (panel = this.getPanel(panel))){
52377             this.tabs.unhideTab(panel.getEl().id);
52378         }
52379     },
52380
52381     clearPanels : function(){
52382         while(this.panels.getCount() > 0){
52383              this.remove(this.panels.first());
52384         }
52385     },
52386
52387     /**
52388      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52389      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52390      * @param {Boolean} preservePanel Overrides the config preservePanel option
52391      * @return {Roo.ContentPanel} The panel that was removed
52392      */
52393     remove : function(panel, preservePanel){
52394         panel = this.getPanel(panel);
52395         if(!panel){
52396             return null;
52397         }
52398         var e = {};
52399         this.fireEvent("beforeremove", this, panel, e);
52400         if(e.cancel === true){
52401             return null;
52402         }
52403         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52404         var panelId = panel.getId();
52405         this.panels.removeKey(panelId);
52406         if(preservePanel){
52407             document.body.appendChild(panel.getEl().dom);
52408         }
52409         if(this.tabs){
52410             this.tabs.removeTab(panel.getEl().id);
52411         }else if (!preservePanel){
52412             this.bodyEl.dom.removeChild(panel.getEl().dom);
52413         }
52414         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52415             var p = this.panels.first();
52416             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52417             tempEl.appendChild(p.getEl().dom);
52418             this.bodyEl.update("");
52419             this.bodyEl.dom.appendChild(p.getEl().dom);
52420             tempEl = null;
52421             this.updateTitle(p.getTitle());
52422             this.tabs = null;
52423             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52424             this.setActivePanel(p);
52425         }
52426         panel.setRegion(null);
52427         if(this.activePanel == panel){
52428             this.activePanel = null;
52429         }
52430         if(this.config.autoDestroy !== false && preservePanel !== true){
52431             try{panel.destroy();}catch(e){}
52432         }
52433         this.fireEvent("panelremoved", this, panel);
52434         return panel;
52435     },
52436
52437     /**
52438      * Returns the TabPanel component used by this region
52439      * @return {Roo.TabPanel}
52440      */
52441     getTabs : function(){
52442         return this.tabs;
52443     },
52444
52445     createTool : function(parentEl, className){
52446         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52447             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52448         btn.addClassOnOver("x-layout-tools-button-over");
52449         return btn;
52450     }
52451 });/*
52452  * Based on:
52453  * Ext JS Library 1.1.1
52454  * Copyright(c) 2006-2007, Ext JS, LLC.
52455  *
52456  * Originally Released Under LGPL - original licence link has changed is not relivant.
52457  *
52458  * Fork - LGPL
52459  * <script type="text/javascript">
52460  */
52461  
52462
52463
52464 /**
52465  * @class Roo.SplitLayoutRegion
52466  * @extends Roo.LayoutRegion
52467  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52468  */
52469 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52470     this.cursor = cursor;
52471     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52472 };
52473
52474 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52475     splitTip : "Drag to resize.",
52476     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52477     useSplitTips : false,
52478
52479     applyConfig : function(config){
52480         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52481         if(config.split){
52482             if(!this.split){
52483                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52484                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52485                 /** The SplitBar for this region 
52486                 * @type Roo.SplitBar */
52487                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52488                 this.split.on("moved", this.onSplitMove, this);
52489                 this.split.useShim = config.useShim === true;
52490                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52491                 if(this.useSplitTips){
52492                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52493                 }
52494                 if(config.collapsible){
52495                     this.split.el.on("dblclick", this.collapse,  this);
52496                 }
52497             }
52498             if(typeof config.minSize != "undefined"){
52499                 this.split.minSize = config.minSize;
52500             }
52501             if(typeof config.maxSize != "undefined"){
52502                 this.split.maxSize = config.maxSize;
52503             }
52504             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52505                 this.hideSplitter();
52506             }
52507         }
52508     },
52509
52510     getHMaxSize : function(){
52511          var cmax = this.config.maxSize || 10000;
52512          var center = this.mgr.getRegion("center");
52513          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52514     },
52515
52516     getVMaxSize : function(){
52517          var cmax = this.config.maxSize || 10000;
52518          var center = this.mgr.getRegion("center");
52519          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52520     },
52521
52522     onSplitMove : function(split, newSize){
52523         this.fireEvent("resized", this, newSize);
52524     },
52525     
52526     /** 
52527      * Returns the {@link Roo.SplitBar} for this region.
52528      * @return {Roo.SplitBar}
52529      */
52530     getSplitBar : function(){
52531         return this.split;
52532     },
52533     
52534     hide : function(){
52535         this.hideSplitter();
52536         Roo.SplitLayoutRegion.superclass.hide.call(this);
52537     },
52538
52539     hideSplitter : function(){
52540         if(this.split){
52541             this.split.el.setLocation(-2000,-2000);
52542             this.split.el.hide();
52543         }
52544     },
52545
52546     show : function(){
52547         if(this.split){
52548             this.split.el.show();
52549         }
52550         Roo.SplitLayoutRegion.superclass.show.call(this);
52551     },
52552     
52553     beforeSlide: function(){
52554         if(Roo.isGecko){// firefox overflow auto bug workaround
52555             this.bodyEl.clip();
52556             if(this.tabs) {
52557                 this.tabs.bodyEl.clip();
52558             }
52559             if(this.activePanel){
52560                 this.activePanel.getEl().clip();
52561                 
52562                 if(this.activePanel.beforeSlide){
52563                     this.activePanel.beforeSlide();
52564                 }
52565             }
52566         }
52567     },
52568     
52569     afterSlide : function(){
52570         if(Roo.isGecko){// firefox overflow auto bug workaround
52571             this.bodyEl.unclip();
52572             if(this.tabs) {
52573                 this.tabs.bodyEl.unclip();
52574             }
52575             if(this.activePanel){
52576                 this.activePanel.getEl().unclip();
52577                 if(this.activePanel.afterSlide){
52578                     this.activePanel.afterSlide();
52579                 }
52580             }
52581         }
52582     },
52583
52584     initAutoHide : function(){
52585         if(this.autoHide !== false){
52586             if(!this.autoHideHd){
52587                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52588                 this.autoHideHd = {
52589                     "mouseout": function(e){
52590                         if(!e.within(this.el, true)){
52591                             st.delay(500);
52592                         }
52593                     },
52594                     "mouseover" : function(e){
52595                         st.cancel();
52596                     },
52597                     scope : this
52598                 };
52599             }
52600             this.el.on(this.autoHideHd);
52601         }
52602     },
52603
52604     clearAutoHide : function(){
52605         if(this.autoHide !== false){
52606             this.el.un("mouseout", this.autoHideHd.mouseout);
52607             this.el.un("mouseover", this.autoHideHd.mouseover);
52608         }
52609     },
52610
52611     clearMonitor : function(){
52612         Roo.get(document).un("click", this.slideInIf, this);
52613     },
52614
52615     // these names are backwards but not changed for compat
52616     slideOut : function(){
52617         if(this.isSlid || this.el.hasActiveFx()){
52618             return;
52619         }
52620         this.isSlid = true;
52621         if(this.collapseBtn){
52622             this.collapseBtn.hide();
52623         }
52624         this.closeBtnState = this.closeBtn.getStyle('display');
52625         this.closeBtn.hide();
52626         if(this.stickBtn){
52627             this.stickBtn.show();
52628         }
52629         this.el.show();
52630         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52631         this.beforeSlide();
52632         this.el.setStyle("z-index", 10001);
52633         this.el.slideIn(this.getSlideAnchor(), {
52634             callback: function(){
52635                 this.afterSlide();
52636                 this.initAutoHide();
52637                 Roo.get(document).on("click", this.slideInIf, this);
52638                 this.fireEvent("slideshow", this);
52639             },
52640             scope: this,
52641             block: true
52642         });
52643     },
52644
52645     afterSlideIn : function(){
52646         this.clearAutoHide();
52647         this.isSlid = false;
52648         this.clearMonitor();
52649         this.el.setStyle("z-index", "");
52650         if(this.collapseBtn){
52651             this.collapseBtn.show();
52652         }
52653         this.closeBtn.setStyle('display', this.closeBtnState);
52654         if(this.stickBtn){
52655             this.stickBtn.hide();
52656         }
52657         this.fireEvent("slidehide", this);
52658     },
52659
52660     slideIn : function(cb){
52661         if(!this.isSlid || this.el.hasActiveFx()){
52662             Roo.callback(cb);
52663             return;
52664         }
52665         this.isSlid = false;
52666         this.beforeSlide();
52667         this.el.slideOut(this.getSlideAnchor(), {
52668             callback: function(){
52669                 this.el.setLeftTop(-10000, -10000);
52670                 this.afterSlide();
52671                 this.afterSlideIn();
52672                 Roo.callback(cb);
52673             },
52674             scope: this,
52675             block: true
52676         });
52677     },
52678     
52679     slideInIf : function(e){
52680         if(!e.within(this.el)){
52681             this.slideIn();
52682         }
52683     },
52684
52685     animateCollapse : function(){
52686         this.beforeSlide();
52687         this.el.setStyle("z-index", 20000);
52688         var anchor = this.getSlideAnchor();
52689         this.el.slideOut(anchor, {
52690             callback : function(){
52691                 this.el.setStyle("z-index", "");
52692                 this.collapsedEl.slideIn(anchor, {duration:.3});
52693                 this.afterSlide();
52694                 this.el.setLocation(-10000,-10000);
52695                 this.el.hide();
52696                 this.fireEvent("collapsed", this);
52697             },
52698             scope: this,
52699             block: true
52700         });
52701     },
52702
52703     animateExpand : function(){
52704         this.beforeSlide();
52705         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52706         this.el.setStyle("z-index", 20000);
52707         this.collapsedEl.hide({
52708             duration:.1
52709         });
52710         this.el.slideIn(this.getSlideAnchor(), {
52711             callback : function(){
52712                 this.el.setStyle("z-index", "");
52713                 this.afterSlide();
52714                 if(this.split){
52715                     this.split.el.show();
52716                 }
52717                 this.fireEvent("invalidated", this);
52718                 this.fireEvent("expanded", this);
52719             },
52720             scope: this,
52721             block: true
52722         });
52723     },
52724
52725     anchors : {
52726         "west" : "left",
52727         "east" : "right",
52728         "north" : "top",
52729         "south" : "bottom"
52730     },
52731
52732     sanchors : {
52733         "west" : "l",
52734         "east" : "r",
52735         "north" : "t",
52736         "south" : "b"
52737     },
52738
52739     canchors : {
52740         "west" : "tl-tr",
52741         "east" : "tr-tl",
52742         "north" : "tl-bl",
52743         "south" : "bl-tl"
52744     },
52745
52746     getAnchor : function(){
52747         return this.anchors[this.position];
52748     },
52749
52750     getCollapseAnchor : function(){
52751         return this.canchors[this.position];
52752     },
52753
52754     getSlideAnchor : function(){
52755         return this.sanchors[this.position];
52756     },
52757
52758     getAlignAdj : function(){
52759         var cm = this.cmargins;
52760         switch(this.position){
52761             case "west":
52762                 return [0, 0];
52763             break;
52764             case "east":
52765                 return [0, 0];
52766             break;
52767             case "north":
52768                 return [0, 0];
52769             break;
52770             case "south":
52771                 return [0, 0];
52772             break;
52773         }
52774     },
52775
52776     getExpandAdj : function(){
52777         var c = this.collapsedEl, cm = this.cmargins;
52778         switch(this.position){
52779             case "west":
52780                 return [-(cm.right+c.getWidth()+cm.left), 0];
52781             break;
52782             case "east":
52783                 return [cm.right+c.getWidth()+cm.left, 0];
52784             break;
52785             case "north":
52786                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52787             break;
52788             case "south":
52789                 return [0, cm.top+cm.bottom+c.getHeight()];
52790             break;
52791         }
52792     }
52793 });/*
52794  * Based on:
52795  * Ext JS Library 1.1.1
52796  * Copyright(c) 2006-2007, Ext JS, LLC.
52797  *
52798  * Originally Released Under LGPL - original licence link has changed is not relivant.
52799  *
52800  * Fork - LGPL
52801  * <script type="text/javascript">
52802  */
52803 /*
52804  * These classes are private internal classes
52805  */
52806 Roo.CenterLayoutRegion = function(mgr, config){
52807     Roo.LayoutRegion.call(this, mgr, config, "center");
52808     this.visible = true;
52809     this.minWidth = config.minWidth || 20;
52810     this.minHeight = config.minHeight || 20;
52811 };
52812
52813 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52814     hide : function(){
52815         // center panel can't be hidden
52816     },
52817     
52818     show : function(){
52819         // center panel can't be hidden
52820     },
52821     
52822     getMinWidth: function(){
52823         return this.minWidth;
52824     },
52825     
52826     getMinHeight: function(){
52827         return this.minHeight;
52828     }
52829 });
52830
52831
52832 Roo.NorthLayoutRegion = function(mgr, config){
52833     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52834     if(this.split){
52835         this.split.placement = Roo.SplitBar.TOP;
52836         this.split.orientation = Roo.SplitBar.VERTICAL;
52837         this.split.el.addClass("x-layout-split-v");
52838     }
52839     var size = config.initialSize || config.height;
52840     if(typeof size != "undefined"){
52841         this.el.setHeight(size);
52842     }
52843 };
52844 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52845     orientation: Roo.SplitBar.VERTICAL,
52846     getBox : function(){
52847         if(this.collapsed){
52848             return this.collapsedEl.getBox();
52849         }
52850         var box = this.el.getBox();
52851         if(this.split){
52852             box.height += this.split.el.getHeight();
52853         }
52854         return box;
52855     },
52856     
52857     updateBox : function(box){
52858         if(this.split && !this.collapsed){
52859             box.height -= this.split.el.getHeight();
52860             this.split.el.setLeft(box.x);
52861             this.split.el.setTop(box.y+box.height);
52862             this.split.el.setWidth(box.width);
52863         }
52864         if(this.collapsed){
52865             this.updateBody(box.width, null);
52866         }
52867         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52868     }
52869 });
52870
52871 Roo.SouthLayoutRegion = function(mgr, config){
52872     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52873     if(this.split){
52874         this.split.placement = Roo.SplitBar.BOTTOM;
52875         this.split.orientation = Roo.SplitBar.VERTICAL;
52876         this.split.el.addClass("x-layout-split-v");
52877     }
52878     var size = config.initialSize || config.height;
52879     if(typeof size != "undefined"){
52880         this.el.setHeight(size);
52881     }
52882 };
52883 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52884     orientation: Roo.SplitBar.VERTICAL,
52885     getBox : function(){
52886         if(this.collapsed){
52887             return this.collapsedEl.getBox();
52888         }
52889         var box = this.el.getBox();
52890         if(this.split){
52891             var sh = this.split.el.getHeight();
52892             box.height += sh;
52893             box.y -= sh;
52894         }
52895         return box;
52896     },
52897     
52898     updateBox : function(box){
52899         if(this.split && !this.collapsed){
52900             var sh = this.split.el.getHeight();
52901             box.height -= sh;
52902             box.y += sh;
52903             this.split.el.setLeft(box.x);
52904             this.split.el.setTop(box.y-sh);
52905             this.split.el.setWidth(box.width);
52906         }
52907         if(this.collapsed){
52908             this.updateBody(box.width, null);
52909         }
52910         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52911     }
52912 });
52913
52914 Roo.EastLayoutRegion = function(mgr, config){
52915     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52916     if(this.split){
52917         this.split.placement = Roo.SplitBar.RIGHT;
52918         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52919         this.split.el.addClass("x-layout-split-h");
52920     }
52921     var size = config.initialSize || config.width;
52922     if(typeof size != "undefined"){
52923         this.el.setWidth(size);
52924     }
52925 };
52926 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52927     orientation: Roo.SplitBar.HORIZONTAL,
52928     getBox : function(){
52929         if(this.collapsed){
52930             return this.collapsedEl.getBox();
52931         }
52932         var box = this.el.getBox();
52933         if(this.split){
52934             var sw = this.split.el.getWidth();
52935             box.width += sw;
52936             box.x -= sw;
52937         }
52938         return box;
52939     },
52940
52941     updateBox : function(box){
52942         if(this.split && !this.collapsed){
52943             var sw = this.split.el.getWidth();
52944             box.width -= sw;
52945             this.split.el.setLeft(box.x);
52946             this.split.el.setTop(box.y);
52947             this.split.el.setHeight(box.height);
52948             box.x += sw;
52949         }
52950         if(this.collapsed){
52951             this.updateBody(null, box.height);
52952         }
52953         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52954     }
52955 });
52956
52957 Roo.WestLayoutRegion = function(mgr, config){
52958     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52959     if(this.split){
52960         this.split.placement = Roo.SplitBar.LEFT;
52961         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52962         this.split.el.addClass("x-layout-split-h");
52963     }
52964     var size = config.initialSize || config.width;
52965     if(typeof size != "undefined"){
52966         this.el.setWidth(size);
52967     }
52968 };
52969 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52970     orientation: Roo.SplitBar.HORIZONTAL,
52971     getBox : function(){
52972         if(this.collapsed){
52973             return this.collapsedEl.getBox();
52974         }
52975         var box = this.el.getBox();
52976         if(this.split){
52977             box.width += this.split.el.getWidth();
52978         }
52979         return box;
52980     },
52981     
52982     updateBox : function(box){
52983         if(this.split && !this.collapsed){
52984             var sw = this.split.el.getWidth();
52985             box.width -= sw;
52986             this.split.el.setLeft(box.x+box.width);
52987             this.split.el.setTop(box.y);
52988             this.split.el.setHeight(box.height);
52989         }
52990         if(this.collapsed){
52991             this.updateBody(null, box.height);
52992         }
52993         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52994     }
52995 });
52996 /*
52997  * Based on:
52998  * Ext JS Library 1.1.1
52999  * Copyright(c) 2006-2007, Ext JS, LLC.
53000  *
53001  * Originally Released Under LGPL - original licence link has changed is not relivant.
53002  *
53003  * Fork - LGPL
53004  * <script type="text/javascript">
53005  */
53006  
53007  
53008 /*
53009  * Private internal class for reading and applying state
53010  */
53011 Roo.LayoutStateManager = function(layout){
53012      // default empty state
53013      this.state = {
53014         north: {},
53015         south: {},
53016         east: {},
53017         west: {}       
53018     };
53019 };
53020
53021 Roo.LayoutStateManager.prototype = {
53022     init : function(layout, provider){
53023         this.provider = provider;
53024         var state = provider.get(layout.id+"-layout-state");
53025         if(state){
53026             var wasUpdating = layout.isUpdating();
53027             if(!wasUpdating){
53028                 layout.beginUpdate();
53029             }
53030             for(var key in state){
53031                 if(typeof state[key] != "function"){
53032                     var rstate = state[key];
53033                     var r = layout.getRegion(key);
53034                     if(r && rstate){
53035                         if(rstate.size){
53036                             r.resizeTo(rstate.size);
53037                         }
53038                         if(rstate.collapsed == true){
53039                             r.collapse(true);
53040                         }else{
53041                             r.expand(null, true);
53042                         }
53043                     }
53044                 }
53045             }
53046             if(!wasUpdating){
53047                 layout.endUpdate();
53048             }
53049             this.state = state; 
53050         }
53051         this.layout = layout;
53052         layout.on("regionresized", this.onRegionResized, this);
53053         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53054         layout.on("regionexpanded", this.onRegionExpanded, this);
53055     },
53056     
53057     storeState : function(){
53058         this.provider.set(this.layout.id+"-layout-state", this.state);
53059     },
53060     
53061     onRegionResized : function(region, newSize){
53062         this.state[region.getPosition()].size = newSize;
53063         this.storeState();
53064     },
53065     
53066     onRegionCollapsed : function(region){
53067         this.state[region.getPosition()].collapsed = true;
53068         this.storeState();
53069     },
53070     
53071     onRegionExpanded : function(region){
53072         this.state[region.getPosition()].collapsed = false;
53073         this.storeState();
53074     }
53075 };/*
53076  * Based on:
53077  * Ext JS Library 1.1.1
53078  * Copyright(c) 2006-2007, Ext JS, LLC.
53079  *
53080  * Originally Released Under LGPL - original licence link has changed is not relivant.
53081  *
53082  * Fork - LGPL
53083  * <script type="text/javascript">
53084  */
53085 /**
53086  * @class Roo.ContentPanel
53087  * @extends Roo.util.Observable
53088  * A basic ContentPanel element.
53089  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53090  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53091  * @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
53092  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53093  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53094  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53095  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53096  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53097  * @cfg {String} title          The title for this panel
53098  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53099  * @cfg {String} url            Calls {@link #setUrl} with this value
53100  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53101  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53102  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53103  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53104
53105  * @constructor
53106  * Create a new ContentPanel.
53107  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53108  * @param {String/Object} config A string to set only the title or a config object
53109  * @param {String} content (optional) Set the HTML content for this panel
53110  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53111  */
53112 Roo.ContentPanel = function(el, config, content){
53113     
53114      
53115     /*
53116     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53117         config = el;
53118         el = Roo.id();
53119     }
53120     if (config && config.parentLayout) { 
53121         el = config.parentLayout.el.createChild(); 
53122     }
53123     */
53124     if(el.autoCreate){ // xtype is available if this is called from factory
53125         config = el;
53126         el = Roo.id();
53127     }
53128     this.el = Roo.get(el);
53129     if(!this.el && config && config.autoCreate){
53130         if(typeof config.autoCreate == "object"){
53131             if(!config.autoCreate.id){
53132                 config.autoCreate.id = config.id||el;
53133             }
53134             this.el = Roo.DomHelper.append(document.body,
53135                         config.autoCreate, true);
53136         }else{
53137             this.el = Roo.DomHelper.append(document.body,
53138                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53139         }
53140     }
53141     this.closable = false;
53142     this.loaded = false;
53143     this.active = false;
53144     if(typeof config == "string"){
53145         this.title = config;
53146     }else{
53147         Roo.apply(this, config);
53148     }
53149     
53150     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53151         this.wrapEl = this.el.wrap();
53152         this.toolbar.container = this.el.insertSibling(false, 'before');
53153         this.toolbar = new Roo.Toolbar(this.toolbar);
53154     }
53155     
53156     // xtype created footer. - not sure if will work as we normally have to render first..
53157     if (this.footer && !this.footer.el && this.footer.xtype) {
53158         if (!this.wrapEl) {
53159             this.wrapEl = this.el.wrap();
53160         }
53161     
53162         this.footer.container = this.wrapEl.createChild();
53163          
53164         this.footer = Roo.factory(this.footer, Roo);
53165         
53166     }
53167     
53168     if(this.resizeEl){
53169         this.resizeEl = Roo.get(this.resizeEl, true);
53170     }else{
53171         this.resizeEl = this.el;
53172     }
53173     // handle view.xtype
53174     
53175  
53176     
53177     
53178     this.addEvents({
53179         /**
53180          * @event activate
53181          * Fires when this panel is activated. 
53182          * @param {Roo.ContentPanel} this
53183          */
53184         "activate" : true,
53185         /**
53186          * @event deactivate
53187          * Fires when this panel is activated. 
53188          * @param {Roo.ContentPanel} this
53189          */
53190         "deactivate" : true,
53191
53192         /**
53193          * @event resize
53194          * Fires when this panel is resized if fitToFrame is true.
53195          * @param {Roo.ContentPanel} this
53196          * @param {Number} width The width after any component adjustments
53197          * @param {Number} height The height after any component adjustments
53198          */
53199         "resize" : true,
53200         
53201          /**
53202          * @event render
53203          * Fires when this tab is created
53204          * @param {Roo.ContentPanel} this
53205          */
53206         "render" : true
53207         
53208         
53209         
53210     });
53211     
53212
53213     
53214     
53215     if(this.autoScroll){
53216         this.resizeEl.setStyle("overflow", "auto");
53217     } else {
53218         // fix randome scrolling
53219         this.el.on('scroll', function() {
53220             Roo.log('fix random scolling');
53221             this.scrollTo('top',0); 
53222         });
53223     }
53224     content = content || this.content;
53225     if(content){
53226         this.setContent(content);
53227     }
53228     if(config && config.url){
53229         this.setUrl(this.url, this.params, this.loadOnce);
53230     }
53231     
53232     
53233     
53234     Roo.ContentPanel.superclass.constructor.call(this);
53235     
53236     if (this.view && typeof(this.view.xtype) != 'undefined') {
53237         this.view.el = this.el.appendChild(document.createElement("div"));
53238         this.view = Roo.factory(this.view); 
53239         this.view.render  &&  this.view.render(false, '');  
53240     }
53241     
53242     
53243     this.fireEvent('render', this);
53244 };
53245
53246 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53247     tabTip:'',
53248     setRegion : function(region){
53249         this.region = region;
53250         if(region){
53251            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53252         }else{
53253            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53254         } 
53255     },
53256     
53257     /**
53258      * Returns the toolbar for this Panel if one was configured. 
53259      * @return {Roo.Toolbar} 
53260      */
53261     getToolbar : function(){
53262         return this.toolbar;
53263     },
53264     
53265     setActiveState : function(active){
53266         this.active = active;
53267         if(!active){
53268             this.fireEvent("deactivate", this);
53269         }else{
53270             this.fireEvent("activate", this);
53271         }
53272     },
53273     /**
53274      * Updates this panel's element
53275      * @param {String} content The new content
53276      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53277     */
53278     setContent : function(content, loadScripts){
53279         this.el.update(content, loadScripts);
53280     },
53281
53282     ignoreResize : function(w, h){
53283         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53284             return true;
53285         }else{
53286             this.lastSize = {width: w, height: h};
53287             return false;
53288         }
53289     },
53290     /**
53291      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53292      * @return {Roo.UpdateManager} The UpdateManager
53293      */
53294     getUpdateManager : function(){
53295         return this.el.getUpdateManager();
53296     },
53297      /**
53298      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53299      * @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:
53300 <pre><code>
53301 panel.load({
53302     url: "your-url.php",
53303     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53304     callback: yourFunction,
53305     scope: yourObject, //(optional scope)
53306     discardUrl: false,
53307     nocache: false,
53308     text: "Loading...",
53309     timeout: 30,
53310     scripts: false
53311 });
53312 </code></pre>
53313      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53314      * 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.
53315      * @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}
53316      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53317      * @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.
53318      * @return {Roo.ContentPanel} this
53319      */
53320     load : function(){
53321         var um = this.el.getUpdateManager();
53322         um.update.apply(um, arguments);
53323         return this;
53324     },
53325
53326
53327     /**
53328      * 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.
53329      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53330      * @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)
53331      * @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)
53332      * @return {Roo.UpdateManager} The UpdateManager
53333      */
53334     setUrl : function(url, params, loadOnce){
53335         if(this.refreshDelegate){
53336             this.removeListener("activate", this.refreshDelegate);
53337         }
53338         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53339         this.on("activate", this.refreshDelegate);
53340         return this.el.getUpdateManager();
53341     },
53342     
53343     _handleRefresh : function(url, params, loadOnce){
53344         if(!loadOnce || !this.loaded){
53345             var updater = this.el.getUpdateManager();
53346             updater.update(url, params, this._setLoaded.createDelegate(this));
53347         }
53348     },
53349     
53350     _setLoaded : function(){
53351         this.loaded = true;
53352     }, 
53353     
53354     /**
53355      * Returns this panel's id
53356      * @return {String} 
53357      */
53358     getId : function(){
53359         return this.el.id;
53360     },
53361     
53362     /** 
53363      * Returns this panel's element - used by regiosn to add.
53364      * @return {Roo.Element} 
53365      */
53366     getEl : function(){
53367         return this.wrapEl || this.el;
53368     },
53369     
53370     adjustForComponents : function(width, height)
53371     {
53372         //Roo.log('adjustForComponents ');
53373         if(this.resizeEl != this.el){
53374             width -= this.el.getFrameWidth('lr');
53375             height -= this.el.getFrameWidth('tb');
53376         }
53377         if(this.toolbar){
53378             var te = this.toolbar.getEl();
53379             height -= te.getHeight();
53380             te.setWidth(width);
53381         }
53382         if(this.footer){
53383             var te = this.footer.getEl();
53384             Roo.log("footer:" + te.getHeight());
53385             
53386             height -= te.getHeight();
53387             te.setWidth(width);
53388         }
53389         
53390         
53391         if(this.adjustments){
53392             width += this.adjustments[0];
53393             height += this.adjustments[1];
53394         }
53395         return {"width": width, "height": height};
53396     },
53397     
53398     setSize : function(width, height){
53399         if(this.fitToFrame && !this.ignoreResize(width, height)){
53400             if(this.fitContainer && this.resizeEl != this.el){
53401                 this.el.setSize(width, height);
53402             }
53403             var size = this.adjustForComponents(width, height);
53404             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53405             this.fireEvent('resize', this, size.width, size.height);
53406         }
53407     },
53408     
53409     /**
53410      * Returns this panel's title
53411      * @return {String} 
53412      */
53413     getTitle : function(){
53414         return this.title;
53415     },
53416     
53417     /**
53418      * Set this panel's title
53419      * @param {String} title
53420      */
53421     setTitle : function(title){
53422         this.title = title;
53423         if(this.region){
53424             this.region.updatePanelTitle(this, title);
53425         }
53426     },
53427     
53428     /**
53429      * Returns true is this panel was configured to be closable
53430      * @return {Boolean} 
53431      */
53432     isClosable : function(){
53433         return this.closable;
53434     },
53435     
53436     beforeSlide : function(){
53437         this.el.clip();
53438         this.resizeEl.clip();
53439     },
53440     
53441     afterSlide : function(){
53442         this.el.unclip();
53443         this.resizeEl.unclip();
53444     },
53445     
53446     /**
53447      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53448      *   Will fail silently if the {@link #setUrl} method has not been called.
53449      *   This does not activate the panel, just updates its content.
53450      */
53451     refresh : function(){
53452         if(this.refreshDelegate){
53453            this.loaded = false;
53454            this.refreshDelegate();
53455         }
53456     },
53457     
53458     /**
53459      * Destroys this panel
53460      */
53461     destroy : function(){
53462         this.el.removeAllListeners();
53463         var tempEl = document.createElement("span");
53464         tempEl.appendChild(this.el.dom);
53465         tempEl.innerHTML = "";
53466         this.el.remove();
53467         this.el = null;
53468     },
53469     
53470     /**
53471      * form - if the content panel contains a form - this is a reference to it.
53472      * @type {Roo.form.Form}
53473      */
53474     form : false,
53475     /**
53476      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53477      *    This contains a reference to it.
53478      * @type {Roo.View}
53479      */
53480     view : false,
53481     
53482       /**
53483      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53484      * <pre><code>
53485
53486 layout.addxtype({
53487        xtype : 'Form',
53488        items: [ .... ]
53489    }
53490 );
53491
53492 </code></pre>
53493      * @param {Object} cfg Xtype definition of item to add.
53494      */
53495     
53496     addxtype : function(cfg) {
53497         // add form..
53498         if (cfg.xtype.match(/^Form$/)) {
53499             
53500             var el;
53501             //if (this.footer) {
53502             //    el = this.footer.container.insertSibling(false, 'before');
53503             //} else {
53504                 el = this.el.createChild();
53505             //}
53506
53507             this.form = new  Roo.form.Form(cfg);
53508             
53509             
53510             if ( this.form.allItems.length) {
53511                 this.form.render(el.dom);
53512             }
53513             return this.form;
53514         }
53515         // should only have one of theses..
53516         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53517             // views.. should not be just added - used named prop 'view''
53518             
53519             cfg.el = this.el.appendChild(document.createElement("div"));
53520             // factory?
53521             
53522             var ret = new Roo.factory(cfg);
53523              
53524              ret.render && ret.render(false, ''); // render blank..
53525             this.view = ret;
53526             return ret;
53527         }
53528         return false;
53529     }
53530 });
53531
53532 /**
53533  * @class Roo.GridPanel
53534  * @extends Roo.ContentPanel
53535  * @constructor
53536  * Create a new GridPanel.
53537  * @param {Roo.grid.Grid} grid The grid for this panel
53538  * @param {String/Object} config A string to set only the panel's title, or a config object
53539  */
53540 Roo.GridPanel = function(grid, config){
53541     
53542   
53543     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53544         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53545         
53546     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53547     
53548     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53549     
53550     if(this.toolbar){
53551         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53552     }
53553     // xtype created footer. - not sure if will work as we normally have to render first..
53554     if (this.footer && !this.footer.el && this.footer.xtype) {
53555         
53556         this.footer.container = this.grid.getView().getFooterPanel(true);
53557         this.footer.dataSource = this.grid.dataSource;
53558         this.footer = Roo.factory(this.footer, Roo);
53559         
53560     }
53561     
53562     grid.monitorWindowResize = false; // turn off autosizing
53563     grid.autoHeight = false;
53564     grid.autoWidth = false;
53565     this.grid = grid;
53566     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53567 };
53568
53569 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53570     getId : function(){
53571         return this.grid.id;
53572     },
53573     
53574     /**
53575      * Returns the grid for this panel
53576      * @return {Roo.grid.Grid} 
53577      */
53578     getGrid : function(){
53579         return this.grid;    
53580     },
53581     
53582     setSize : function(width, height){
53583         if(!this.ignoreResize(width, height)){
53584             var grid = this.grid;
53585             var size = this.adjustForComponents(width, height);
53586             grid.getGridEl().setSize(size.width, size.height);
53587             grid.autoSize();
53588         }
53589     },
53590     
53591     beforeSlide : function(){
53592         this.grid.getView().scroller.clip();
53593     },
53594     
53595     afterSlide : function(){
53596         this.grid.getView().scroller.unclip();
53597     },
53598     
53599     destroy : function(){
53600         this.grid.destroy();
53601         delete this.grid;
53602         Roo.GridPanel.superclass.destroy.call(this); 
53603     }
53604 });
53605
53606
53607 /**
53608  * @class Roo.NestedLayoutPanel
53609  * @extends Roo.ContentPanel
53610  * @constructor
53611  * Create a new NestedLayoutPanel.
53612  * 
53613  * 
53614  * @param {Roo.BorderLayout} layout The layout for this panel
53615  * @param {String/Object} config A string to set only the title or a config object
53616  */
53617 Roo.NestedLayoutPanel = function(layout, config)
53618 {
53619     // construct with only one argument..
53620     /* FIXME - implement nicer consturctors
53621     if (layout.layout) {
53622         config = layout;
53623         layout = config.layout;
53624         delete config.layout;
53625     }
53626     if (layout.xtype && !layout.getEl) {
53627         // then layout needs constructing..
53628         layout = Roo.factory(layout, Roo);
53629     }
53630     */
53631     
53632     
53633     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53634     
53635     layout.monitorWindowResize = false; // turn off autosizing
53636     this.layout = layout;
53637     this.layout.getEl().addClass("x-layout-nested-layout");
53638     
53639     
53640     
53641     
53642 };
53643
53644 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53645
53646     setSize : function(width, height){
53647         if(!this.ignoreResize(width, height)){
53648             var size = this.adjustForComponents(width, height);
53649             var el = this.layout.getEl();
53650             el.setSize(size.width, size.height);
53651             var touch = el.dom.offsetWidth;
53652             this.layout.layout();
53653             // ie requires a double layout on the first pass
53654             if(Roo.isIE && !this.initialized){
53655                 this.initialized = true;
53656                 this.layout.layout();
53657             }
53658         }
53659     },
53660     
53661     // activate all subpanels if not currently active..
53662     
53663     setActiveState : function(active){
53664         this.active = active;
53665         if(!active){
53666             this.fireEvent("deactivate", this);
53667             return;
53668         }
53669         
53670         this.fireEvent("activate", this);
53671         // not sure if this should happen before or after..
53672         if (!this.layout) {
53673             return; // should not happen..
53674         }
53675         var reg = false;
53676         for (var r in this.layout.regions) {
53677             reg = this.layout.getRegion(r);
53678             if (reg.getActivePanel()) {
53679                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53680                 reg.setActivePanel(reg.getActivePanel());
53681                 continue;
53682             }
53683             if (!reg.panels.length) {
53684                 continue;
53685             }
53686             reg.showPanel(reg.getPanel(0));
53687         }
53688         
53689         
53690         
53691         
53692     },
53693     
53694     /**
53695      * Returns the nested BorderLayout for this panel
53696      * @return {Roo.BorderLayout} 
53697      */
53698     getLayout : function(){
53699         return this.layout;
53700     },
53701     
53702      /**
53703      * Adds a xtype elements to the layout of the nested panel
53704      * <pre><code>
53705
53706 panel.addxtype({
53707        xtype : 'ContentPanel',
53708        region: 'west',
53709        items: [ .... ]
53710    }
53711 );
53712
53713 panel.addxtype({
53714         xtype : 'NestedLayoutPanel',
53715         region: 'west',
53716         layout: {
53717            center: { },
53718            west: { }   
53719         },
53720         items : [ ... list of content panels or nested layout panels.. ]
53721    }
53722 );
53723 </code></pre>
53724      * @param {Object} cfg Xtype definition of item to add.
53725      */
53726     addxtype : function(cfg) {
53727         return this.layout.addxtype(cfg);
53728     
53729     }
53730 });
53731
53732 Roo.ScrollPanel = function(el, config, content){
53733     config = config || {};
53734     config.fitToFrame = true;
53735     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53736     
53737     this.el.dom.style.overflow = "hidden";
53738     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53739     this.el.removeClass("x-layout-inactive-content");
53740     this.el.on("mousewheel", this.onWheel, this);
53741
53742     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53743     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53744     up.unselectable(); down.unselectable();
53745     up.on("click", this.scrollUp, this);
53746     down.on("click", this.scrollDown, this);
53747     up.addClassOnOver("x-scroller-btn-over");
53748     down.addClassOnOver("x-scroller-btn-over");
53749     up.addClassOnClick("x-scroller-btn-click");
53750     down.addClassOnClick("x-scroller-btn-click");
53751     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53752
53753     this.resizeEl = this.el;
53754     this.el = wrap; this.up = up; this.down = down;
53755 };
53756
53757 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53758     increment : 100,
53759     wheelIncrement : 5,
53760     scrollUp : function(){
53761         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53762     },
53763
53764     scrollDown : function(){
53765         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53766     },
53767
53768     afterScroll : function(){
53769         var el = this.resizeEl;
53770         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53771         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53772         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53773     },
53774
53775     setSize : function(){
53776         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53777         this.afterScroll();
53778     },
53779
53780     onWheel : function(e){
53781         var d = e.getWheelDelta();
53782         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53783         this.afterScroll();
53784         e.stopEvent();
53785     },
53786
53787     setContent : function(content, loadScripts){
53788         this.resizeEl.update(content, loadScripts);
53789     }
53790
53791 });
53792
53793
53794
53795
53796
53797
53798
53799
53800
53801 /**
53802  * @class Roo.TreePanel
53803  * @extends Roo.ContentPanel
53804  * @constructor
53805  * Create a new TreePanel. - defaults to fit/scoll contents.
53806  * @param {String/Object} config A string to set only the panel's title, or a config object
53807  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53808  */
53809 Roo.TreePanel = function(config){
53810     var el = config.el;
53811     var tree = config.tree;
53812     delete config.tree; 
53813     delete config.el; // hopefull!
53814     
53815     // wrapper for IE7 strict & safari scroll issue
53816     
53817     var treeEl = el.createChild();
53818     config.resizeEl = treeEl;
53819     
53820     
53821     
53822     Roo.TreePanel.superclass.constructor.call(this, el, config);
53823  
53824  
53825     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53826     //console.log(tree);
53827     this.on('activate', function()
53828     {
53829         if (this.tree.rendered) {
53830             return;
53831         }
53832         //console.log('render tree');
53833         this.tree.render();
53834     });
53835     // this should not be needed.. - it's actually the 'el' that resizes?
53836     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53837     
53838     //this.on('resize',  function (cp, w, h) {
53839     //        this.tree.innerCt.setWidth(w);
53840     //        this.tree.innerCt.setHeight(h);
53841     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53842     //});
53843
53844         
53845     
53846 };
53847
53848 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53849     fitToFrame : true,
53850     autoScroll : true
53851 });
53852
53853
53854
53855
53856
53857
53858
53859
53860
53861
53862
53863 /*
53864  * Based on:
53865  * Ext JS Library 1.1.1
53866  * Copyright(c) 2006-2007, Ext JS, LLC.
53867  *
53868  * Originally Released Under LGPL - original licence link has changed is not relivant.
53869  *
53870  * Fork - LGPL
53871  * <script type="text/javascript">
53872  */
53873  
53874
53875 /**
53876  * @class Roo.ReaderLayout
53877  * @extends Roo.BorderLayout
53878  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53879  * center region containing two nested regions (a top one for a list view and one for item preview below),
53880  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53881  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53882  * expedites the setup of the overall layout and regions for this common application style.
53883  * Example:
53884  <pre><code>
53885 var reader = new Roo.ReaderLayout();
53886 var CP = Roo.ContentPanel;  // shortcut for adding
53887
53888 reader.beginUpdate();
53889 reader.add("north", new CP("north", "North"));
53890 reader.add("west", new CP("west", {title: "West"}));
53891 reader.add("east", new CP("east", {title: "East"}));
53892
53893 reader.regions.listView.add(new CP("listView", "List"));
53894 reader.regions.preview.add(new CP("preview", "Preview"));
53895 reader.endUpdate();
53896 </code></pre>
53897 * @constructor
53898 * Create a new ReaderLayout
53899 * @param {Object} config Configuration options
53900 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53901 * document.body if omitted)
53902 */
53903 Roo.ReaderLayout = function(config, renderTo){
53904     var c = config || {size:{}};
53905     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53906         north: c.north !== false ? Roo.apply({
53907             split:false,
53908             initialSize: 32,
53909             titlebar: false
53910         }, c.north) : false,
53911         west: c.west !== false ? Roo.apply({
53912             split:true,
53913             initialSize: 200,
53914             minSize: 175,
53915             maxSize: 400,
53916             titlebar: true,
53917             collapsible: true,
53918             animate: true,
53919             margins:{left:5,right:0,bottom:5,top:5},
53920             cmargins:{left:5,right:5,bottom:5,top:5}
53921         }, c.west) : false,
53922         east: c.east !== false ? Roo.apply({
53923             split:true,
53924             initialSize: 200,
53925             minSize: 175,
53926             maxSize: 400,
53927             titlebar: true,
53928             collapsible: true,
53929             animate: true,
53930             margins:{left:0,right:5,bottom:5,top:5},
53931             cmargins:{left:5,right:5,bottom:5,top:5}
53932         }, c.east) : false,
53933         center: Roo.apply({
53934             tabPosition: 'top',
53935             autoScroll:false,
53936             closeOnTab: true,
53937             titlebar:false,
53938             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53939         }, c.center)
53940     });
53941
53942     this.el.addClass('x-reader');
53943
53944     this.beginUpdate();
53945
53946     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53947         south: c.preview !== false ? Roo.apply({
53948             split:true,
53949             initialSize: 200,
53950             minSize: 100,
53951             autoScroll:true,
53952             collapsible:true,
53953             titlebar: true,
53954             cmargins:{top:5,left:0, right:0, bottom:0}
53955         }, c.preview) : false,
53956         center: Roo.apply({
53957             autoScroll:false,
53958             titlebar:false,
53959             minHeight:200
53960         }, c.listView)
53961     });
53962     this.add('center', new Roo.NestedLayoutPanel(inner,
53963             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53964
53965     this.endUpdate();
53966
53967     this.regions.preview = inner.getRegion('south');
53968     this.regions.listView = inner.getRegion('center');
53969 };
53970
53971 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53972  * Based on:
53973  * Ext JS Library 1.1.1
53974  * Copyright(c) 2006-2007, Ext JS, LLC.
53975  *
53976  * Originally Released Under LGPL - original licence link has changed is not relivant.
53977  *
53978  * Fork - LGPL
53979  * <script type="text/javascript">
53980  */
53981  
53982 /**
53983  * @class Roo.grid.Grid
53984  * @extends Roo.util.Observable
53985  * This class represents the primary interface of a component based grid control.
53986  * <br><br>Usage:<pre><code>
53987  var grid = new Roo.grid.Grid("my-container-id", {
53988      ds: myDataStore,
53989      cm: myColModel,
53990      selModel: mySelectionModel,
53991      autoSizeColumns: true,
53992      monitorWindowResize: false,
53993      trackMouseOver: true
53994  });
53995  // set any options
53996  grid.render();
53997  * </code></pre>
53998  * <b>Common Problems:</b><br/>
53999  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54000  * element will correct this<br/>
54001  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54002  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54003  * are unpredictable.<br/>
54004  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54005  * grid to calculate dimensions/offsets.<br/>
54006   * @constructor
54007  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54008  * The container MUST have some type of size defined for the grid to fill. The container will be
54009  * automatically set to position relative if it isn't already.
54010  * @param {Object} config A config object that sets properties on this grid.
54011  */
54012 Roo.grid.Grid = function(container, config){
54013         // initialize the container
54014         this.container = Roo.get(container);
54015         this.container.update("");
54016         this.container.setStyle("overflow", "hidden");
54017     this.container.addClass('x-grid-container');
54018
54019     this.id = this.container.id;
54020
54021     Roo.apply(this, config);
54022     // check and correct shorthanded configs
54023     if(this.ds){
54024         this.dataSource = this.ds;
54025         delete this.ds;
54026     }
54027     if(this.cm){
54028         this.colModel = this.cm;
54029         delete this.cm;
54030     }
54031     if(this.sm){
54032         this.selModel = this.sm;
54033         delete this.sm;
54034     }
54035
54036     if (this.selModel) {
54037         this.selModel = Roo.factory(this.selModel, Roo.grid);
54038         this.sm = this.selModel;
54039         this.sm.xmodule = this.xmodule || false;
54040     }
54041     if (typeof(this.colModel.config) == 'undefined') {
54042         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54043         this.cm = this.colModel;
54044         this.cm.xmodule = this.xmodule || false;
54045     }
54046     if (this.dataSource) {
54047         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54048         this.ds = this.dataSource;
54049         this.ds.xmodule = this.xmodule || false;
54050          
54051     }
54052     
54053     
54054     
54055     if(this.width){
54056         this.container.setWidth(this.width);
54057     }
54058
54059     if(this.height){
54060         this.container.setHeight(this.height);
54061     }
54062     /** @private */
54063         this.addEvents({
54064         // raw events
54065         /**
54066          * @event click
54067          * The raw click event for the entire grid.
54068          * @param {Roo.EventObject} e
54069          */
54070         "click" : true,
54071         /**
54072          * @event dblclick
54073          * The raw dblclick event for the entire grid.
54074          * @param {Roo.EventObject} e
54075          */
54076         "dblclick" : true,
54077         /**
54078          * @event contextmenu
54079          * The raw contextmenu event for the entire grid.
54080          * @param {Roo.EventObject} e
54081          */
54082         "contextmenu" : true,
54083         /**
54084          * @event mousedown
54085          * The raw mousedown event for the entire grid.
54086          * @param {Roo.EventObject} e
54087          */
54088         "mousedown" : true,
54089         /**
54090          * @event mouseup
54091          * The raw mouseup event for the entire grid.
54092          * @param {Roo.EventObject} e
54093          */
54094         "mouseup" : true,
54095         /**
54096          * @event mouseover
54097          * The raw mouseover event for the entire grid.
54098          * @param {Roo.EventObject} e
54099          */
54100         "mouseover" : true,
54101         /**
54102          * @event mouseout
54103          * The raw mouseout event for the entire grid.
54104          * @param {Roo.EventObject} e
54105          */
54106         "mouseout" : true,
54107         /**
54108          * @event keypress
54109          * The raw keypress event for the entire grid.
54110          * @param {Roo.EventObject} e
54111          */
54112         "keypress" : true,
54113         /**
54114          * @event keydown
54115          * The raw keydown event for the entire grid.
54116          * @param {Roo.EventObject} e
54117          */
54118         "keydown" : true,
54119
54120         // custom events
54121
54122         /**
54123          * @event cellclick
54124          * Fires when a cell is clicked
54125          * @param {Grid} this
54126          * @param {Number} rowIndex
54127          * @param {Number} columnIndex
54128          * @param {Roo.EventObject} e
54129          */
54130         "cellclick" : true,
54131         /**
54132          * @event celldblclick
54133          * Fires when a cell is double clicked
54134          * @param {Grid} this
54135          * @param {Number} rowIndex
54136          * @param {Number} columnIndex
54137          * @param {Roo.EventObject} e
54138          */
54139         "celldblclick" : true,
54140         /**
54141          * @event rowclick
54142          * Fires when a row is clicked
54143          * @param {Grid} this
54144          * @param {Number} rowIndex
54145          * @param {Roo.EventObject} e
54146          */
54147         "rowclick" : true,
54148         /**
54149          * @event rowdblclick
54150          * Fires when a row is double clicked
54151          * @param {Grid} this
54152          * @param {Number} rowIndex
54153          * @param {Roo.EventObject} e
54154          */
54155         "rowdblclick" : true,
54156         /**
54157          * @event headerclick
54158          * Fires when a header is clicked
54159          * @param {Grid} this
54160          * @param {Number} columnIndex
54161          * @param {Roo.EventObject} e
54162          */
54163         "headerclick" : true,
54164         /**
54165          * @event headerdblclick
54166          * Fires when a header cell is double clicked
54167          * @param {Grid} this
54168          * @param {Number} columnIndex
54169          * @param {Roo.EventObject} e
54170          */
54171         "headerdblclick" : true,
54172         /**
54173          * @event rowcontextmenu
54174          * Fires when a row is right clicked
54175          * @param {Grid} this
54176          * @param {Number} rowIndex
54177          * @param {Roo.EventObject} e
54178          */
54179         "rowcontextmenu" : true,
54180         /**
54181          * @event cellcontextmenu
54182          * Fires when a cell is right clicked
54183          * @param {Grid} this
54184          * @param {Number} rowIndex
54185          * @param {Number} cellIndex
54186          * @param {Roo.EventObject} e
54187          */
54188          "cellcontextmenu" : true,
54189         /**
54190          * @event headercontextmenu
54191          * Fires when a header is right clicked
54192          * @param {Grid} this
54193          * @param {Number} columnIndex
54194          * @param {Roo.EventObject} e
54195          */
54196         "headercontextmenu" : true,
54197         /**
54198          * @event bodyscroll
54199          * Fires when the body element is scrolled
54200          * @param {Number} scrollLeft
54201          * @param {Number} scrollTop
54202          */
54203         "bodyscroll" : true,
54204         /**
54205          * @event columnresize
54206          * Fires when the user resizes a column
54207          * @param {Number} columnIndex
54208          * @param {Number} newSize
54209          */
54210         "columnresize" : true,
54211         /**
54212          * @event columnmove
54213          * Fires when the user moves a column
54214          * @param {Number} oldIndex
54215          * @param {Number} newIndex
54216          */
54217         "columnmove" : true,
54218         /**
54219          * @event startdrag
54220          * Fires when row(s) start being dragged
54221          * @param {Grid} this
54222          * @param {Roo.GridDD} dd The drag drop object
54223          * @param {event} e The raw browser event
54224          */
54225         "startdrag" : true,
54226         /**
54227          * @event enddrag
54228          * Fires when a drag operation is complete
54229          * @param {Grid} this
54230          * @param {Roo.GridDD} dd The drag drop object
54231          * @param {event} e The raw browser event
54232          */
54233         "enddrag" : true,
54234         /**
54235          * @event dragdrop
54236          * Fires when dragged row(s) are dropped on a valid DD target
54237          * @param {Grid} this
54238          * @param {Roo.GridDD} dd The drag drop object
54239          * @param {String} targetId The target drag drop object
54240          * @param {event} e The raw browser event
54241          */
54242         "dragdrop" : true,
54243         /**
54244          * @event dragover
54245          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54246          * @param {Grid} this
54247          * @param {Roo.GridDD} dd The drag drop object
54248          * @param {String} targetId The target drag drop object
54249          * @param {event} e The raw browser event
54250          */
54251         "dragover" : true,
54252         /**
54253          * @event dragenter
54254          *  Fires when the dragged row(s) first cross another DD target while being dragged
54255          * @param {Grid} this
54256          * @param {Roo.GridDD} dd The drag drop object
54257          * @param {String} targetId The target drag drop object
54258          * @param {event} e The raw browser event
54259          */
54260         "dragenter" : true,
54261         /**
54262          * @event dragout
54263          * Fires when the dragged row(s) leave another DD target while being dragged
54264          * @param {Grid} this
54265          * @param {Roo.GridDD} dd The drag drop object
54266          * @param {String} targetId The target drag drop object
54267          * @param {event} e The raw browser event
54268          */
54269         "dragout" : true,
54270         /**
54271          * @event rowclass
54272          * Fires when a row is rendered, so you can change add a style to it.
54273          * @param {GridView} gridview   The grid view
54274          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54275          */
54276         'rowclass' : true,
54277
54278         /**
54279          * @event render
54280          * Fires when the grid is rendered
54281          * @param {Grid} grid
54282          */
54283         'render' : true
54284     });
54285
54286     Roo.grid.Grid.superclass.constructor.call(this);
54287 };
54288 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54289     
54290     /**
54291      * @cfg {String} ddGroup - drag drop group.
54292      */
54293
54294     /**
54295      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54296      */
54297     minColumnWidth : 25,
54298
54299     /**
54300      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54301      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54302      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54303      */
54304     autoSizeColumns : false,
54305
54306     /**
54307      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54308      */
54309     autoSizeHeaders : true,
54310
54311     /**
54312      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54313      */
54314     monitorWindowResize : true,
54315
54316     /**
54317      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54318      * rows measured to get a columns size. Default is 0 (all rows).
54319      */
54320     maxRowsToMeasure : 0,
54321
54322     /**
54323      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54324      */
54325     trackMouseOver : true,
54326
54327     /**
54328     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54329     */
54330     
54331     /**
54332     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54333     */
54334     enableDragDrop : false,
54335     
54336     /**
54337     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54338     */
54339     enableColumnMove : true,
54340     
54341     /**
54342     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54343     */
54344     enableColumnHide : true,
54345     
54346     /**
54347     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54348     */
54349     enableRowHeightSync : false,
54350     
54351     /**
54352     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54353     */
54354     stripeRows : true,
54355     
54356     /**
54357     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54358     */
54359     autoHeight : false,
54360
54361     /**
54362      * @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.
54363      */
54364     autoExpandColumn : false,
54365
54366     /**
54367     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54368     * Default is 50.
54369     */
54370     autoExpandMin : 50,
54371
54372     /**
54373     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54374     */
54375     autoExpandMax : 1000,
54376
54377     /**
54378     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54379     */
54380     view : null,
54381
54382     /**
54383     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54384     */
54385     loadMask : false,
54386     /**
54387     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54388     */
54389     dropTarget: false,
54390     
54391    
54392     
54393     // private
54394     rendered : false,
54395
54396     /**
54397     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54398     * of a fixed width. Default is false.
54399     */
54400     /**
54401     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54402     */
54403     /**
54404      * Called once after all setup has been completed and the grid is ready to be rendered.
54405      * @return {Roo.grid.Grid} this
54406      */
54407     render : function()
54408     {
54409         var c = this.container;
54410         // try to detect autoHeight/width mode
54411         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54412             this.autoHeight = true;
54413         }
54414         var view = this.getView();
54415         view.init(this);
54416
54417         c.on("click", this.onClick, this);
54418         c.on("dblclick", this.onDblClick, this);
54419         c.on("contextmenu", this.onContextMenu, this);
54420         c.on("keydown", this.onKeyDown, this);
54421         if (Roo.isTouch) {
54422             c.on("touchstart", this.onTouchStart, this);
54423         }
54424
54425         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54426
54427         this.getSelectionModel().init(this);
54428
54429         view.render();
54430
54431         if(this.loadMask){
54432             this.loadMask = new Roo.LoadMask(this.container,
54433                     Roo.apply({store:this.dataSource}, this.loadMask));
54434         }
54435         
54436         
54437         if (this.toolbar && this.toolbar.xtype) {
54438             this.toolbar.container = this.getView().getHeaderPanel(true);
54439             this.toolbar = new Roo.Toolbar(this.toolbar);
54440         }
54441         if (this.footer && this.footer.xtype) {
54442             this.footer.dataSource = this.getDataSource();
54443             this.footer.container = this.getView().getFooterPanel(true);
54444             this.footer = Roo.factory(this.footer, Roo);
54445         }
54446         if (this.dropTarget && this.dropTarget.xtype) {
54447             delete this.dropTarget.xtype;
54448             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54449         }
54450         
54451         
54452         this.rendered = true;
54453         this.fireEvent('render', this);
54454         return this;
54455     },
54456
54457         /**
54458          * Reconfigures the grid to use a different Store and Column Model.
54459          * The View will be bound to the new objects and refreshed.
54460          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54461          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54462          */
54463     reconfigure : function(dataSource, colModel){
54464         if(this.loadMask){
54465             this.loadMask.destroy();
54466             this.loadMask = new Roo.LoadMask(this.container,
54467                     Roo.apply({store:dataSource}, this.loadMask));
54468         }
54469         this.view.bind(dataSource, colModel);
54470         this.dataSource = dataSource;
54471         this.colModel = colModel;
54472         this.view.refresh(true);
54473     },
54474
54475     // private
54476     onKeyDown : function(e){
54477         this.fireEvent("keydown", e);
54478     },
54479
54480     /**
54481      * Destroy this grid.
54482      * @param {Boolean} removeEl True to remove the element
54483      */
54484     destroy : function(removeEl, keepListeners){
54485         if(this.loadMask){
54486             this.loadMask.destroy();
54487         }
54488         var c = this.container;
54489         c.removeAllListeners();
54490         this.view.destroy();
54491         this.colModel.purgeListeners();
54492         if(!keepListeners){
54493             this.purgeListeners();
54494         }
54495         c.update("");
54496         if(removeEl === true){
54497             c.remove();
54498         }
54499     },
54500
54501     // private
54502     processEvent : function(name, e){
54503         // does this fire select???
54504         //Roo.log('grid:processEvent '  + name);
54505         
54506         if (name != 'touchstart' ) {
54507             this.fireEvent(name, e);    
54508         }
54509         
54510         var t = e.getTarget();
54511         var v = this.view;
54512         var header = v.findHeaderIndex(t);
54513         if(header !== false){
54514             var ename = name == 'touchstart' ? 'click' : name;
54515              
54516             this.fireEvent("header" + ename, this, header, e);
54517         }else{
54518             var row = v.findRowIndex(t);
54519             var cell = v.findCellIndex(t);
54520             if (name == 'touchstart') {
54521                 // first touch is always a click.
54522                 // hopefull this happens after selection is updated.?
54523                 name = false;
54524                 
54525                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54526                     var cs = this.selModel.getSelectedCell();
54527                     if (row == cs[0] && cell == cs[1]){
54528                         name = 'dblclick';
54529                     }
54530                 }
54531                 if (typeof(this.selModel.getSelections) != 'undefined') {
54532                     var cs = this.selModel.getSelections();
54533                     var ds = this.dataSource;
54534                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54535                         name = 'dblclick';
54536                     }
54537                 }
54538                 if (!name) {
54539                     return;
54540                 }
54541             }
54542             
54543             
54544             if(row !== false){
54545                 this.fireEvent("row" + name, this, row, e);
54546                 if(cell !== false){
54547                     this.fireEvent("cell" + name, this, row, cell, e);
54548                 }
54549             }
54550         }
54551     },
54552
54553     // private
54554     onClick : function(e){
54555         this.processEvent("click", e);
54556     },
54557    // private
54558     onTouchStart : function(e){
54559         this.processEvent("touchstart", e);
54560     },
54561
54562     // private
54563     onContextMenu : function(e, t){
54564         this.processEvent("contextmenu", e);
54565     },
54566
54567     // private
54568     onDblClick : function(e){
54569         this.processEvent("dblclick", e);
54570     },
54571
54572     // private
54573     walkCells : function(row, col, step, fn, scope){
54574         var cm = this.colModel, clen = cm.getColumnCount();
54575         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54576         if(step < 0){
54577             if(col < 0){
54578                 row--;
54579                 first = false;
54580             }
54581             while(row >= 0){
54582                 if(!first){
54583                     col = clen-1;
54584                 }
54585                 first = false;
54586                 while(col >= 0){
54587                     if(fn.call(scope || this, row, col, cm) === true){
54588                         return [row, col];
54589                     }
54590                     col--;
54591                 }
54592                 row--;
54593             }
54594         } else {
54595             if(col >= clen){
54596                 row++;
54597                 first = false;
54598             }
54599             while(row < rlen){
54600                 if(!first){
54601                     col = 0;
54602                 }
54603                 first = false;
54604                 while(col < clen){
54605                     if(fn.call(scope || this, row, col, cm) === true){
54606                         return [row, col];
54607                     }
54608                     col++;
54609                 }
54610                 row++;
54611             }
54612         }
54613         return null;
54614     },
54615
54616     // private
54617     getSelections : function(){
54618         return this.selModel.getSelections();
54619     },
54620
54621     /**
54622      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54623      * but if manual update is required this method will initiate it.
54624      */
54625     autoSize : function(){
54626         if(this.rendered){
54627             this.view.layout();
54628             if(this.view.adjustForScroll){
54629                 this.view.adjustForScroll();
54630             }
54631         }
54632     },
54633
54634     /**
54635      * Returns the grid's underlying element.
54636      * @return {Element} The element
54637      */
54638     getGridEl : function(){
54639         return this.container;
54640     },
54641
54642     // private for compatibility, overridden by editor grid
54643     stopEditing : function(){},
54644
54645     /**
54646      * Returns the grid's SelectionModel.
54647      * @return {SelectionModel}
54648      */
54649     getSelectionModel : function(){
54650         if(!this.selModel){
54651             this.selModel = new Roo.grid.RowSelectionModel();
54652         }
54653         return this.selModel;
54654     },
54655
54656     /**
54657      * Returns the grid's DataSource.
54658      * @return {DataSource}
54659      */
54660     getDataSource : function(){
54661         return this.dataSource;
54662     },
54663
54664     /**
54665      * Returns the grid's ColumnModel.
54666      * @return {ColumnModel}
54667      */
54668     getColumnModel : function(){
54669         return this.colModel;
54670     },
54671
54672     /**
54673      * Returns the grid's GridView object.
54674      * @return {GridView}
54675      */
54676     getView : function(){
54677         if(!this.view){
54678             this.view = new Roo.grid.GridView(this.viewConfig);
54679         }
54680         return this.view;
54681     },
54682     /**
54683      * Called to get grid's drag proxy text, by default returns this.ddText.
54684      * @return {String}
54685      */
54686     getDragDropText : function(){
54687         var count = this.selModel.getCount();
54688         return String.format(this.ddText, count, count == 1 ? '' : 's');
54689     }
54690 });
54691 /**
54692  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54693  * %0 is replaced with the number of selected rows.
54694  * @type String
54695  */
54696 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54697  * Based on:
54698  * Ext JS Library 1.1.1
54699  * Copyright(c) 2006-2007, Ext JS, LLC.
54700  *
54701  * Originally Released Under LGPL - original licence link has changed is not relivant.
54702  *
54703  * Fork - LGPL
54704  * <script type="text/javascript">
54705  */
54706  
54707 Roo.grid.AbstractGridView = function(){
54708         this.grid = null;
54709         
54710         this.events = {
54711             "beforerowremoved" : true,
54712             "beforerowsinserted" : true,
54713             "beforerefresh" : true,
54714             "rowremoved" : true,
54715             "rowsinserted" : true,
54716             "rowupdated" : true,
54717             "refresh" : true
54718         };
54719     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54720 };
54721
54722 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54723     rowClass : "x-grid-row",
54724     cellClass : "x-grid-cell",
54725     tdClass : "x-grid-td",
54726     hdClass : "x-grid-hd",
54727     splitClass : "x-grid-hd-split",
54728     
54729     init: function(grid){
54730         this.grid = grid;
54731                 var cid = this.grid.getGridEl().id;
54732         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54733         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54734         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54735         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54736         },
54737         
54738     getColumnRenderers : function(){
54739         var renderers = [];
54740         var cm = this.grid.colModel;
54741         var colCount = cm.getColumnCount();
54742         for(var i = 0; i < colCount; i++){
54743             renderers[i] = cm.getRenderer(i);
54744         }
54745         return renderers;
54746     },
54747     
54748     getColumnIds : function(){
54749         var ids = [];
54750         var cm = this.grid.colModel;
54751         var colCount = cm.getColumnCount();
54752         for(var i = 0; i < colCount; i++){
54753             ids[i] = cm.getColumnId(i);
54754         }
54755         return ids;
54756     },
54757     
54758     getDataIndexes : function(){
54759         if(!this.indexMap){
54760             this.indexMap = this.buildIndexMap();
54761         }
54762         return this.indexMap.colToData;
54763     },
54764     
54765     getColumnIndexByDataIndex : function(dataIndex){
54766         if(!this.indexMap){
54767             this.indexMap = this.buildIndexMap();
54768         }
54769         return this.indexMap.dataToCol[dataIndex];
54770     },
54771     
54772     /**
54773      * Set a css style for a column dynamically. 
54774      * @param {Number} colIndex The index of the column
54775      * @param {String} name The css property name
54776      * @param {String} value The css value
54777      */
54778     setCSSStyle : function(colIndex, name, value){
54779         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54780         Roo.util.CSS.updateRule(selector, name, value);
54781     },
54782     
54783     generateRules : function(cm){
54784         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54785         Roo.util.CSS.removeStyleSheet(rulesId);
54786         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54787             var cid = cm.getColumnId(i);
54788             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54789                          this.tdSelector, cid, " {\n}\n",
54790                          this.hdSelector, cid, " {\n}\n",
54791                          this.splitSelector, cid, " {\n}\n");
54792         }
54793         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54794     }
54795 });/*
54796  * Based on:
54797  * Ext JS Library 1.1.1
54798  * Copyright(c) 2006-2007, Ext JS, LLC.
54799  *
54800  * Originally Released Under LGPL - original licence link has changed is not relivant.
54801  *
54802  * Fork - LGPL
54803  * <script type="text/javascript">
54804  */
54805
54806 // private
54807 // This is a support class used internally by the Grid components
54808 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54809     this.grid = grid;
54810     this.view = grid.getView();
54811     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54812     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54813     if(hd2){
54814         this.setHandleElId(Roo.id(hd));
54815         this.setOuterHandleElId(Roo.id(hd2));
54816     }
54817     this.scroll = false;
54818 };
54819 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54820     maxDragWidth: 120,
54821     getDragData : function(e){
54822         var t = Roo.lib.Event.getTarget(e);
54823         var h = this.view.findHeaderCell(t);
54824         if(h){
54825             return {ddel: h.firstChild, header:h};
54826         }
54827         return false;
54828     },
54829
54830     onInitDrag : function(e){
54831         this.view.headersDisabled = true;
54832         var clone = this.dragData.ddel.cloneNode(true);
54833         clone.id = Roo.id();
54834         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54835         this.proxy.update(clone);
54836         return true;
54837     },
54838
54839     afterValidDrop : function(){
54840         var v = this.view;
54841         setTimeout(function(){
54842             v.headersDisabled = false;
54843         }, 50);
54844     },
54845
54846     afterInvalidDrop : function(){
54847         var v = this.view;
54848         setTimeout(function(){
54849             v.headersDisabled = false;
54850         }, 50);
54851     }
54852 });
54853 /*
54854  * Based on:
54855  * Ext JS Library 1.1.1
54856  * Copyright(c) 2006-2007, Ext JS, LLC.
54857  *
54858  * Originally Released Under LGPL - original licence link has changed is not relivant.
54859  *
54860  * Fork - LGPL
54861  * <script type="text/javascript">
54862  */
54863 // private
54864 // This is a support class used internally by the Grid components
54865 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54866     this.grid = grid;
54867     this.view = grid.getView();
54868     // split the proxies so they don't interfere with mouse events
54869     this.proxyTop = Roo.DomHelper.append(document.body, {
54870         cls:"col-move-top", html:"&#160;"
54871     }, true);
54872     this.proxyBottom = Roo.DomHelper.append(document.body, {
54873         cls:"col-move-bottom", html:"&#160;"
54874     }, true);
54875     this.proxyTop.hide = this.proxyBottom.hide = function(){
54876         this.setLeftTop(-100,-100);
54877         this.setStyle("visibility", "hidden");
54878     };
54879     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54880     // temporarily disabled
54881     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54882     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54883 };
54884 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54885     proxyOffsets : [-4, -9],
54886     fly: Roo.Element.fly,
54887
54888     getTargetFromEvent : function(e){
54889         var t = Roo.lib.Event.getTarget(e);
54890         var cindex = this.view.findCellIndex(t);
54891         if(cindex !== false){
54892             return this.view.getHeaderCell(cindex);
54893         }
54894         return null;
54895     },
54896
54897     nextVisible : function(h){
54898         var v = this.view, cm = this.grid.colModel;
54899         h = h.nextSibling;
54900         while(h){
54901             if(!cm.isHidden(v.getCellIndex(h))){
54902                 return h;
54903             }
54904             h = h.nextSibling;
54905         }
54906         return null;
54907     },
54908
54909     prevVisible : function(h){
54910         var v = this.view, cm = this.grid.colModel;
54911         h = h.prevSibling;
54912         while(h){
54913             if(!cm.isHidden(v.getCellIndex(h))){
54914                 return h;
54915             }
54916             h = h.prevSibling;
54917         }
54918         return null;
54919     },
54920
54921     positionIndicator : function(h, n, e){
54922         var x = Roo.lib.Event.getPageX(e);
54923         var r = Roo.lib.Dom.getRegion(n.firstChild);
54924         var px, pt, py = r.top + this.proxyOffsets[1];
54925         if((r.right - x) <= (r.right-r.left)/2){
54926             px = r.right+this.view.borderWidth;
54927             pt = "after";
54928         }else{
54929             px = r.left;
54930             pt = "before";
54931         }
54932         var oldIndex = this.view.getCellIndex(h);
54933         var newIndex = this.view.getCellIndex(n);
54934
54935         if(this.grid.colModel.isFixed(newIndex)){
54936             return false;
54937         }
54938
54939         var locked = this.grid.colModel.isLocked(newIndex);
54940
54941         if(pt == "after"){
54942             newIndex++;
54943         }
54944         if(oldIndex < newIndex){
54945             newIndex--;
54946         }
54947         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54948             return false;
54949         }
54950         px +=  this.proxyOffsets[0];
54951         this.proxyTop.setLeftTop(px, py);
54952         this.proxyTop.show();
54953         if(!this.bottomOffset){
54954             this.bottomOffset = this.view.mainHd.getHeight();
54955         }
54956         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54957         this.proxyBottom.show();
54958         return pt;
54959     },
54960
54961     onNodeEnter : function(n, dd, e, data){
54962         if(data.header != n){
54963             this.positionIndicator(data.header, n, e);
54964         }
54965     },
54966
54967     onNodeOver : function(n, dd, e, data){
54968         var result = false;
54969         if(data.header != n){
54970             result = this.positionIndicator(data.header, n, e);
54971         }
54972         if(!result){
54973             this.proxyTop.hide();
54974             this.proxyBottom.hide();
54975         }
54976         return result ? this.dropAllowed : this.dropNotAllowed;
54977     },
54978
54979     onNodeOut : function(n, dd, e, data){
54980         this.proxyTop.hide();
54981         this.proxyBottom.hide();
54982     },
54983
54984     onNodeDrop : function(n, dd, e, data){
54985         var h = data.header;
54986         if(h != n){
54987             var cm = this.grid.colModel;
54988             var x = Roo.lib.Event.getPageX(e);
54989             var r = Roo.lib.Dom.getRegion(n.firstChild);
54990             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54991             var oldIndex = this.view.getCellIndex(h);
54992             var newIndex = this.view.getCellIndex(n);
54993             var locked = cm.isLocked(newIndex);
54994             if(pt == "after"){
54995                 newIndex++;
54996             }
54997             if(oldIndex < newIndex){
54998                 newIndex--;
54999             }
55000             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55001                 return false;
55002             }
55003             cm.setLocked(oldIndex, locked, true);
55004             cm.moveColumn(oldIndex, newIndex);
55005             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55006             return true;
55007         }
55008         return false;
55009     }
55010 });
55011 /*
55012  * Based on:
55013  * Ext JS Library 1.1.1
55014  * Copyright(c) 2006-2007, Ext JS, LLC.
55015  *
55016  * Originally Released Under LGPL - original licence link has changed is not relivant.
55017  *
55018  * Fork - LGPL
55019  * <script type="text/javascript">
55020  */
55021   
55022 /**
55023  * @class Roo.grid.GridView
55024  * @extends Roo.util.Observable
55025  *
55026  * @constructor
55027  * @param {Object} config
55028  */
55029 Roo.grid.GridView = function(config){
55030     Roo.grid.GridView.superclass.constructor.call(this);
55031     this.el = null;
55032
55033     Roo.apply(this, config);
55034 };
55035
55036 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55037
55038     unselectable :  'unselectable="on"',
55039     unselectableCls :  'x-unselectable',
55040     
55041     
55042     rowClass : "x-grid-row",
55043
55044     cellClass : "x-grid-col",
55045
55046     tdClass : "x-grid-td",
55047
55048     hdClass : "x-grid-hd",
55049
55050     splitClass : "x-grid-split",
55051
55052     sortClasses : ["sort-asc", "sort-desc"],
55053
55054     enableMoveAnim : false,
55055
55056     hlColor: "C3DAF9",
55057
55058     dh : Roo.DomHelper,
55059
55060     fly : Roo.Element.fly,
55061
55062     css : Roo.util.CSS,
55063
55064     borderWidth: 1,
55065
55066     splitOffset: 3,
55067
55068     scrollIncrement : 22,
55069
55070     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55071
55072     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55073
55074     bind : function(ds, cm){
55075         if(this.ds){
55076             this.ds.un("load", this.onLoad, this);
55077             this.ds.un("datachanged", this.onDataChange, this);
55078             this.ds.un("add", this.onAdd, this);
55079             this.ds.un("remove", this.onRemove, this);
55080             this.ds.un("update", this.onUpdate, this);
55081             this.ds.un("clear", this.onClear, this);
55082         }
55083         if(ds){
55084             ds.on("load", this.onLoad, this);
55085             ds.on("datachanged", this.onDataChange, this);
55086             ds.on("add", this.onAdd, this);
55087             ds.on("remove", this.onRemove, this);
55088             ds.on("update", this.onUpdate, this);
55089             ds.on("clear", this.onClear, this);
55090         }
55091         this.ds = ds;
55092
55093         if(this.cm){
55094             this.cm.un("widthchange", this.onColWidthChange, this);
55095             this.cm.un("headerchange", this.onHeaderChange, this);
55096             this.cm.un("hiddenchange", this.onHiddenChange, this);
55097             this.cm.un("columnmoved", this.onColumnMove, this);
55098             this.cm.un("columnlockchange", this.onColumnLock, this);
55099         }
55100         if(cm){
55101             this.generateRules(cm);
55102             cm.on("widthchange", this.onColWidthChange, this);
55103             cm.on("headerchange", this.onHeaderChange, this);
55104             cm.on("hiddenchange", this.onHiddenChange, this);
55105             cm.on("columnmoved", this.onColumnMove, this);
55106             cm.on("columnlockchange", this.onColumnLock, this);
55107         }
55108         this.cm = cm;
55109     },
55110
55111     init: function(grid){
55112         Roo.grid.GridView.superclass.init.call(this, grid);
55113
55114         this.bind(grid.dataSource, grid.colModel);
55115
55116         grid.on("headerclick", this.handleHeaderClick, this);
55117
55118         if(grid.trackMouseOver){
55119             grid.on("mouseover", this.onRowOver, this);
55120             grid.on("mouseout", this.onRowOut, this);
55121         }
55122         grid.cancelTextSelection = function(){};
55123         this.gridId = grid.id;
55124
55125         var tpls = this.templates || {};
55126
55127         if(!tpls.master){
55128             tpls.master = new Roo.Template(
55129                '<div class="x-grid" hidefocus="true">',
55130                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55131                   '<div class="x-grid-topbar"></div>',
55132                   '<div class="x-grid-scroller"><div></div></div>',
55133                   '<div class="x-grid-locked">',
55134                       '<div class="x-grid-header">{lockedHeader}</div>',
55135                       '<div class="x-grid-body">{lockedBody}</div>',
55136                   "</div>",
55137                   '<div class="x-grid-viewport">',
55138                       '<div class="x-grid-header">{header}</div>',
55139                       '<div class="x-grid-body">{body}</div>',
55140                   "</div>",
55141                   '<div class="x-grid-bottombar"></div>',
55142                  
55143                   '<div class="x-grid-resize-proxy">&#160;</div>',
55144                "</div>"
55145             );
55146             tpls.master.disableformats = true;
55147         }
55148
55149         if(!tpls.header){
55150             tpls.header = new Roo.Template(
55151                '<table border="0" cellspacing="0" cellpadding="0">',
55152                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55153                "</table>{splits}"
55154             );
55155             tpls.header.disableformats = true;
55156         }
55157         tpls.header.compile();
55158
55159         if(!tpls.hcell){
55160             tpls.hcell = new Roo.Template(
55161                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55162                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55163                 "</div></td>"
55164              );
55165              tpls.hcell.disableFormats = true;
55166         }
55167         tpls.hcell.compile();
55168
55169         if(!tpls.hsplit){
55170             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55171                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55172             tpls.hsplit.disableFormats = true;
55173         }
55174         tpls.hsplit.compile();
55175
55176         if(!tpls.body){
55177             tpls.body = new Roo.Template(
55178                '<table border="0" cellspacing="0" cellpadding="0">',
55179                "<tbody>{rows}</tbody>",
55180                "</table>"
55181             );
55182             tpls.body.disableFormats = true;
55183         }
55184         tpls.body.compile();
55185
55186         if(!tpls.row){
55187             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55188             tpls.row.disableFormats = true;
55189         }
55190         tpls.row.compile();
55191
55192         if(!tpls.cell){
55193             tpls.cell = new Roo.Template(
55194                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55195                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55196                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55197                 "</td>"
55198             );
55199             tpls.cell.disableFormats = true;
55200         }
55201         tpls.cell.compile();
55202
55203         this.templates = tpls;
55204     },
55205
55206     // remap these for backwards compat
55207     onColWidthChange : function(){
55208         this.updateColumns.apply(this, arguments);
55209     },
55210     onHeaderChange : function(){
55211         this.updateHeaders.apply(this, arguments);
55212     }, 
55213     onHiddenChange : function(){
55214         this.handleHiddenChange.apply(this, arguments);
55215     },
55216     onColumnMove : function(){
55217         this.handleColumnMove.apply(this, arguments);
55218     },
55219     onColumnLock : function(){
55220         this.handleLockChange.apply(this, arguments);
55221     },
55222
55223     onDataChange : function(){
55224         this.refresh();
55225         this.updateHeaderSortState();
55226     },
55227
55228     onClear : function(){
55229         this.refresh();
55230     },
55231
55232     onUpdate : function(ds, record){
55233         this.refreshRow(record);
55234     },
55235
55236     refreshRow : function(record){
55237         var ds = this.ds, index;
55238         if(typeof record == 'number'){
55239             index = record;
55240             record = ds.getAt(index);
55241         }else{
55242             index = ds.indexOf(record);
55243         }
55244         this.insertRows(ds, index, index, true);
55245         this.onRemove(ds, record, index+1, true);
55246         this.syncRowHeights(index, index);
55247         this.layout();
55248         this.fireEvent("rowupdated", this, index, record);
55249     },
55250
55251     onAdd : function(ds, records, index){
55252         this.insertRows(ds, index, index + (records.length-1));
55253     },
55254
55255     onRemove : function(ds, record, index, isUpdate){
55256         if(isUpdate !== true){
55257             this.fireEvent("beforerowremoved", this, index, record);
55258         }
55259         var bt = this.getBodyTable(), lt = this.getLockedTable();
55260         if(bt.rows[index]){
55261             bt.firstChild.removeChild(bt.rows[index]);
55262         }
55263         if(lt.rows[index]){
55264             lt.firstChild.removeChild(lt.rows[index]);
55265         }
55266         if(isUpdate !== true){
55267             this.stripeRows(index);
55268             this.syncRowHeights(index, index);
55269             this.layout();
55270             this.fireEvent("rowremoved", this, index, record);
55271         }
55272     },
55273
55274     onLoad : function(){
55275         this.scrollToTop();
55276     },
55277
55278     /**
55279      * Scrolls the grid to the top
55280      */
55281     scrollToTop : function(){
55282         if(this.scroller){
55283             this.scroller.dom.scrollTop = 0;
55284             this.syncScroll();
55285         }
55286     },
55287
55288     /**
55289      * Gets a panel in the header of the grid that can be used for toolbars etc.
55290      * After modifying the contents of this panel a call to grid.autoSize() may be
55291      * required to register any changes in size.
55292      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55293      * @return Roo.Element
55294      */
55295     getHeaderPanel : function(doShow){
55296         if(doShow){
55297             this.headerPanel.show();
55298         }
55299         return this.headerPanel;
55300     },
55301
55302     /**
55303      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55304      * After modifying the contents of this panel a call to grid.autoSize() may be
55305      * required to register any changes in size.
55306      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55307      * @return Roo.Element
55308      */
55309     getFooterPanel : function(doShow){
55310         if(doShow){
55311             this.footerPanel.show();
55312         }
55313         return this.footerPanel;
55314     },
55315
55316     initElements : function(){
55317         var E = Roo.Element;
55318         var el = this.grid.getGridEl().dom.firstChild;
55319         var cs = el.childNodes;
55320
55321         this.el = new E(el);
55322         
55323          this.focusEl = new E(el.firstChild);
55324         this.focusEl.swallowEvent("click", true);
55325         
55326         this.headerPanel = new E(cs[1]);
55327         this.headerPanel.enableDisplayMode("block");
55328
55329         this.scroller = new E(cs[2]);
55330         this.scrollSizer = new E(this.scroller.dom.firstChild);
55331
55332         this.lockedWrap = new E(cs[3]);
55333         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55334         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55335
55336         this.mainWrap = new E(cs[4]);
55337         this.mainHd = new E(this.mainWrap.dom.firstChild);
55338         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55339
55340         this.footerPanel = new E(cs[5]);
55341         this.footerPanel.enableDisplayMode("block");
55342
55343         this.resizeProxy = new E(cs[6]);
55344
55345         this.headerSelector = String.format(
55346            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55347            this.lockedHd.id, this.mainHd.id
55348         );
55349
55350         this.splitterSelector = String.format(
55351            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55352            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55353         );
55354     },
55355     idToCssName : function(s)
55356     {
55357         return s.replace(/[^a-z0-9]+/ig, '-');
55358     },
55359
55360     getHeaderCell : function(index){
55361         return Roo.DomQuery.select(this.headerSelector)[index];
55362     },
55363
55364     getHeaderCellMeasure : function(index){
55365         return this.getHeaderCell(index).firstChild;
55366     },
55367
55368     getHeaderCellText : function(index){
55369         return this.getHeaderCell(index).firstChild.firstChild;
55370     },
55371
55372     getLockedTable : function(){
55373         return this.lockedBody.dom.firstChild;
55374     },
55375
55376     getBodyTable : function(){
55377         return this.mainBody.dom.firstChild;
55378     },
55379
55380     getLockedRow : function(index){
55381         return this.getLockedTable().rows[index];
55382     },
55383
55384     getRow : function(index){
55385         return this.getBodyTable().rows[index];
55386     },
55387
55388     getRowComposite : function(index){
55389         if(!this.rowEl){
55390             this.rowEl = new Roo.CompositeElementLite();
55391         }
55392         var els = [], lrow, mrow;
55393         if(lrow = this.getLockedRow(index)){
55394             els.push(lrow);
55395         }
55396         if(mrow = this.getRow(index)){
55397             els.push(mrow);
55398         }
55399         this.rowEl.elements = els;
55400         return this.rowEl;
55401     },
55402     /**
55403      * Gets the 'td' of the cell
55404      * 
55405      * @param {Integer} rowIndex row to select
55406      * @param {Integer} colIndex column to select
55407      * 
55408      * @return {Object} 
55409      */
55410     getCell : function(rowIndex, colIndex){
55411         var locked = this.cm.getLockedCount();
55412         var source;
55413         if(colIndex < locked){
55414             source = this.lockedBody.dom.firstChild;
55415         }else{
55416             source = this.mainBody.dom.firstChild;
55417             colIndex -= locked;
55418         }
55419         return source.rows[rowIndex].childNodes[colIndex];
55420     },
55421
55422     getCellText : function(rowIndex, colIndex){
55423         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55424     },
55425
55426     getCellBox : function(cell){
55427         var b = this.fly(cell).getBox();
55428         if(Roo.isOpera){ // opera fails to report the Y
55429             b.y = cell.offsetTop + this.mainBody.getY();
55430         }
55431         return b;
55432     },
55433
55434     getCellIndex : function(cell){
55435         var id = String(cell.className).match(this.cellRE);
55436         if(id){
55437             return parseInt(id[1], 10);
55438         }
55439         return 0;
55440     },
55441
55442     findHeaderIndex : function(n){
55443         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55444         return r ? this.getCellIndex(r) : false;
55445     },
55446
55447     findHeaderCell : function(n){
55448         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55449         return r ? r : false;
55450     },
55451
55452     findRowIndex : function(n){
55453         if(!n){
55454             return false;
55455         }
55456         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55457         return r ? r.rowIndex : false;
55458     },
55459
55460     findCellIndex : function(node){
55461         var stop = this.el.dom;
55462         while(node && node != stop){
55463             if(this.findRE.test(node.className)){
55464                 return this.getCellIndex(node);
55465             }
55466             node = node.parentNode;
55467         }
55468         return false;
55469     },
55470
55471     getColumnId : function(index){
55472         return this.cm.getColumnId(index);
55473     },
55474
55475     getSplitters : function()
55476     {
55477         if(this.splitterSelector){
55478            return Roo.DomQuery.select(this.splitterSelector);
55479         }else{
55480             return null;
55481       }
55482     },
55483
55484     getSplitter : function(index){
55485         return this.getSplitters()[index];
55486     },
55487
55488     onRowOver : function(e, t){
55489         var row;
55490         if((row = this.findRowIndex(t)) !== false){
55491             this.getRowComposite(row).addClass("x-grid-row-over");
55492         }
55493     },
55494
55495     onRowOut : function(e, t){
55496         var row;
55497         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55498             this.getRowComposite(row).removeClass("x-grid-row-over");
55499         }
55500     },
55501
55502     renderHeaders : function(){
55503         var cm = this.cm;
55504         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55505         var cb = [], lb = [], sb = [], lsb = [], p = {};
55506         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55507             p.cellId = "x-grid-hd-0-" + i;
55508             p.splitId = "x-grid-csplit-0-" + i;
55509             p.id = cm.getColumnId(i);
55510             p.value = cm.getColumnHeader(i) || "";
55511             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55512             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55513             if(!cm.isLocked(i)){
55514                 cb[cb.length] = ct.apply(p);
55515                 sb[sb.length] = st.apply(p);
55516             }else{
55517                 lb[lb.length] = ct.apply(p);
55518                 lsb[lsb.length] = st.apply(p);
55519             }
55520         }
55521         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55522                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55523     },
55524
55525     updateHeaders : function(){
55526         var html = this.renderHeaders();
55527         this.lockedHd.update(html[0]);
55528         this.mainHd.update(html[1]);
55529     },
55530
55531     /**
55532      * Focuses the specified row.
55533      * @param {Number} row The row index
55534      */
55535     focusRow : function(row)
55536     {
55537         //Roo.log('GridView.focusRow');
55538         var x = this.scroller.dom.scrollLeft;
55539         this.focusCell(row, 0, false);
55540         this.scroller.dom.scrollLeft = x;
55541     },
55542
55543     /**
55544      * Focuses the specified cell.
55545      * @param {Number} row The row index
55546      * @param {Number} col The column index
55547      * @param {Boolean} hscroll false to disable horizontal scrolling
55548      */
55549     focusCell : function(row, col, hscroll)
55550     {
55551         //Roo.log('GridView.focusCell');
55552         var el = this.ensureVisible(row, col, hscroll);
55553         this.focusEl.alignTo(el, "tl-tl");
55554         if(Roo.isGecko){
55555             this.focusEl.focus();
55556         }else{
55557             this.focusEl.focus.defer(1, this.focusEl);
55558         }
55559     },
55560
55561     /**
55562      * Scrolls the specified cell into view
55563      * @param {Number} row The row index
55564      * @param {Number} col The column index
55565      * @param {Boolean} hscroll false to disable horizontal scrolling
55566      */
55567     ensureVisible : function(row, col, hscroll)
55568     {
55569         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55570         //return null; //disable for testing.
55571         if(typeof row != "number"){
55572             row = row.rowIndex;
55573         }
55574         if(row < 0 && row >= this.ds.getCount()){
55575             return  null;
55576         }
55577         col = (col !== undefined ? col : 0);
55578         var cm = this.grid.colModel;
55579         while(cm.isHidden(col)){
55580             col++;
55581         }
55582
55583         var el = this.getCell(row, col);
55584         if(!el){
55585             return null;
55586         }
55587         var c = this.scroller.dom;
55588
55589         var ctop = parseInt(el.offsetTop, 10);
55590         var cleft = parseInt(el.offsetLeft, 10);
55591         var cbot = ctop + el.offsetHeight;
55592         var cright = cleft + el.offsetWidth;
55593         
55594         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55595         var stop = parseInt(c.scrollTop, 10);
55596         var sleft = parseInt(c.scrollLeft, 10);
55597         var sbot = stop + ch;
55598         var sright = sleft + c.clientWidth;
55599         /*
55600         Roo.log('GridView.ensureVisible:' +
55601                 ' ctop:' + ctop +
55602                 ' c.clientHeight:' + c.clientHeight +
55603                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55604                 ' stop:' + stop +
55605                 ' cbot:' + cbot +
55606                 ' sbot:' + sbot +
55607                 ' ch:' + ch  
55608                 );
55609         */
55610         if(ctop < stop){
55611              c.scrollTop = ctop;
55612             //Roo.log("set scrolltop to ctop DISABLE?");
55613         }else if(cbot > sbot){
55614             //Roo.log("set scrolltop to cbot-ch");
55615             c.scrollTop = cbot-ch;
55616         }
55617         
55618         if(hscroll !== false){
55619             if(cleft < sleft){
55620                 c.scrollLeft = cleft;
55621             }else if(cright > sright){
55622                 c.scrollLeft = cright-c.clientWidth;
55623             }
55624         }
55625          
55626         return el;
55627     },
55628
55629     updateColumns : function(){
55630         this.grid.stopEditing();
55631         var cm = this.grid.colModel, colIds = this.getColumnIds();
55632         //var totalWidth = cm.getTotalWidth();
55633         var pos = 0;
55634         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55635             //if(cm.isHidden(i)) continue;
55636             var w = cm.getColumnWidth(i);
55637             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55638             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55639         }
55640         this.updateSplitters();
55641     },
55642
55643     generateRules : function(cm){
55644         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55645         Roo.util.CSS.removeStyleSheet(rulesId);
55646         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55647             var cid = cm.getColumnId(i);
55648             var align = '';
55649             if(cm.config[i].align){
55650                 align = 'text-align:'+cm.config[i].align+';';
55651             }
55652             var hidden = '';
55653             if(cm.isHidden(i)){
55654                 hidden = 'display:none;';
55655             }
55656             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55657             ruleBuf.push(
55658                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55659                     this.hdSelector, cid, " {\n", align, width, "}\n",
55660                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55661                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55662         }
55663         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55664     },
55665
55666     updateSplitters : function(){
55667         var cm = this.cm, s = this.getSplitters();
55668         if(s){ // splitters not created yet
55669             var pos = 0, locked = true;
55670             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55671                 if(cm.isHidden(i)) {
55672                     continue;
55673                 }
55674                 var w = cm.getColumnWidth(i); // make sure it's a number
55675                 if(!cm.isLocked(i) && locked){
55676                     pos = 0;
55677                     locked = false;
55678                 }
55679                 pos += w;
55680                 s[i].style.left = (pos-this.splitOffset) + "px";
55681             }
55682         }
55683     },
55684
55685     handleHiddenChange : function(colModel, colIndex, hidden){
55686         if(hidden){
55687             this.hideColumn(colIndex);
55688         }else{
55689             this.unhideColumn(colIndex);
55690         }
55691     },
55692
55693     hideColumn : function(colIndex){
55694         var cid = this.getColumnId(colIndex);
55695         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55696         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55697         if(Roo.isSafari){
55698             this.updateHeaders();
55699         }
55700         this.updateSplitters();
55701         this.layout();
55702     },
55703
55704     unhideColumn : function(colIndex){
55705         var cid = this.getColumnId(colIndex);
55706         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55707         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55708
55709         if(Roo.isSafari){
55710             this.updateHeaders();
55711         }
55712         this.updateSplitters();
55713         this.layout();
55714     },
55715
55716     insertRows : function(dm, firstRow, lastRow, isUpdate){
55717         if(firstRow == 0 && lastRow == dm.getCount()-1){
55718             this.refresh();
55719         }else{
55720             if(!isUpdate){
55721                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55722             }
55723             var s = this.getScrollState();
55724             var markup = this.renderRows(firstRow, lastRow);
55725             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55726             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55727             this.restoreScroll(s);
55728             if(!isUpdate){
55729                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55730                 this.syncRowHeights(firstRow, lastRow);
55731                 this.stripeRows(firstRow);
55732                 this.layout();
55733             }
55734         }
55735     },
55736
55737     bufferRows : function(markup, target, index){
55738         var before = null, trows = target.rows, tbody = target.tBodies[0];
55739         if(index < trows.length){
55740             before = trows[index];
55741         }
55742         var b = document.createElement("div");
55743         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55744         var rows = b.firstChild.rows;
55745         for(var i = 0, len = rows.length; i < len; i++){
55746             if(before){
55747                 tbody.insertBefore(rows[0], before);
55748             }else{
55749                 tbody.appendChild(rows[0]);
55750             }
55751         }
55752         b.innerHTML = "";
55753         b = null;
55754     },
55755
55756     deleteRows : function(dm, firstRow, lastRow){
55757         if(dm.getRowCount()<1){
55758             this.fireEvent("beforerefresh", this);
55759             this.mainBody.update("");
55760             this.lockedBody.update("");
55761             this.fireEvent("refresh", this);
55762         }else{
55763             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55764             var bt = this.getBodyTable();
55765             var tbody = bt.firstChild;
55766             var rows = bt.rows;
55767             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55768                 tbody.removeChild(rows[firstRow]);
55769             }
55770             this.stripeRows(firstRow);
55771             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55772         }
55773     },
55774
55775     updateRows : function(dataSource, firstRow, lastRow){
55776         var s = this.getScrollState();
55777         this.refresh();
55778         this.restoreScroll(s);
55779     },
55780
55781     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55782         if(!noRefresh){
55783            this.refresh();
55784         }
55785         this.updateHeaderSortState();
55786     },
55787
55788     getScrollState : function(){
55789         
55790         var sb = this.scroller.dom;
55791         return {left: sb.scrollLeft, top: sb.scrollTop};
55792     },
55793
55794     stripeRows : function(startRow){
55795         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55796             return;
55797         }
55798         startRow = startRow || 0;
55799         var rows = this.getBodyTable().rows;
55800         var lrows = this.getLockedTable().rows;
55801         var cls = ' x-grid-row-alt ';
55802         for(var i = startRow, len = rows.length; i < len; i++){
55803             var row = rows[i], lrow = lrows[i];
55804             var isAlt = ((i+1) % 2 == 0);
55805             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55806             if(isAlt == hasAlt){
55807                 continue;
55808             }
55809             if(isAlt){
55810                 row.className += " x-grid-row-alt";
55811             }else{
55812                 row.className = row.className.replace("x-grid-row-alt", "");
55813             }
55814             if(lrow){
55815                 lrow.className = row.className;
55816             }
55817         }
55818     },
55819
55820     restoreScroll : function(state){
55821         //Roo.log('GridView.restoreScroll');
55822         var sb = this.scroller.dom;
55823         sb.scrollLeft = state.left;
55824         sb.scrollTop = state.top;
55825         this.syncScroll();
55826     },
55827
55828     syncScroll : function(){
55829         //Roo.log('GridView.syncScroll');
55830         var sb = this.scroller.dom;
55831         var sh = this.mainHd.dom;
55832         var bs = this.mainBody.dom;
55833         var lv = this.lockedBody.dom;
55834         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55835         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55836     },
55837
55838     handleScroll : function(e){
55839         this.syncScroll();
55840         var sb = this.scroller.dom;
55841         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55842         e.stopEvent();
55843     },
55844
55845     handleWheel : function(e){
55846         var d = e.getWheelDelta();
55847         this.scroller.dom.scrollTop -= d*22;
55848         // set this here to prevent jumpy scrolling on large tables
55849         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55850         e.stopEvent();
55851     },
55852
55853     renderRows : function(startRow, endRow){
55854         // pull in all the crap needed to render rows
55855         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55856         var colCount = cm.getColumnCount();
55857
55858         if(ds.getCount() < 1){
55859             return ["", ""];
55860         }
55861
55862         // build a map for all the columns
55863         var cs = [];
55864         for(var i = 0; i < colCount; i++){
55865             var name = cm.getDataIndex(i);
55866             cs[i] = {
55867                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55868                 renderer : cm.getRenderer(i),
55869                 id : cm.getColumnId(i),
55870                 locked : cm.isLocked(i),
55871                 has_editor : cm.isCellEditable(i)
55872             };
55873         }
55874
55875         startRow = startRow || 0;
55876         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55877
55878         // records to render
55879         var rs = ds.getRange(startRow, endRow);
55880
55881         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55882     },
55883
55884     // As much as I hate to duplicate code, this was branched because FireFox really hates
55885     // [].join("") on strings. The performance difference was substantial enough to
55886     // branch this function
55887     doRender : Roo.isGecko ?
55888             function(cs, rs, ds, startRow, colCount, stripe){
55889                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55890                 // buffers
55891                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55892                 
55893                 var hasListener = this.grid.hasListener('rowclass');
55894                 var rowcfg = {};
55895                 for(var j = 0, len = rs.length; j < len; j++){
55896                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55897                     for(var i = 0; i < colCount; i++){
55898                         c = cs[i];
55899                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55900                         p.id = c.id;
55901                         p.css = p.attr = "";
55902                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55903                         if(p.value == undefined || p.value === "") {
55904                             p.value = "&#160;";
55905                         }
55906                         if(c.has_editor){
55907                             p.css += ' x-grid-editable-cell';
55908                         }
55909                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55910                             p.css +=  ' x-grid-dirty-cell';
55911                         }
55912                         var markup = ct.apply(p);
55913                         if(!c.locked){
55914                             cb+= markup;
55915                         }else{
55916                             lcb+= markup;
55917                         }
55918                     }
55919                     var alt = [];
55920                     if(stripe && ((rowIndex+1) % 2 == 0)){
55921                         alt.push("x-grid-row-alt")
55922                     }
55923                     if(r.dirty){
55924                         alt.push(  " x-grid-dirty-row");
55925                     }
55926                     rp.cells = lcb;
55927                     if(this.getRowClass){
55928                         alt.push(this.getRowClass(r, rowIndex));
55929                     }
55930                     if (hasListener) {
55931                         rowcfg = {
55932                              
55933                             record: r,
55934                             rowIndex : rowIndex,
55935                             rowClass : ''
55936                         };
55937                         this.grid.fireEvent('rowclass', this, rowcfg);
55938                         alt.push(rowcfg.rowClass);
55939                     }
55940                     rp.alt = alt.join(" ");
55941                     lbuf+= rt.apply(rp);
55942                     rp.cells = cb;
55943                     buf+=  rt.apply(rp);
55944                 }
55945                 return [lbuf, buf];
55946             } :
55947             function(cs, rs, ds, startRow, colCount, stripe){
55948                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55949                 // buffers
55950                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55951                 var hasListener = this.grid.hasListener('rowclass');
55952  
55953                 var rowcfg = {};
55954                 for(var j = 0, len = rs.length; j < len; j++){
55955                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55956                     for(var i = 0; i < colCount; i++){
55957                         c = cs[i];
55958                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55959                         p.id = c.id;
55960                         p.css = p.attr = "";
55961                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55962                         if(p.value == undefined || p.value === "") {
55963                             p.value = "&#160;";
55964                         }
55965                         //Roo.log(c);
55966                          if(c.has_editor){
55967                             p.css += ' x-grid-editable-cell';
55968                         }
55969                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55970                             p.css += ' x-grid-dirty-cell' 
55971                         }
55972                         
55973                         var markup = ct.apply(p);
55974                         if(!c.locked){
55975                             cb[cb.length] = markup;
55976                         }else{
55977                             lcb[lcb.length] = markup;
55978                         }
55979                     }
55980                     var alt = [];
55981                     if(stripe && ((rowIndex+1) % 2 == 0)){
55982                         alt.push( "x-grid-row-alt");
55983                     }
55984                     if(r.dirty){
55985                         alt.push(" x-grid-dirty-row");
55986                     }
55987                     rp.cells = lcb;
55988                     if(this.getRowClass){
55989                         alt.push( this.getRowClass(r, rowIndex));
55990                     }
55991                     if (hasListener) {
55992                         rowcfg = {
55993                              
55994                             record: r,
55995                             rowIndex : rowIndex,
55996                             rowClass : ''
55997                         };
55998                         this.grid.fireEvent('rowclass', this, rowcfg);
55999                         alt.push(rowcfg.rowClass);
56000                     }
56001                     
56002                     rp.alt = alt.join(" ");
56003                     rp.cells = lcb.join("");
56004                     lbuf[lbuf.length] = rt.apply(rp);
56005                     rp.cells = cb.join("");
56006                     buf[buf.length] =  rt.apply(rp);
56007                 }
56008                 return [lbuf.join(""), buf.join("")];
56009             },
56010
56011     renderBody : function(){
56012         var markup = this.renderRows();
56013         var bt = this.templates.body;
56014         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56015     },
56016
56017     /**
56018      * Refreshes the grid
56019      * @param {Boolean} headersToo
56020      */
56021     refresh : function(headersToo){
56022         this.fireEvent("beforerefresh", this);
56023         this.grid.stopEditing();
56024         var result = this.renderBody();
56025         this.lockedBody.update(result[0]);
56026         this.mainBody.update(result[1]);
56027         if(headersToo === true){
56028             this.updateHeaders();
56029             this.updateColumns();
56030             this.updateSplitters();
56031             this.updateHeaderSortState();
56032         }
56033         this.syncRowHeights();
56034         this.layout();
56035         this.fireEvent("refresh", this);
56036     },
56037
56038     handleColumnMove : function(cm, oldIndex, newIndex){
56039         this.indexMap = null;
56040         var s = this.getScrollState();
56041         this.refresh(true);
56042         this.restoreScroll(s);
56043         this.afterMove(newIndex);
56044     },
56045
56046     afterMove : function(colIndex){
56047         if(this.enableMoveAnim && Roo.enableFx){
56048             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56049         }
56050         // if multisort - fix sortOrder, and reload..
56051         if (this.grid.dataSource.multiSort) {
56052             // the we can call sort again..
56053             var dm = this.grid.dataSource;
56054             var cm = this.grid.colModel;
56055             var so = [];
56056             for(var i = 0; i < cm.config.length; i++ ) {
56057                 
56058                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56059                     continue; // dont' bother, it's not in sort list or being set.
56060                 }
56061                 
56062                 so.push(cm.config[i].dataIndex);
56063             };
56064             dm.sortOrder = so;
56065             dm.load(dm.lastOptions);
56066             
56067             
56068         }
56069         
56070     },
56071
56072     updateCell : function(dm, rowIndex, dataIndex){
56073         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56074         if(typeof colIndex == "undefined"){ // not present in grid
56075             return;
56076         }
56077         var cm = this.grid.colModel;
56078         var cell = this.getCell(rowIndex, colIndex);
56079         var cellText = this.getCellText(rowIndex, colIndex);
56080
56081         var p = {
56082             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56083             id : cm.getColumnId(colIndex),
56084             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56085         };
56086         var renderer = cm.getRenderer(colIndex);
56087         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56088         if(typeof val == "undefined" || val === "") {
56089             val = "&#160;";
56090         }
56091         cellText.innerHTML = val;
56092         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56093         this.syncRowHeights(rowIndex, rowIndex);
56094     },
56095
56096     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56097         var maxWidth = 0;
56098         if(this.grid.autoSizeHeaders){
56099             var h = this.getHeaderCellMeasure(colIndex);
56100             maxWidth = Math.max(maxWidth, h.scrollWidth);
56101         }
56102         var tb, index;
56103         if(this.cm.isLocked(colIndex)){
56104             tb = this.getLockedTable();
56105             index = colIndex;
56106         }else{
56107             tb = this.getBodyTable();
56108             index = colIndex - this.cm.getLockedCount();
56109         }
56110         if(tb && tb.rows){
56111             var rows = tb.rows;
56112             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56113             for(var i = 0; i < stopIndex; i++){
56114                 var cell = rows[i].childNodes[index].firstChild;
56115                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56116             }
56117         }
56118         return maxWidth + /*margin for error in IE*/ 5;
56119     },
56120     /**
56121      * Autofit a column to its content.
56122      * @param {Number} colIndex
56123      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56124      */
56125      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56126          if(this.cm.isHidden(colIndex)){
56127              return; // can't calc a hidden column
56128          }
56129         if(forceMinSize){
56130             var cid = this.cm.getColumnId(colIndex);
56131             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56132            if(this.grid.autoSizeHeaders){
56133                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56134            }
56135         }
56136         var newWidth = this.calcColumnWidth(colIndex);
56137         this.cm.setColumnWidth(colIndex,
56138             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56139         if(!suppressEvent){
56140             this.grid.fireEvent("columnresize", colIndex, newWidth);
56141         }
56142     },
56143
56144     /**
56145      * Autofits all columns to their content and then expands to fit any extra space in the grid
56146      */
56147      autoSizeColumns : function(){
56148         var cm = this.grid.colModel;
56149         var colCount = cm.getColumnCount();
56150         for(var i = 0; i < colCount; i++){
56151             this.autoSizeColumn(i, true, true);
56152         }
56153         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56154             this.fitColumns();
56155         }else{
56156             this.updateColumns();
56157             this.layout();
56158         }
56159     },
56160
56161     /**
56162      * Autofits all columns to the grid's width proportionate with their current size
56163      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56164      */
56165     fitColumns : function(reserveScrollSpace){
56166         var cm = this.grid.colModel;
56167         var colCount = cm.getColumnCount();
56168         var cols = [];
56169         var width = 0;
56170         var i, w;
56171         for (i = 0; i < colCount; i++){
56172             if(!cm.isHidden(i) && !cm.isFixed(i)){
56173                 w = cm.getColumnWidth(i);
56174                 cols.push(i);
56175                 cols.push(w);
56176                 width += w;
56177             }
56178         }
56179         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56180         if(reserveScrollSpace){
56181             avail -= 17;
56182         }
56183         var frac = (avail - cm.getTotalWidth())/width;
56184         while (cols.length){
56185             w = cols.pop();
56186             i = cols.pop();
56187             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56188         }
56189         this.updateColumns();
56190         this.layout();
56191     },
56192
56193     onRowSelect : function(rowIndex){
56194         var row = this.getRowComposite(rowIndex);
56195         row.addClass("x-grid-row-selected");
56196     },
56197
56198     onRowDeselect : function(rowIndex){
56199         var row = this.getRowComposite(rowIndex);
56200         row.removeClass("x-grid-row-selected");
56201     },
56202
56203     onCellSelect : function(row, col){
56204         var cell = this.getCell(row, col);
56205         if(cell){
56206             Roo.fly(cell).addClass("x-grid-cell-selected");
56207         }
56208     },
56209
56210     onCellDeselect : function(row, col){
56211         var cell = this.getCell(row, col);
56212         if(cell){
56213             Roo.fly(cell).removeClass("x-grid-cell-selected");
56214         }
56215     },
56216
56217     updateHeaderSortState : function(){
56218         
56219         // sort state can be single { field: xxx, direction : yyy}
56220         // or   { xxx=>ASC , yyy : DESC ..... }
56221         
56222         var mstate = {};
56223         if (!this.ds.multiSort) { 
56224             var state = this.ds.getSortState();
56225             if(!state){
56226                 return;
56227             }
56228             mstate[state.field] = state.direction;
56229             // FIXME... - this is not used here.. but might be elsewhere..
56230             this.sortState = state;
56231             
56232         } else {
56233             mstate = this.ds.sortToggle;
56234         }
56235         //remove existing sort classes..
56236         
56237         var sc = this.sortClasses;
56238         var hds = this.el.select(this.headerSelector).removeClass(sc);
56239         
56240         for(var f in mstate) {
56241         
56242             var sortColumn = this.cm.findColumnIndex(f);
56243             
56244             if(sortColumn != -1){
56245                 var sortDir = mstate[f];        
56246                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56247             }
56248         }
56249         
56250          
56251         
56252     },
56253
56254
56255     handleHeaderClick : function(g, index,e){
56256         
56257         Roo.log("header click");
56258         
56259         if (Roo.isTouch) {
56260             // touch events on header are handled by context
56261             this.handleHdCtx(g,index,e);
56262             return;
56263         }
56264         
56265         
56266         if(this.headersDisabled){
56267             return;
56268         }
56269         var dm = g.dataSource, cm = g.colModel;
56270         if(!cm.isSortable(index)){
56271             return;
56272         }
56273         g.stopEditing();
56274         
56275         if (dm.multiSort) {
56276             // update the sortOrder
56277             var so = [];
56278             for(var i = 0; i < cm.config.length; i++ ) {
56279                 
56280                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56281                     continue; // dont' bother, it's not in sort list or being set.
56282                 }
56283                 
56284                 so.push(cm.config[i].dataIndex);
56285             };
56286             dm.sortOrder = so;
56287         }
56288         
56289         
56290         dm.sort(cm.getDataIndex(index));
56291     },
56292
56293
56294     destroy : function(){
56295         if(this.colMenu){
56296             this.colMenu.removeAll();
56297             Roo.menu.MenuMgr.unregister(this.colMenu);
56298             this.colMenu.getEl().remove();
56299             delete this.colMenu;
56300         }
56301         if(this.hmenu){
56302             this.hmenu.removeAll();
56303             Roo.menu.MenuMgr.unregister(this.hmenu);
56304             this.hmenu.getEl().remove();
56305             delete this.hmenu;
56306         }
56307         if(this.grid.enableColumnMove){
56308             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56309             if(dds){
56310                 for(var dd in dds){
56311                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56312                         var elid = dds[dd].dragElId;
56313                         dds[dd].unreg();
56314                         Roo.get(elid).remove();
56315                     } else if(dds[dd].config.isTarget){
56316                         dds[dd].proxyTop.remove();
56317                         dds[dd].proxyBottom.remove();
56318                         dds[dd].unreg();
56319                     }
56320                     if(Roo.dd.DDM.locationCache[dd]){
56321                         delete Roo.dd.DDM.locationCache[dd];
56322                     }
56323                 }
56324                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56325             }
56326         }
56327         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56328         this.bind(null, null);
56329         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56330     },
56331
56332     handleLockChange : function(){
56333         this.refresh(true);
56334     },
56335
56336     onDenyColumnLock : function(){
56337
56338     },
56339
56340     onDenyColumnHide : function(){
56341
56342     },
56343
56344     handleHdMenuClick : function(item){
56345         var index = this.hdCtxIndex;
56346         var cm = this.cm, ds = this.ds;
56347         switch(item.id){
56348             case "asc":
56349                 ds.sort(cm.getDataIndex(index), "ASC");
56350                 break;
56351             case "desc":
56352                 ds.sort(cm.getDataIndex(index), "DESC");
56353                 break;
56354             case "lock":
56355                 var lc = cm.getLockedCount();
56356                 if(cm.getColumnCount(true) <= lc+1){
56357                     this.onDenyColumnLock();
56358                     return;
56359                 }
56360                 if(lc != index){
56361                     cm.setLocked(index, true, true);
56362                     cm.moveColumn(index, lc);
56363                     this.grid.fireEvent("columnmove", index, lc);
56364                 }else{
56365                     cm.setLocked(index, true);
56366                 }
56367             break;
56368             case "unlock":
56369                 var lc = cm.getLockedCount();
56370                 if((lc-1) != index){
56371                     cm.setLocked(index, false, true);
56372                     cm.moveColumn(index, lc-1);
56373                     this.grid.fireEvent("columnmove", index, lc-1);
56374                 }else{
56375                     cm.setLocked(index, false);
56376                 }
56377             break;
56378             case 'wider': // used to expand cols on touch..
56379             case 'narrow':
56380                 var cw = cm.getColumnWidth(index);
56381                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56382                 cw = Math.max(0, cw);
56383                 cw = Math.min(cw,4000);
56384                 cm.setColumnWidth(index, cw);
56385                 break;
56386                 
56387             default:
56388                 index = cm.getIndexById(item.id.substr(4));
56389                 if(index != -1){
56390                     if(item.checked && cm.getColumnCount(true) <= 1){
56391                         this.onDenyColumnHide();
56392                         return false;
56393                     }
56394                     cm.setHidden(index, item.checked);
56395                 }
56396         }
56397         return true;
56398     },
56399
56400     beforeColMenuShow : function(){
56401         var cm = this.cm,  colCount = cm.getColumnCount();
56402         this.colMenu.removeAll();
56403         for(var i = 0; i < colCount; i++){
56404             this.colMenu.add(new Roo.menu.CheckItem({
56405                 id: "col-"+cm.getColumnId(i),
56406                 text: cm.getColumnHeader(i),
56407                 checked: !cm.isHidden(i),
56408                 hideOnClick:false
56409             }));
56410         }
56411     },
56412
56413     handleHdCtx : function(g, index, e){
56414         e.stopEvent();
56415         var hd = this.getHeaderCell(index);
56416         this.hdCtxIndex = index;
56417         var ms = this.hmenu.items, cm = this.cm;
56418         ms.get("asc").setDisabled(!cm.isSortable(index));
56419         ms.get("desc").setDisabled(!cm.isSortable(index));
56420         if(this.grid.enableColLock !== false){
56421             ms.get("lock").setDisabled(cm.isLocked(index));
56422             ms.get("unlock").setDisabled(!cm.isLocked(index));
56423         }
56424         this.hmenu.show(hd, "tl-bl");
56425     },
56426
56427     handleHdOver : function(e){
56428         var hd = this.findHeaderCell(e.getTarget());
56429         if(hd && !this.headersDisabled){
56430             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56431                this.fly(hd).addClass("x-grid-hd-over");
56432             }
56433         }
56434     },
56435
56436     handleHdOut : function(e){
56437         var hd = this.findHeaderCell(e.getTarget());
56438         if(hd){
56439             this.fly(hd).removeClass("x-grid-hd-over");
56440         }
56441     },
56442
56443     handleSplitDblClick : function(e, t){
56444         var i = this.getCellIndex(t);
56445         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56446             this.autoSizeColumn(i, true);
56447             this.layout();
56448         }
56449     },
56450
56451     render : function(){
56452
56453         var cm = this.cm;
56454         var colCount = cm.getColumnCount();
56455
56456         if(this.grid.monitorWindowResize === true){
56457             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56458         }
56459         var header = this.renderHeaders();
56460         var body = this.templates.body.apply({rows:""});
56461         var html = this.templates.master.apply({
56462             lockedBody: body,
56463             body: body,
56464             lockedHeader: header[0],
56465             header: header[1]
56466         });
56467
56468         //this.updateColumns();
56469
56470         this.grid.getGridEl().dom.innerHTML = html;
56471
56472         this.initElements();
56473         
56474         // a kludge to fix the random scolling effect in webkit
56475         this.el.on("scroll", function() {
56476             this.el.dom.scrollTop=0; // hopefully not recursive..
56477         },this);
56478
56479         this.scroller.on("scroll", this.handleScroll, this);
56480         this.lockedBody.on("mousewheel", this.handleWheel, this);
56481         this.mainBody.on("mousewheel", this.handleWheel, this);
56482
56483         this.mainHd.on("mouseover", this.handleHdOver, this);
56484         this.mainHd.on("mouseout", this.handleHdOut, this);
56485         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56486                 {delegate: "."+this.splitClass});
56487
56488         this.lockedHd.on("mouseover", this.handleHdOver, this);
56489         this.lockedHd.on("mouseout", this.handleHdOut, this);
56490         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56491                 {delegate: "."+this.splitClass});
56492
56493         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56494             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56495         }
56496
56497         this.updateSplitters();
56498
56499         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56500             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56501             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56502         }
56503
56504         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56505             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56506             this.hmenu.add(
56507                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56508                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56509             );
56510             if(this.grid.enableColLock !== false){
56511                 this.hmenu.add('-',
56512                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56513                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56514                 );
56515             }
56516             if (Roo.isTouch) {
56517                  this.hmenu.add('-',
56518                     {id:"wider", text: this.columnsWiderText},
56519                     {id:"narrow", text: this.columnsNarrowText }
56520                 );
56521                 
56522                  
56523             }
56524             
56525             if(this.grid.enableColumnHide !== false){
56526
56527                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56528                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56529                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56530
56531                 this.hmenu.add('-',
56532                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56533                 );
56534             }
56535             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56536
56537             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56538         }
56539
56540         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56541             this.dd = new Roo.grid.GridDragZone(this.grid, {
56542                 ddGroup : this.grid.ddGroup || 'GridDD'
56543             });
56544             
56545         }
56546
56547         /*
56548         for(var i = 0; i < colCount; i++){
56549             if(cm.isHidden(i)){
56550                 this.hideColumn(i);
56551             }
56552             if(cm.config[i].align){
56553                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56554                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56555             }
56556         }*/
56557         
56558         this.updateHeaderSortState();
56559
56560         this.beforeInitialResize();
56561         this.layout(true);
56562
56563         // two part rendering gives faster view to the user
56564         this.renderPhase2.defer(1, this);
56565     },
56566
56567     renderPhase2 : function(){
56568         // render the rows now
56569         this.refresh();
56570         if(this.grid.autoSizeColumns){
56571             this.autoSizeColumns();
56572         }
56573     },
56574
56575     beforeInitialResize : function(){
56576
56577     },
56578
56579     onColumnSplitterMoved : function(i, w){
56580         this.userResized = true;
56581         var cm = this.grid.colModel;
56582         cm.setColumnWidth(i, w, true);
56583         var cid = cm.getColumnId(i);
56584         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56585         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56586         this.updateSplitters();
56587         this.layout();
56588         this.grid.fireEvent("columnresize", i, w);
56589     },
56590
56591     syncRowHeights : function(startIndex, endIndex){
56592         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56593             startIndex = startIndex || 0;
56594             var mrows = this.getBodyTable().rows;
56595             var lrows = this.getLockedTable().rows;
56596             var len = mrows.length-1;
56597             endIndex = Math.min(endIndex || len, len);
56598             for(var i = startIndex; i <= endIndex; i++){
56599                 var m = mrows[i], l = lrows[i];
56600                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56601                 m.style.height = l.style.height = h + "px";
56602             }
56603         }
56604     },
56605
56606     layout : function(initialRender, is2ndPass){
56607         var g = this.grid;
56608         var auto = g.autoHeight;
56609         var scrollOffset = 16;
56610         var c = g.getGridEl(), cm = this.cm,
56611                 expandCol = g.autoExpandColumn,
56612                 gv = this;
56613         //c.beginMeasure();
56614
56615         if(!c.dom.offsetWidth){ // display:none?
56616             if(initialRender){
56617                 this.lockedWrap.show();
56618                 this.mainWrap.show();
56619             }
56620             return;
56621         }
56622
56623         var hasLock = this.cm.isLocked(0);
56624
56625         var tbh = this.headerPanel.getHeight();
56626         var bbh = this.footerPanel.getHeight();
56627
56628         if(auto){
56629             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56630             var newHeight = ch + c.getBorderWidth("tb");
56631             if(g.maxHeight){
56632                 newHeight = Math.min(g.maxHeight, newHeight);
56633             }
56634             c.setHeight(newHeight);
56635         }
56636
56637         if(g.autoWidth){
56638             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56639         }
56640
56641         var s = this.scroller;
56642
56643         var csize = c.getSize(true);
56644
56645         this.el.setSize(csize.width, csize.height);
56646
56647         this.headerPanel.setWidth(csize.width);
56648         this.footerPanel.setWidth(csize.width);
56649
56650         var hdHeight = this.mainHd.getHeight();
56651         var vw = csize.width;
56652         var vh = csize.height - (tbh + bbh);
56653
56654         s.setSize(vw, vh);
56655
56656         var bt = this.getBodyTable();
56657         
56658         if(cm.getLockedCount() == cm.config.length){
56659             bt = this.getLockedTable();
56660         }
56661         
56662         var ltWidth = hasLock ?
56663                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56664
56665         var scrollHeight = bt.offsetHeight;
56666         var scrollWidth = ltWidth + bt.offsetWidth;
56667         var vscroll = false, hscroll = false;
56668
56669         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56670
56671         var lw = this.lockedWrap, mw = this.mainWrap;
56672         var lb = this.lockedBody, mb = this.mainBody;
56673
56674         setTimeout(function(){
56675             var t = s.dom.offsetTop;
56676             var w = s.dom.clientWidth,
56677                 h = s.dom.clientHeight;
56678
56679             lw.setTop(t);
56680             lw.setSize(ltWidth, h);
56681
56682             mw.setLeftTop(ltWidth, t);
56683             mw.setSize(w-ltWidth, h);
56684
56685             lb.setHeight(h-hdHeight);
56686             mb.setHeight(h-hdHeight);
56687
56688             if(is2ndPass !== true && !gv.userResized && expandCol){
56689                 // high speed resize without full column calculation
56690                 
56691                 var ci = cm.getIndexById(expandCol);
56692                 if (ci < 0) {
56693                     ci = cm.findColumnIndex(expandCol);
56694                 }
56695                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56696                 var expandId = cm.getColumnId(ci);
56697                 var  tw = cm.getTotalWidth(false);
56698                 var currentWidth = cm.getColumnWidth(ci);
56699                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56700                 if(currentWidth != cw){
56701                     cm.setColumnWidth(ci, cw, true);
56702                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56703                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56704                     gv.updateSplitters();
56705                     gv.layout(false, true);
56706                 }
56707             }
56708
56709             if(initialRender){
56710                 lw.show();
56711                 mw.show();
56712             }
56713             //c.endMeasure();
56714         }, 10);
56715     },
56716
56717     onWindowResize : function(){
56718         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56719             return;
56720         }
56721         this.layout();
56722     },
56723
56724     appendFooter : function(parentEl){
56725         return null;
56726     },
56727
56728     sortAscText : "Sort Ascending",
56729     sortDescText : "Sort Descending",
56730     lockText : "Lock Column",
56731     unlockText : "Unlock Column",
56732     columnsText : "Columns",
56733  
56734     columnsWiderText : "Wider",
56735     columnsNarrowText : "Thinner"
56736 });
56737
56738
56739 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56740     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56741     this.proxy.el.addClass('x-grid3-col-dd');
56742 };
56743
56744 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56745     handleMouseDown : function(e){
56746
56747     },
56748
56749     callHandleMouseDown : function(e){
56750         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56751     }
56752 });
56753 /*
56754  * Based on:
56755  * Ext JS Library 1.1.1
56756  * Copyright(c) 2006-2007, Ext JS, LLC.
56757  *
56758  * Originally Released Under LGPL - original licence link has changed is not relivant.
56759  *
56760  * Fork - LGPL
56761  * <script type="text/javascript">
56762  */
56763  
56764 // private
56765 // This is a support class used internally by the Grid components
56766 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56767     this.grid = grid;
56768     this.view = grid.getView();
56769     this.proxy = this.view.resizeProxy;
56770     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56771         "gridSplitters" + this.grid.getGridEl().id, {
56772         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56773     });
56774     this.setHandleElId(Roo.id(hd));
56775     this.setOuterHandleElId(Roo.id(hd2));
56776     this.scroll = false;
56777 };
56778 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56779     fly: Roo.Element.fly,
56780
56781     b4StartDrag : function(x, y){
56782         this.view.headersDisabled = true;
56783         this.proxy.setHeight(this.view.mainWrap.getHeight());
56784         var w = this.cm.getColumnWidth(this.cellIndex);
56785         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56786         this.resetConstraints();
56787         this.setXConstraint(minw, 1000);
56788         this.setYConstraint(0, 0);
56789         this.minX = x - minw;
56790         this.maxX = x + 1000;
56791         this.startPos = x;
56792         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56793     },
56794
56795
56796     handleMouseDown : function(e){
56797         ev = Roo.EventObject.setEvent(e);
56798         var t = this.fly(ev.getTarget());
56799         if(t.hasClass("x-grid-split")){
56800             this.cellIndex = this.view.getCellIndex(t.dom);
56801             this.split = t.dom;
56802             this.cm = this.grid.colModel;
56803             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56804                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56805             }
56806         }
56807     },
56808
56809     endDrag : function(e){
56810         this.view.headersDisabled = false;
56811         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56812         var diff = endX - this.startPos;
56813         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56814     },
56815
56816     autoOffset : function(){
56817         this.setDelta(0,0);
56818     }
56819 });/*
56820  * Based on:
56821  * Ext JS Library 1.1.1
56822  * Copyright(c) 2006-2007, Ext JS, LLC.
56823  *
56824  * Originally Released Under LGPL - original licence link has changed is not relivant.
56825  *
56826  * Fork - LGPL
56827  * <script type="text/javascript">
56828  */
56829  
56830 // private
56831 // This is a support class used internally by the Grid components
56832 Roo.grid.GridDragZone = function(grid, config){
56833     this.view = grid.getView();
56834     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56835     if(this.view.lockedBody){
56836         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56837         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56838     }
56839     this.scroll = false;
56840     this.grid = grid;
56841     this.ddel = document.createElement('div');
56842     this.ddel.className = 'x-grid-dd-wrap';
56843 };
56844
56845 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56846     ddGroup : "GridDD",
56847
56848     getDragData : function(e){
56849         var t = Roo.lib.Event.getTarget(e);
56850         var rowIndex = this.view.findRowIndex(t);
56851         var sm = this.grid.selModel;
56852             
56853         //Roo.log(rowIndex);
56854         
56855         if (sm.getSelectedCell) {
56856             // cell selection..
56857             if (!sm.getSelectedCell()) {
56858                 return false;
56859             }
56860             if (rowIndex != sm.getSelectedCell()[0]) {
56861                 return false;
56862             }
56863         
56864         }
56865         
56866         if(rowIndex !== false){
56867             
56868             // if editorgrid.. 
56869             
56870             
56871             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56872                
56873             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56874               //  
56875             //}
56876             if (e.hasModifier()){
56877                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56878             }
56879             
56880             Roo.log("getDragData");
56881             
56882             return {
56883                 grid: this.grid,
56884                 ddel: this.ddel,
56885                 rowIndex: rowIndex,
56886                 selections:sm.getSelections ? sm.getSelections() : (
56887                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56888                 )
56889             };
56890         }
56891         return false;
56892     },
56893
56894     onInitDrag : function(e){
56895         var data = this.dragData;
56896         this.ddel.innerHTML = this.grid.getDragDropText();
56897         this.proxy.update(this.ddel);
56898         // fire start drag?
56899     },
56900
56901     afterRepair : function(){
56902         this.dragging = false;
56903     },
56904
56905     getRepairXY : function(e, data){
56906         return false;
56907     },
56908
56909     onEndDrag : function(data, e){
56910         // fire end drag?
56911     },
56912
56913     onValidDrop : function(dd, e, id){
56914         // fire drag drop?
56915         this.hideProxy();
56916     },
56917
56918     beforeInvalidDrop : function(e, id){
56919
56920     }
56921 });/*
56922  * Based on:
56923  * Ext JS Library 1.1.1
56924  * Copyright(c) 2006-2007, Ext JS, LLC.
56925  *
56926  * Originally Released Under LGPL - original licence link has changed is not relivant.
56927  *
56928  * Fork - LGPL
56929  * <script type="text/javascript">
56930  */
56931  
56932
56933 /**
56934  * @class Roo.grid.ColumnModel
56935  * @extends Roo.util.Observable
56936  * This is the default implementation of a ColumnModel used by the Grid. It defines
56937  * the columns in the grid.
56938  * <br>Usage:<br>
56939  <pre><code>
56940  var colModel = new Roo.grid.ColumnModel([
56941         {header: "Ticker", width: 60, sortable: true, locked: true},
56942         {header: "Company Name", width: 150, sortable: true},
56943         {header: "Market Cap.", width: 100, sortable: true},
56944         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56945         {header: "Employees", width: 100, sortable: true, resizable: false}
56946  ]);
56947  </code></pre>
56948  * <p>
56949  
56950  * The config options listed for this class are options which may appear in each
56951  * individual column definition.
56952  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56953  * @constructor
56954  * @param {Object} config An Array of column config objects. See this class's
56955  * config objects for details.
56956 */
56957 Roo.grid.ColumnModel = function(config){
56958         /**
56959      * The config passed into the constructor
56960      */
56961     this.config = config;
56962     this.lookup = {};
56963
56964     // if no id, create one
56965     // if the column does not have a dataIndex mapping,
56966     // map it to the order it is in the config
56967     for(var i = 0, len = config.length; i < len; i++){
56968         var c = config[i];
56969         if(typeof c.dataIndex == "undefined"){
56970             c.dataIndex = i;
56971         }
56972         if(typeof c.renderer == "string"){
56973             c.renderer = Roo.util.Format[c.renderer];
56974         }
56975         if(typeof c.id == "undefined"){
56976             c.id = Roo.id();
56977         }
56978         if(c.editor && c.editor.xtype){
56979             c.editor  = Roo.factory(c.editor, Roo.grid);
56980         }
56981         if(c.editor && c.editor.isFormField){
56982             c.editor = new Roo.grid.GridEditor(c.editor);
56983         }
56984         this.lookup[c.id] = c;
56985     }
56986
56987     /**
56988      * The width of columns which have no width specified (defaults to 100)
56989      * @type Number
56990      */
56991     this.defaultWidth = 100;
56992
56993     /**
56994      * Default sortable of columns which have no sortable specified (defaults to false)
56995      * @type Boolean
56996      */
56997     this.defaultSortable = false;
56998
56999     this.addEvents({
57000         /**
57001              * @event widthchange
57002              * Fires when the width of a column changes.
57003              * @param {ColumnModel} this
57004              * @param {Number} columnIndex The column index
57005              * @param {Number} newWidth The new width
57006              */
57007             "widthchange": true,
57008         /**
57009              * @event headerchange
57010              * Fires when the text of a header changes.
57011              * @param {ColumnModel} this
57012              * @param {Number} columnIndex The column index
57013              * @param {Number} newText The new header text
57014              */
57015             "headerchange": true,
57016         /**
57017              * @event hiddenchange
57018              * Fires when a column is hidden or "unhidden".
57019              * @param {ColumnModel} this
57020              * @param {Number} columnIndex The column index
57021              * @param {Boolean} hidden true if hidden, false otherwise
57022              */
57023             "hiddenchange": true,
57024             /**
57025          * @event columnmoved
57026          * Fires when a column is moved.
57027          * @param {ColumnModel} this
57028          * @param {Number} oldIndex
57029          * @param {Number} newIndex
57030          */
57031         "columnmoved" : true,
57032         /**
57033          * @event columlockchange
57034          * Fires when a column's locked state is changed
57035          * @param {ColumnModel} this
57036          * @param {Number} colIndex
57037          * @param {Boolean} locked true if locked
57038          */
57039         "columnlockchange" : true
57040     });
57041     Roo.grid.ColumnModel.superclass.constructor.call(this);
57042 };
57043 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57044     /**
57045      * @cfg {String} header The header text to display in the Grid view.
57046      */
57047     /**
57048      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57049      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57050      * specified, the column's index is used as an index into the Record's data Array.
57051      */
57052     /**
57053      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57054      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57055      */
57056     /**
57057      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57058      * Defaults to the value of the {@link #defaultSortable} property.
57059      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57060      */
57061     /**
57062      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57063      */
57064     /**
57065      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57066      */
57067     /**
57068      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57069      */
57070     /**
57071      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57072      */
57073     /**
57074      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57075      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57076      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57077      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57078      */
57079        /**
57080      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57081      */
57082     /**
57083      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57084      */
57085     /**
57086      * @cfg {String} cursor (Optional)
57087      */
57088     /**
57089      * @cfg {String} tooltip (Optional)
57090      */
57091     /**
57092      * @cfg {Number} xs (Optional)
57093      */
57094     /**
57095      * @cfg {Number} sm (Optional)
57096      */
57097     /**
57098      * @cfg {Number} md (Optional)
57099      */
57100     /**
57101      * @cfg {Number} lg (Optional)
57102      */
57103     /**
57104      * Returns the id of the column at the specified index.
57105      * @param {Number} index The column index
57106      * @return {String} the id
57107      */
57108     getColumnId : function(index){
57109         return this.config[index].id;
57110     },
57111
57112     /**
57113      * Returns the column for a specified id.
57114      * @param {String} id The column id
57115      * @return {Object} the column
57116      */
57117     getColumnById : function(id){
57118         return this.lookup[id];
57119     },
57120
57121     
57122     /**
57123      * Returns the column for a specified dataIndex.
57124      * @param {String} dataIndex The column dataIndex
57125      * @return {Object|Boolean} the column or false if not found
57126      */
57127     getColumnByDataIndex: function(dataIndex){
57128         var index = this.findColumnIndex(dataIndex);
57129         return index > -1 ? this.config[index] : false;
57130     },
57131     
57132     /**
57133      * Returns the index for a specified column id.
57134      * @param {String} id The column id
57135      * @return {Number} the index, or -1 if not found
57136      */
57137     getIndexById : function(id){
57138         for(var i = 0, len = this.config.length; i < len; i++){
57139             if(this.config[i].id == id){
57140                 return i;
57141             }
57142         }
57143         return -1;
57144     },
57145     
57146     /**
57147      * Returns the index for a specified column dataIndex.
57148      * @param {String} dataIndex The column dataIndex
57149      * @return {Number} the index, or -1 if not found
57150      */
57151     
57152     findColumnIndex : function(dataIndex){
57153         for(var i = 0, len = this.config.length; i < len; i++){
57154             if(this.config[i].dataIndex == dataIndex){
57155                 return i;
57156             }
57157         }
57158         return -1;
57159     },
57160     
57161     
57162     moveColumn : function(oldIndex, newIndex){
57163         var c = this.config[oldIndex];
57164         this.config.splice(oldIndex, 1);
57165         this.config.splice(newIndex, 0, c);
57166         this.dataMap = null;
57167         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57168     },
57169
57170     isLocked : function(colIndex){
57171         return this.config[colIndex].locked === true;
57172     },
57173
57174     setLocked : function(colIndex, value, suppressEvent){
57175         if(this.isLocked(colIndex) == value){
57176             return;
57177         }
57178         this.config[colIndex].locked = value;
57179         if(!suppressEvent){
57180             this.fireEvent("columnlockchange", this, colIndex, value);
57181         }
57182     },
57183
57184     getTotalLockedWidth : function(){
57185         var totalWidth = 0;
57186         for(var i = 0; i < this.config.length; i++){
57187             if(this.isLocked(i) && !this.isHidden(i)){
57188                 this.totalWidth += this.getColumnWidth(i);
57189             }
57190         }
57191         return totalWidth;
57192     },
57193
57194     getLockedCount : function(){
57195         for(var i = 0, len = this.config.length; i < len; i++){
57196             if(!this.isLocked(i)){
57197                 return i;
57198             }
57199         }
57200         
57201         return this.config.length;
57202     },
57203
57204     /**
57205      * Returns the number of columns.
57206      * @return {Number}
57207      */
57208     getColumnCount : function(visibleOnly){
57209         if(visibleOnly === true){
57210             var c = 0;
57211             for(var i = 0, len = this.config.length; i < len; i++){
57212                 if(!this.isHidden(i)){
57213                     c++;
57214                 }
57215             }
57216             return c;
57217         }
57218         return this.config.length;
57219     },
57220
57221     /**
57222      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57223      * @param {Function} fn
57224      * @param {Object} scope (optional)
57225      * @return {Array} result
57226      */
57227     getColumnsBy : function(fn, scope){
57228         var r = [];
57229         for(var i = 0, len = this.config.length; i < len; i++){
57230             var c = this.config[i];
57231             if(fn.call(scope||this, c, i) === true){
57232                 r[r.length] = c;
57233             }
57234         }
57235         return r;
57236     },
57237
57238     /**
57239      * Returns true if the specified column is sortable.
57240      * @param {Number} col The column index
57241      * @return {Boolean}
57242      */
57243     isSortable : function(col){
57244         if(typeof this.config[col].sortable == "undefined"){
57245             return this.defaultSortable;
57246         }
57247         return this.config[col].sortable;
57248     },
57249
57250     /**
57251      * Returns the rendering (formatting) function defined for the column.
57252      * @param {Number} col The column index.
57253      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57254      */
57255     getRenderer : function(col){
57256         if(!this.config[col].renderer){
57257             return Roo.grid.ColumnModel.defaultRenderer;
57258         }
57259         return this.config[col].renderer;
57260     },
57261
57262     /**
57263      * Sets the rendering (formatting) function for a column.
57264      * @param {Number} col The column index
57265      * @param {Function} fn The function to use to process the cell's raw data
57266      * to return HTML markup for the grid view. The render function is called with
57267      * the following parameters:<ul>
57268      * <li>Data value.</li>
57269      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57270      * <li>css A CSS style string to apply to the table cell.</li>
57271      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57272      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57273      * <li>Row index</li>
57274      * <li>Column index</li>
57275      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57276      */
57277     setRenderer : function(col, fn){
57278         this.config[col].renderer = fn;
57279     },
57280
57281     /**
57282      * Returns the width for the specified column.
57283      * @param {Number} col The column index
57284      * @return {Number}
57285      */
57286     getColumnWidth : function(col){
57287         return this.config[col].width * 1 || this.defaultWidth;
57288     },
57289
57290     /**
57291      * Sets the width for a column.
57292      * @param {Number} col The column index
57293      * @param {Number} width The new width
57294      */
57295     setColumnWidth : function(col, width, suppressEvent){
57296         this.config[col].width = width;
57297         this.totalWidth = null;
57298         if(!suppressEvent){
57299              this.fireEvent("widthchange", this, col, width);
57300         }
57301     },
57302
57303     /**
57304      * Returns the total width of all columns.
57305      * @param {Boolean} includeHidden True to include hidden column widths
57306      * @return {Number}
57307      */
57308     getTotalWidth : function(includeHidden){
57309         if(!this.totalWidth){
57310             this.totalWidth = 0;
57311             for(var i = 0, len = this.config.length; i < len; i++){
57312                 if(includeHidden || !this.isHidden(i)){
57313                     this.totalWidth += this.getColumnWidth(i);
57314                 }
57315             }
57316         }
57317         return this.totalWidth;
57318     },
57319
57320     /**
57321      * Returns the header for the specified column.
57322      * @param {Number} col The column index
57323      * @return {String}
57324      */
57325     getColumnHeader : function(col){
57326         return this.config[col].header;
57327     },
57328
57329     /**
57330      * Sets the header for a column.
57331      * @param {Number} col The column index
57332      * @param {String} header The new header
57333      */
57334     setColumnHeader : function(col, header){
57335         this.config[col].header = header;
57336         this.fireEvent("headerchange", this, col, header);
57337     },
57338
57339     /**
57340      * Returns the tooltip for the specified column.
57341      * @param {Number} col The column index
57342      * @return {String}
57343      */
57344     getColumnTooltip : function(col){
57345             return this.config[col].tooltip;
57346     },
57347     /**
57348      * Sets the tooltip for a column.
57349      * @param {Number} col The column index
57350      * @param {String} tooltip The new tooltip
57351      */
57352     setColumnTooltip : function(col, tooltip){
57353             this.config[col].tooltip = tooltip;
57354     },
57355
57356     /**
57357      * Returns the dataIndex for the specified column.
57358      * @param {Number} col The column index
57359      * @return {Number}
57360      */
57361     getDataIndex : function(col){
57362         return this.config[col].dataIndex;
57363     },
57364
57365     /**
57366      * Sets the dataIndex for a column.
57367      * @param {Number} col The column index
57368      * @param {Number} dataIndex The new dataIndex
57369      */
57370     setDataIndex : function(col, dataIndex){
57371         this.config[col].dataIndex = dataIndex;
57372     },
57373
57374     
57375     
57376     /**
57377      * Returns true if the cell is editable.
57378      * @param {Number} colIndex The column index
57379      * @param {Number} rowIndex The row index - this is nto actually used..?
57380      * @return {Boolean}
57381      */
57382     isCellEditable : function(colIndex, rowIndex){
57383         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57384     },
57385
57386     /**
57387      * Returns the editor defined for the cell/column.
57388      * return false or null to disable editing.
57389      * @param {Number} colIndex The column index
57390      * @param {Number} rowIndex The row index
57391      * @return {Object}
57392      */
57393     getCellEditor : function(colIndex, rowIndex){
57394         return this.config[colIndex].editor;
57395     },
57396
57397     /**
57398      * Sets if a column is editable.
57399      * @param {Number} col The column index
57400      * @param {Boolean} editable True if the column is editable
57401      */
57402     setEditable : function(col, editable){
57403         this.config[col].editable = editable;
57404     },
57405
57406
57407     /**
57408      * Returns true if the column is hidden.
57409      * @param {Number} colIndex The column index
57410      * @return {Boolean}
57411      */
57412     isHidden : function(colIndex){
57413         return this.config[colIndex].hidden;
57414     },
57415
57416
57417     /**
57418      * Returns true if the column width cannot be changed
57419      */
57420     isFixed : function(colIndex){
57421         return this.config[colIndex].fixed;
57422     },
57423
57424     /**
57425      * Returns true if the column can be resized
57426      * @return {Boolean}
57427      */
57428     isResizable : function(colIndex){
57429         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57430     },
57431     /**
57432      * Sets if a column is hidden.
57433      * @param {Number} colIndex The column index
57434      * @param {Boolean} hidden True if the column is hidden
57435      */
57436     setHidden : function(colIndex, hidden){
57437         this.config[colIndex].hidden = hidden;
57438         this.totalWidth = null;
57439         this.fireEvent("hiddenchange", this, colIndex, hidden);
57440     },
57441
57442     /**
57443      * Sets the editor for a column.
57444      * @param {Number} col The column index
57445      * @param {Object} editor The editor object
57446      */
57447     setEditor : function(col, editor){
57448         this.config[col].editor = editor;
57449     }
57450 });
57451
57452 Roo.grid.ColumnModel.defaultRenderer = function(value)
57453 {
57454     if(typeof value == "object") {
57455         return value;
57456     }
57457         if(typeof value == "string" && value.length < 1){
57458             return "&#160;";
57459         }
57460     
57461         return String.format("{0}", value);
57462 };
57463
57464 // Alias for backwards compatibility
57465 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57466 /*
57467  * Based on:
57468  * Ext JS Library 1.1.1
57469  * Copyright(c) 2006-2007, Ext JS, LLC.
57470  *
57471  * Originally Released Under LGPL - original licence link has changed is not relivant.
57472  *
57473  * Fork - LGPL
57474  * <script type="text/javascript">
57475  */
57476
57477 /**
57478  * @class Roo.grid.AbstractSelectionModel
57479  * @extends Roo.util.Observable
57480  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57481  * implemented by descendant classes.  This class should not be directly instantiated.
57482  * @constructor
57483  */
57484 Roo.grid.AbstractSelectionModel = function(){
57485     this.locked = false;
57486     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57487 };
57488
57489 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57490     /** @ignore Called by the grid automatically. Do not call directly. */
57491     init : function(grid){
57492         this.grid = grid;
57493         this.initEvents();
57494     },
57495
57496     /**
57497      * Locks the selections.
57498      */
57499     lock : function(){
57500         this.locked = true;
57501     },
57502
57503     /**
57504      * Unlocks the selections.
57505      */
57506     unlock : function(){
57507         this.locked = false;
57508     },
57509
57510     /**
57511      * Returns true if the selections are locked.
57512      * @return {Boolean}
57513      */
57514     isLocked : function(){
57515         return this.locked;
57516     }
57517 });/*
57518  * Based on:
57519  * Ext JS Library 1.1.1
57520  * Copyright(c) 2006-2007, Ext JS, LLC.
57521  *
57522  * Originally Released Under LGPL - original licence link has changed is not relivant.
57523  *
57524  * Fork - LGPL
57525  * <script type="text/javascript">
57526  */
57527 /**
57528  * @extends Roo.grid.AbstractSelectionModel
57529  * @class Roo.grid.RowSelectionModel
57530  * The default SelectionModel used by {@link Roo.grid.Grid}.
57531  * It supports multiple selections and keyboard selection/navigation. 
57532  * @constructor
57533  * @param {Object} config
57534  */
57535 Roo.grid.RowSelectionModel = function(config){
57536     Roo.apply(this, config);
57537     this.selections = new Roo.util.MixedCollection(false, function(o){
57538         return o.id;
57539     });
57540
57541     this.last = false;
57542     this.lastActive = false;
57543
57544     this.addEvents({
57545         /**
57546              * @event selectionchange
57547              * Fires when the selection changes
57548              * @param {SelectionModel} this
57549              */
57550             "selectionchange" : true,
57551         /**
57552              * @event afterselectionchange
57553              * Fires after the selection changes (eg. by key press or clicking)
57554              * @param {SelectionModel} this
57555              */
57556             "afterselectionchange" : true,
57557         /**
57558              * @event beforerowselect
57559              * Fires when a row is selected being selected, return false to cancel.
57560              * @param {SelectionModel} this
57561              * @param {Number} rowIndex The selected index
57562              * @param {Boolean} keepExisting False if other selections will be cleared
57563              */
57564             "beforerowselect" : true,
57565         /**
57566              * @event rowselect
57567              * Fires when a row is selected.
57568              * @param {SelectionModel} this
57569              * @param {Number} rowIndex The selected index
57570              * @param {Roo.data.Record} r The record
57571              */
57572             "rowselect" : true,
57573         /**
57574              * @event rowdeselect
57575              * Fires when a row is deselected.
57576              * @param {SelectionModel} this
57577              * @param {Number} rowIndex The selected index
57578              */
57579         "rowdeselect" : true
57580     });
57581     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57582     this.locked = false;
57583 };
57584
57585 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57586     /**
57587      * @cfg {Boolean} singleSelect
57588      * True to allow selection of only one row at a time (defaults to false)
57589      */
57590     singleSelect : false,
57591
57592     // private
57593     initEvents : function(){
57594
57595         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57596             this.grid.on("mousedown", this.handleMouseDown, this);
57597         }else{ // allow click to work like normal
57598             this.grid.on("rowclick", this.handleDragableRowClick, this);
57599         }
57600
57601         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57602             "up" : function(e){
57603                 if(!e.shiftKey){
57604                     this.selectPrevious(e.shiftKey);
57605                 }else if(this.last !== false && this.lastActive !== false){
57606                     var last = this.last;
57607                     this.selectRange(this.last,  this.lastActive-1);
57608                     this.grid.getView().focusRow(this.lastActive);
57609                     if(last !== false){
57610                         this.last = last;
57611                     }
57612                 }else{
57613                     this.selectFirstRow();
57614                 }
57615                 this.fireEvent("afterselectionchange", this);
57616             },
57617             "down" : function(e){
57618                 if(!e.shiftKey){
57619                     this.selectNext(e.shiftKey);
57620                 }else if(this.last !== false && this.lastActive !== false){
57621                     var last = this.last;
57622                     this.selectRange(this.last,  this.lastActive+1);
57623                     this.grid.getView().focusRow(this.lastActive);
57624                     if(last !== false){
57625                         this.last = last;
57626                     }
57627                 }else{
57628                     this.selectFirstRow();
57629                 }
57630                 this.fireEvent("afterselectionchange", this);
57631             },
57632             scope: this
57633         });
57634
57635         var view = this.grid.view;
57636         view.on("refresh", this.onRefresh, this);
57637         view.on("rowupdated", this.onRowUpdated, this);
57638         view.on("rowremoved", this.onRemove, this);
57639     },
57640
57641     // private
57642     onRefresh : function(){
57643         var ds = this.grid.dataSource, i, v = this.grid.view;
57644         var s = this.selections;
57645         s.each(function(r){
57646             if((i = ds.indexOfId(r.id)) != -1){
57647                 v.onRowSelect(i);
57648                 s.add(ds.getAt(i)); // updating the selection relate data
57649             }else{
57650                 s.remove(r);
57651             }
57652         });
57653     },
57654
57655     // private
57656     onRemove : function(v, index, r){
57657         this.selections.remove(r);
57658     },
57659
57660     // private
57661     onRowUpdated : function(v, index, r){
57662         if(this.isSelected(r)){
57663             v.onRowSelect(index);
57664         }
57665     },
57666
57667     /**
57668      * Select records.
57669      * @param {Array} records The records to select
57670      * @param {Boolean} keepExisting (optional) True to keep existing selections
57671      */
57672     selectRecords : function(records, keepExisting){
57673         if(!keepExisting){
57674             this.clearSelections();
57675         }
57676         var ds = this.grid.dataSource;
57677         for(var i = 0, len = records.length; i < len; i++){
57678             this.selectRow(ds.indexOf(records[i]), true);
57679         }
57680     },
57681
57682     /**
57683      * Gets the number of selected rows.
57684      * @return {Number}
57685      */
57686     getCount : function(){
57687         return this.selections.length;
57688     },
57689
57690     /**
57691      * Selects the first row in the grid.
57692      */
57693     selectFirstRow : function(){
57694         this.selectRow(0);
57695     },
57696
57697     /**
57698      * Select the last row.
57699      * @param {Boolean} keepExisting (optional) True to keep existing selections
57700      */
57701     selectLastRow : function(keepExisting){
57702         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57703     },
57704
57705     /**
57706      * Selects the row immediately following the last selected row.
57707      * @param {Boolean} keepExisting (optional) True to keep existing selections
57708      */
57709     selectNext : function(keepExisting){
57710         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57711             this.selectRow(this.last+1, keepExisting);
57712             this.grid.getView().focusRow(this.last);
57713         }
57714     },
57715
57716     /**
57717      * Selects the row that precedes the last selected row.
57718      * @param {Boolean} keepExisting (optional) True to keep existing selections
57719      */
57720     selectPrevious : function(keepExisting){
57721         if(this.last){
57722             this.selectRow(this.last-1, keepExisting);
57723             this.grid.getView().focusRow(this.last);
57724         }
57725     },
57726
57727     /**
57728      * Returns the selected records
57729      * @return {Array} Array of selected records
57730      */
57731     getSelections : function(){
57732         return [].concat(this.selections.items);
57733     },
57734
57735     /**
57736      * Returns the first selected record.
57737      * @return {Record}
57738      */
57739     getSelected : function(){
57740         return this.selections.itemAt(0);
57741     },
57742
57743
57744     /**
57745      * Clears all selections.
57746      */
57747     clearSelections : function(fast){
57748         if(this.locked) {
57749             return;
57750         }
57751         if(fast !== true){
57752             var ds = this.grid.dataSource;
57753             var s = this.selections;
57754             s.each(function(r){
57755                 this.deselectRow(ds.indexOfId(r.id));
57756             }, this);
57757             s.clear();
57758         }else{
57759             this.selections.clear();
57760         }
57761         this.last = false;
57762     },
57763
57764
57765     /**
57766      * Selects all rows.
57767      */
57768     selectAll : function(){
57769         if(this.locked) {
57770             return;
57771         }
57772         this.selections.clear();
57773         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57774             this.selectRow(i, true);
57775         }
57776     },
57777
57778     /**
57779      * Returns True if there is a selection.
57780      * @return {Boolean}
57781      */
57782     hasSelection : function(){
57783         return this.selections.length > 0;
57784     },
57785
57786     /**
57787      * Returns True if the specified row is selected.
57788      * @param {Number/Record} record The record or index of the record to check
57789      * @return {Boolean}
57790      */
57791     isSelected : function(index){
57792         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57793         return (r && this.selections.key(r.id) ? true : false);
57794     },
57795
57796     /**
57797      * Returns True if the specified record id is selected.
57798      * @param {String} id The id of record to check
57799      * @return {Boolean}
57800      */
57801     isIdSelected : function(id){
57802         return (this.selections.key(id) ? true : false);
57803     },
57804
57805     // private
57806     handleMouseDown : function(e, t){
57807         var view = this.grid.getView(), rowIndex;
57808         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57809             return;
57810         };
57811         if(e.shiftKey && this.last !== false){
57812             var last = this.last;
57813             this.selectRange(last, rowIndex, e.ctrlKey);
57814             this.last = last; // reset the last
57815             view.focusRow(rowIndex);
57816         }else{
57817             var isSelected = this.isSelected(rowIndex);
57818             if(e.button !== 0 && isSelected){
57819                 view.focusRow(rowIndex);
57820             }else if(e.ctrlKey && isSelected){
57821                 this.deselectRow(rowIndex);
57822             }else if(!isSelected){
57823                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57824                 view.focusRow(rowIndex);
57825             }
57826         }
57827         this.fireEvent("afterselectionchange", this);
57828     },
57829     // private
57830     handleDragableRowClick :  function(grid, rowIndex, e) 
57831     {
57832         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57833             this.selectRow(rowIndex, false);
57834             grid.view.focusRow(rowIndex);
57835              this.fireEvent("afterselectionchange", this);
57836         }
57837     },
57838     
57839     /**
57840      * Selects multiple rows.
57841      * @param {Array} rows Array of the indexes of the row to select
57842      * @param {Boolean} keepExisting (optional) True to keep existing selections
57843      */
57844     selectRows : function(rows, keepExisting){
57845         if(!keepExisting){
57846             this.clearSelections();
57847         }
57848         for(var i = 0, len = rows.length; i < len; i++){
57849             this.selectRow(rows[i], true);
57850         }
57851     },
57852
57853     /**
57854      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57855      * @param {Number} startRow The index of the first row in the range
57856      * @param {Number} endRow The index of the last row in the range
57857      * @param {Boolean} keepExisting (optional) True to retain existing selections
57858      */
57859     selectRange : function(startRow, endRow, keepExisting){
57860         if(this.locked) {
57861             return;
57862         }
57863         if(!keepExisting){
57864             this.clearSelections();
57865         }
57866         if(startRow <= endRow){
57867             for(var i = startRow; i <= endRow; i++){
57868                 this.selectRow(i, true);
57869             }
57870         }else{
57871             for(var i = startRow; i >= endRow; i--){
57872                 this.selectRow(i, true);
57873             }
57874         }
57875     },
57876
57877     /**
57878      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57879      * @param {Number} startRow The index of the first row in the range
57880      * @param {Number} endRow The index of the last row in the range
57881      */
57882     deselectRange : function(startRow, endRow, preventViewNotify){
57883         if(this.locked) {
57884             return;
57885         }
57886         for(var i = startRow; i <= endRow; i++){
57887             this.deselectRow(i, preventViewNotify);
57888         }
57889     },
57890
57891     /**
57892      * Selects a row.
57893      * @param {Number} row The index of the row to select
57894      * @param {Boolean} keepExisting (optional) True to keep existing selections
57895      */
57896     selectRow : function(index, keepExisting, preventViewNotify){
57897         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57898             return;
57899         }
57900         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57901             if(!keepExisting || this.singleSelect){
57902                 this.clearSelections();
57903             }
57904             var r = this.grid.dataSource.getAt(index);
57905             this.selections.add(r);
57906             this.last = this.lastActive = index;
57907             if(!preventViewNotify){
57908                 this.grid.getView().onRowSelect(index);
57909             }
57910             this.fireEvent("rowselect", this, index, r);
57911             this.fireEvent("selectionchange", this);
57912         }
57913     },
57914
57915     /**
57916      * Deselects a row.
57917      * @param {Number} row The index of the row to deselect
57918      */
57919     deselectRow : function(index, preventViewNotify){
57920         if(this.locked) {
57921             return;
57922         }
57923         if(this.last == index){
57924             this.last = false;
57925         }
57926         if(this.lastActive == index){
57927             this.lastActive = false;
57928         }
57929         var r = this.grid.dataSource.getAt(index);
57930         this.selections.remove(r);
57931         if(!preventViewNotify){
57932             this.grid.getView().onRowDeselect(index);
57933         }
57934         this.fireEvent("rowdeselect", this, index);
57935         this.fireEvent("selectionchange", this);
57936     },
57937
57938     // private
57939     restoreLast : function(){
57940         if(this._last){
57941             this.last = this._last;
57942         }
57943     },
57944
57945     // private
57946     acceptsNav : function(row, col, cm){
57947         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57948     },
57949
57950     // private
57951     onEditorKey : function(field, e){
57952         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57953         if(k == e.TAB){
57954             e.stopEvent();
57955             ed.completeEdit();
57956             if(e.shiftKey){
57957                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57958             }else{
57959                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57960             }
57961         }else if(k == e.ENTER && !e.ctrlKey){
57962             e.stopEvent();
57963             ed.completeEdit();
57964             if(e.shiftKey){
57965                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57966             }else{
57967                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57968             }
57969         }else if(k == e.ESC){
57970             ed.cancelEdit();
57971         }
57972         if(newCell){
57973             g.startEditing(newCell[0], newCell[1]);
57974         }
57975     }
57976 });/*
57977  * Based on:
57978  * Ext JS Library 1.1.1
57979  * Copyright(c) 2006-2007, Ext JS, LLC.
57980  *
57981  * Originally Released Under LGPL - original licence link has changed is not relivant.
57982  *
57983  * Fork - LGPL
57984  * <script type="text/javascript">
57985  */
57986 /**
57987  * @class Roo.grid.CellSelectionModel
57988  * @extends Roo.grid.AbstractSelectionModel
57989  * This class provides the basic implementation for cell selection in a grid.
57990  * @constructor
57991  * @param {Object} config The object containing the configuration of this model.
57992  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57993  */
57994 Roo.grid.CellSelectionModel = function(config){
57995     Roo.apply(this, config);
57996
57997     this.selection = null;
57998
57999     this.addEvents({
58000         /**
58001              * @event beforerowselect
58002              * Fires before a cell is selected.
58003              * @param {SelectionModel} this
58004              * @param {Number} rowIndex The selected row index
58005              * @param {Number} colIndex The selected cell index
58006              */
58007             "beforecellselect" : true,
58008         /**
58009              * @event cellselect
58010              * Fires when a cell is selected.
58011              * @param {SelectionModel} this
58012              * @param {Number} rowIndex The selected row index
58013              * @param {Number} colIndex The selected cell index
58014              */
58015             "cellselect" : true,
58016         /**
58017              * @event selectionchange
58018              * Fires when the active selection changes.
58019              * @param {SelectionModel} this
58020              * @param {Object} selection null for no selection or an object (o) with two properties
58021                 <ul>
58022                 <li>o.record: the record object for the row the selection is in</li>
58023                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58024                 </ul>
58025              */
58026             "selectionchange" : true,
58027         /**
58028              * @event tabend
58029              * Fires when the tab (or enter) was pressed on the last editable cell
58030              * You can use this to trigger add new row.
58031              * @param {SelectionModel} this
58032              */
58033             "tabend" : true,
58034          /**
58035              * @event beforeeditnext
58036              * Fires before the next editable sell is made active
58037              * You can use this to skip to another cell or fire the tabend
58038              *    if you set cell to false
58039              * @param {Object} eventdata object : { cell : [ row, col ] } 
58040              */
58041             "beforeeditnext" : true
58042     });
58043     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58044 };
58045
58046 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58047     
58048     enter_is_tab: false,
58049
58050     /** @ignore */
58051     initEvents : function(){
58052         this.grid.on("mousedown", this.handleMouseDown, this);
58053         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58054         var view = this.grid.view;
58055         view.on("refresh", this.onViewChange, this);
58056         view.on("rowupdated", this.onRowUpdated, this);
58057         view.on("beforerowremoved", this.clearSelections, this);
58058         view.on("beforerowsinserted", this.clearSelections, this);
58059         if(this.grid.isEditor){
58060             this.grid.on("beforeedit", this.beforeEdit,  this);
58061         }
58062     },
58063
58064         //private
58065     beforeEdit : function(e){
58066         this.select(e.row, e.column, false, true, e.record);
58067     },
58068
58069         //private
58070     onRowUpdated : function(v, index, r){
58071         if(this.selection && this.selection.record == r){
58072             v.onCellSelect(index, this.selection.cell[1]);
58073         }
58074     },
58075
58076         //private
58077     onViewChange : function(){
58078         this.clearSelections(true);
58079     },
58080
58081         /**
58082          * Returns the currently selected cell,.
58083          * @return {Array} The selected cell (row, column) or null if none selected.
58084          */
58085     getSelectedCell : function(){
58086         return this.selection ? this.selection.cell : null;
58087     },
58088
58089     /**
58090      * Clears all selections.
58091      * @param {Boolean} true to prevent the gridview from being notified about the change.
58092      */
58093     clearSelections : function(preventNotify){
58094         var s = this.selection;
58095         if(s){
58096             if(preventNotify !== true){
58097                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58098             }
58099             this.selection = null;
58100             this.fireEvent("selectionchange", this, null);
58101         }
58102     },
58103
58104     /**
58105      * Returns true if there is a selection.
58106      * @return {Boolean}
58107      */
58108     hasSelection : function(){
58109         return this.selection ? true : false;
58110     },
58111
58112     /** @ignore */
58113     handleMouseDown : function(e, t){
58114         var v = this.grid.getView();
58115         if(this.isLocked()){
58116             return;
58117         };
58118         var row = v.findRowIndex(t);
58119         var cell = v.findCellIndex(t);
58120         if(row !== false && cell !== false){
58121             this.select(row, cell);
58122         }
58123     },
58124
58125     /**
58126      * Selects a cell.
58127      * @param {Number} rowIndex
58128      * @param {Number} collIndex
58129      */
58130     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58131         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58132             this.clearSelections();
58133             r = r || this.grid.dataSource.getAt(rowIndex);
58134             this.selection = {
58135                 record : r,
58136                 cell : [rowIndex, colIndex]
58137             };
58138             if(!preventViewNotify){
58139                 var v = this.grid.getView();
58140                 v.onCellSelect(rowIndex, colIndex);
58141                 if(preventFocus !== true){
58142                     v.focusCell(rowIndex, colIndex);
58143                 }
58144             }
58145             this.fireEvent("cellselect", this, rowIndex, colIndex);
58146             this.fireEvent("selectionchange", this, this.selection);
58147         }
58148     },
58149
58150         //private
58151     isSelectable : function(rowIndex, colIndex, cm){
58152         return !cm.isHidden(colIndex);
58153     },
58154
58155     /** @ignore */
58156     handleKeyDown : function(e){
58157         //Roo.log('Cell Sel Model handleKeyDown');
58158         if(!e.isNavKeyPress()){
58159             return;
58160         }
58161         var g = this.grid, s = this.selection;
58162         if(!s){
58163             e.stopEvent();
58164             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58165             if(cell){
58166                 this.select(cell[0], cell[1]);
58167             }
58168             return;
58169         }
58170         var sm = this;
58171         var walk = function(row, col, step){
58172             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58173         };
58174         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58175         var newCell;
58176
58177       
58178
58179         switch(k){
58180             case e.TAB:
58181                 // handled by onEditorKey
58182                 if (g.isEditor && g.editing) {
58183                     return;
58184                 }
58185                 if(e.shiftKey) {
58186                     newCell = walk(r, c-1, -1);
58187                 } else {
58188                     newCell = walk(r, c+1, 1);
58189                 }
58190                 break;
58191             
58192             case e.DOWN:
58193                newCell = walk(r+1, c, 1);
58194                 break;
58195             
58196             case e.UP:
58197                 newCell = walk(r-1, c, -1);
58198                 break;
58199             
58200             case e.RIGHT:
58201                 newCell = walk(r, c+1, 1);
58202                 break;
58203             
58204             case e.LEFT:
58205                 newCell = walk(r, c-1, -1);
58206                 break;
58207             
58208             case e.ENTER:
58209                 
58210                 if(g.isEditor && !g.editing){
58211                    g.startEditing(r, c);
58212                    e.stopEvent();
58213                    return;
58214                 }
58215                 
58216                 
58217              break;
58218         };
58219         if(newCell){
58220             this.select(newCell[0], newCell[1]);
58221             e.stopEvent();
58222             
58223         }
58224     },
58225
58226     acceptsNav : function(row, col, cm){
58227         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58228     },
58229     /**
58230      * Selects a cell.
58231      * @param {Number} field (not used) - as it's normally used as a listener
58232      * @param {Number} e - event - fake it by using
58233      *
58234      * var e = Roo.EventObjectImpl.prototype;
58235      * e.keyCode = e.TAB
58236      *
58237      * 
58238      */
58239     onEditorKey : function(field, e){
58240         
58241         var k = e.getKey(),
58242             newCell,
58243             g = this.grid,
58244             ed = g.activeEditor,
58245             forward = false;
58246         ///Roo.log('onEditorKey' + k);
58247         
58248         
58249         if (this.enter_is_tab && k == e.ENTER) {
58250             k = e.TAB;
58251         }
58252         
58253         if(k == e.TAB){
58254             if(e.shiftKey){
58255                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58256             }else{
58257                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58258                 forward = true;
58259             }
58260             
58261             e.stopEvent();
58262             
58263         } else if(k == e.ENTER &&  !e.ctrlKey){
58264             ed.completeEdit();
58265             e.stopEvent();
58266             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58267         
58268                 } else if(k == e.ESC){
58269             ed.cancelEdit();
58270         }
58271                 
58272         if (newCell) {
58273             var ecall = { cell : newCell, forward : forward };
58274             this.fireEvent('beforeeditnext', ecall );
58275             newCell = ecall.cell;
58276                         forward = ecall.forward;
58277         }
58278                 
58279         if(newCell){
58280             //Roo.log('next cell after edit');
58281             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58282         } else if (forward) {
58283             // tabbed past last
58284             this.fireEvent.defer(100, this, ['tabend',this]);
58285         }
58286     }
58287 });/*
58288  * Based on:
58289  * Ext JS Library 1.1.1
58290  * Copyright(c) 2006-2007, Ext JS, LLC.
58291  *
58292  * Originally Released Under LGPL - original licence link has changed is not relivant.
58293  *
58294  * Fork - LGPL
58295  * <script type="text/javascript">
58296  */
58297  
58298 /**
58299  * @class Roo.grid.EditorGrid
58300  * @extends Roo.grid.Grid
58301  * Class for creating and editable grid.
58302  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58303  * The container MUST have some type of size defined for the grid to fill. The container will be 
58304  * automatically set to position relative if it isn't already.
58305  * @param {Object} dataSource The data model to bind to
58306  * @param {Object} colModel The column model with info about this grid's columns
58307  */
58308 Roo.grid.EditorGrid = function(container, config){
58309     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58310     this.getGridEl().addClass("xedit-grid");
58311
58312     if(!this.selModel){
58313         this.selModel = new Roo.grid.CellSelectionModel();
58314     }
58315
58316     this.activeEditor = null;
58317
58318         this.addEvents({
58319             /**
58320              * @event beforeedit
58321              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58322              * <ul style="padding:5px;padding-left:16px;">
58323              * <li>grid - This grid</li>
58324              * <li>record - The record being edited</li>
58325              * <li>field - The field name being edited</li>
58326              * <li>value - The value for the field being edited.</li>
58327              * <li>row - The grid row index</li>
58328              * <li>column - The grid column index</li>
58329              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58330              * </ul>
58331              * @param {Object} e An edit event (see above for description)
58332              */
58333             "beforeedit" : true,
58334             /**
58335              * @event afteredit
58336              * Fires after a cell is edited. <br />
58337              * <ul style="padding:5px;padding-left:16px;">
58338              * <li>grid - This grid</li>
58339              * <li>record - The record being edited</li>
58340              * <li>field - The field name being edited</li>
58341              * <li>value - The value being set</li>
58342              * <li>originalValue - The original value for the field, before the edit.</li>
58343              * <li>row - The grid row index</li>
58344              * <li>column - The grid column index</li>
58345              * </ul>
58346              * @param {Object} e An edit event (see above for description)
58347              */
58348             "afteredit" : true,
58349             /**
58350              * @event validateedit
58351              * Fires after a cell is edited, but before the value is set in the record. 
58352          * You can use this to modify the value being set in the field, Return false
58353              * to cancel the change. The edit event object has the following properties <br />
58354              * <ul style="padding:5px;padding-left:16px;">
58355          * <li>editor - This editor</li>
58356              * <li>grid - This grid</li>
58357              * <li>record - The record being edited</li>
58358              * <li>field - The field name being edited</li>
58359              * <li>value - The value being set</li>
58360              * <li>originalValue - The original value for the field, before the edit.</li>
58361              * <li>row - The grid row index</li>
58362              * <li>column - The grid column index</li>
58363              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58364              * </ul>
58365              * @param {Object} e An edit event (see above for description)
58366              */
58367             "validateedit" : true
58368         });
58369     this.on("bodyscroll", this.stopEditing,  this);
58370     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58371 };
58372
58373 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58374     /**
58375      * @cfg {Number} clicksToEdit
58376      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58377      */
58378     clicksToEdit: 2,
58379
58380     // private
58381     isEditor : true,
58382     // private
58383     trackMouseOver: false, // causes very odd FF errors
58384
58385     onCellDblClick : function(g, row, col){
58386         this.startEditing(row, col);
58387     },
58388
58389     onEditComplete : function(ed, value, startValue){
58390         this.editing = false;
58391         this.activeEditor = null;
58392         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58393         var r = ed.record;
58394         var field = this.colModel.getDataIndex(ed.col);
58395         var e = {
58396             grid: this,
58397             record: r,
58398             field: field,
58399             originalValue: startValue,
58400             value: value,
58401             row: ed.row,
58402             column: ed.col,
58403             cancel:false,
58404             editor: ed
58405         };
58406         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58407         cell.show();
58408           
58409         if(String(value) !== String(startValue)){
58410             
58411             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58412                 r.set(field, e.value);
58413                 // if we are dealing with a combo box..
58414                 // then we also set the 'name' colum to be the displayField
58415                 if (ed.field.displayField && ed.field.name) {
58416                     r.set(ed.field.name, ed.field.el.dom.value);
58417                 }
58418                 
58419                 delete e.cancel; //?? why!!!
58420                 this.fireEvent("afteredit", e);
58421             }
58422         } else {
58423             this.fireEvent("afteredit", e); // always fire it!
58424         }
58425         this.view.focusCell(ed.row, ed.col);
58426     },
58427
58428     /**
58429      * Starts editing the specified for the specified row/column
58430      * @param {Number} rowIndex
58431      * @param {Number} colIndex
58432      */
58433     startEditing : function(row, col){
58434         this.stopEditing();
58435         if(this.colModel.isCellEditable(col, row)){
58436             this.view.ensureVisible(row, col, true);
58437           
58438             var r = this.dataSource.getAt(row);
58439             var field = this.colModel.getDataIndex(col);
58440             var cell = Roo.get(this.view.getCell(row,col));
58441             var e = {
58442                 grid: this,
58443                 record: r,
58444                 field: field,
58445                 value: r.data[field],
58446                 row: row,
58447                 column: col,
58448                 cancel:false 
58449             };
58450             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58451                 this.editing = true;
58452                 var ed = this.colModel.getCellEditor(col, row);
58453                 
58454                 if (!ed) {
58455                     return;
58456                 }
58457                 if(!ed.rendered){
58458                     ed.render(ed.parentEl || document.body);
58459                 }
58460                 ed.field.reset();
58461                
58462                 cell.hide();
58463                 
58464                 (function(){ // complex but required for focus issues in safari, ie and opera
58465                     ed.row = row;
58466                     ed.col = col;
58467                     ed.record = r;
58468                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58469                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58470                     this.activeEditor = ed;
58471                     var v = r.data[field];
58472                     ed.startEdit(this.view.getCell(row, col), v);
58473                     // combo's with 'displayField and name set
58474                     if (ed.field.displayField && ed.field.name) {
58475                         ed.field.el.dom.value = r.data[ed.field.name];
58476                     }
58477                     
58478                     
58479                 }).defer(50, this);
58480             }
58481         }
58482     },
58483         
58484     /**
58485      * Stops any active editing
58486      */
58487     stopEditing : function(){
58488         if(this.activeEditor){
58489             this.activeEditor.completeEdit();
58490         }
58491         this.activeEditor = null;
58492     },
58493         
58494          /**
58495      * Called to get grid's drag proxy text, by default returns this.ddText.
58496      * @return {String}
58497      */
58498     getDragDropText : function(){
58499         var count = this.selModel.getSelectedCell() ? 1 : 0;
58500         return String.format(this.ddText, count, count == 1 ? '' : 's');
58501     }
58502         
58503 });/*
58504  * Based on:
58505  * Ext JS Library 1.1.1
58506  * Copyright(c) 2006-2007, Ext JS, LLC.
58507  *
58508  * Originally Released Under LGPL - original licence link has changed is not relivant.
58509  *
58510  * Fork - LGPL
58511  * <script type="text/javascript">
58512  */
58513
58514 // private - not really -- you end up using it !
58515 // This is a support class used internally by the Grid components
58516
58517 /**
58518  * @class Roo.grid.GridEditor
58519  * @extends Roo.Editor
58520  * Class for creating and editable grid elements.
58521  * @param {Object} config any settings (must include field)
58522  */
58523 Roo.grid.GridEditor = function(field, config){
58524     if (!config && field.field) {
58525         config = field;
58526         field = Roo.factory(config.field, Roo.form);
58527     }
58528     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58529     field.monitorTab = false;
58530 };
58531
58532 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58533     
58534     /**
58535      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58536      */
58537     
58538     alignment: "tl-tl",
58539     autoSize: "width",
58540     hideEl : false,
58541     cls: "x-small-editor x-grid-editor",
58542     shim:false,
58543     shadow:"frame"
58544 });/*
58545  * Based on:
58546  * Ext JS Library 1.1.1
58547  * Copyright(c) 2006-2007, Ext JS, LLC.
58548  *
58549  * Originally Released Under LGPL - original licence link has changed is not relivant.
58550  *
58551  * Fork - LGPL
58552  * <script type="text/javascript">
58553  */
58554   
58555
58556   
58557 Roo.grid.PropertyRecord = Roo.data.Record.create([
58558     {name:'name',type:'string'},  'value'
58559 ]);
58560
58561
58562 Roo.grid.PropertyStore = function(grid, source){
58563     this.grid = grid;
58564     this.store = new Roo.data.Store({
58565         recordType : Roo.grid.PropertyRecord
58566     });
58567     this.store.on('update', this.onUpdate,  this);
58568     if(source){
58569         this.setSource(source);
58570     }
58571     Roo.grid.PropertyStore.superclass.constructor.call(this);
58572 };
58573
58574
58575
58576 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58577     setSource : function(o){
58578         this.source = o;
58579         this.store.removeAll();
58580         var data = [];
58581         for(var k in o){
58582             if(this.isEditableValue(o[k])){
58583                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58584             }
58585         }
58586         this.store.loadRecords({records: data}, {}, true);
58587     },
58588
58589     onUpdate : function(ds, record, type){
58590         if(type == Roo.data.Record.EDIT){
58591             var v = record.data['value'];
58592             var oldValue = record.modified['value'];
58593             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58594                 this.source[record.id] = v;
58595                 record.commit();
58596                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58597             }else{
58598                 record.reject();
58599             }
58600         }
58601     },
58602
58603     getProperty : function(row){
58604        return this.store.getAt(row);
58605     },
58606
58607     isEditableValue: function(val){
58608         if(val && val instanceof Date){
58609             return true;
58610         }else if(typeof val == 'object' || typeof val == 'function'){
58611             return false;
58612         }
58613         return true;
58614     },
58615
58616     setValue : function(prop, value){
58617         this.source[prop] = value;
58618         this.store.getById(prop).set('value', value);
58619     },
58620
58621     getSource : function(){
58622         return this.source;
58623     }
58624 });
58625
58626 Roo.grid.PropertyColumnModel = function(grid, store){
58627     this.grid = grid;
58628     var g = Roo.grid;
58629     g.PropertyColumnModel.superclass.constructor.call(this, [
58630         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58631         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58632     ]);
58633     this.store = store;
58634     this.bselect = Roo.DomHelper.append(document.body, {
58635         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58636             {tag: 'option', value: 'true', html: 'true'},
58637             {tag: 'option', value: 'false', html: 'false'}
58638         ]
58639     });
58640     Roo.id(this.bselect);
58641     var f = Roo.form;
58642     this.editors = {
58643         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58644         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58645         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58646         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58647         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58648     };
58649     this.renderCellDelegate = this.renderCell.createDelegate(this);
58650     this.renderPropDelegate = this.renderProp.createDelegate(this);
58651 };
58652
58653 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58654     
58655     
58656     nameText : 'Name',
58657     valueText : 'Value',
58658     
58659     dateFormat : 'm/j/Y',
58660     
58661     
58662     renderDate : function(dateVal){
58663         return dateVal.dateFormat(this.dateFormat);
58664     },
58665
58666     renderBool : function(bVal){
58667         return bVal ? 'true' : 'false';
58668     },
58669
58670     isCellEditable : function(colIndex, rowIndex){
58671         return colIndex == 1;
58672     },
58673
58674     getRenderer : function(col){
58675         return col == 1 ?
58676             this.renderCellDelegate : this.renderPropDelegate;
58677     },
58678
58679     renderProp : function(v){
58680         return this.getPropertyName(v);
58681     },
58682
58683     renderCell : function(val){
58684         var rv = val;
58685         if(val instanceof Date){
58686             rv = this.renderDate(val);
58687         }else if(typeof val == 'boolean'){
58688             rv = this.renderBool(val);
58689         }
58690         return Roo.util.Format.htmlEncode(rv);
58691     },
58692
58693     getPropertyName : function(name){
58694         var pn = this.grid.propertyNames;
58695         return pn && pn[name] ? pn[name] : name;
58696     },
58697
58698     getCellEditor : function(colIndex, rowIndex){
58699         var p = this.store.getProperty(rowIndex);
58700         var n = p.data['name'], val = p.data['value'];
58701         
58702         if(typeof(this.grid.customEditors[n]) == 'string'){
58703             return this.editors[this.grid.customEditors[n]];
58704         }
58705         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58706             return this.grid.customEditors[n];
58707         }
58708         if(val instanceof Date){
58709             return this.editors['date'];
58710         }else if(typeof val == 'number'){
58711             return this.editors['number'];
58712         }else if(typeof val == 'boolean'){
58713             return this.editors['boolean'];
58714         }else{
58715             return this.editors['string'];
58716         }
58717     }
58718 });
58719
58720 /**
58721  * @class Roo.grid.PropertyGrid
58722  * @extends Roo.grid.EditorGrid
58723  * This class represents the  interface of a component based property grid control.
58724  * <br><br>Usage:<pre><code>
58725  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58726       
58727  });
58728  // set any options
58729  grid.render();
58730  * </code></pre>
58731   
58732  * @constructor
58733  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58734  * The container MUST have some type of size defined for the grid to fill. The container will be
58735  * automatically set to position relative if it isn't already.
58736  * @param {Object} config A config object that sets properties on this grid.
58737  */
58738 Roo.grid.PropertyGrid = function(container, config){
58739     config = config || {};
58740     var store = new Roo.grid.PropertyStore(this);
58741     this.store = store;
58742     var cm = new Roo.grid.PropertyColumnModel(this, store);
58743     store.store.sort('name', 'ASC');
58744     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58745         ds: store.store,
58746         cm: cm,
58747         enableColLock:false,
58748         enableColumnMove:false,
58749         stripeRows:false,
58750         trackMouseOver: false,
58751         clicksToEdit:1
58752     }, config));
58753     this.getGridEl().addClass('x-props-grid');
58754     this.lastEditRow = null;
58755     this.on('columnresize', this.onColumnResize, this);
58756     this.addEvents({
58757          /**
58758              * @event beforepropertychange
58759              * Fires before a property changes (return false to stop?)
58760              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58761              * @param {String} id Record Id
58762              * @param {String} newval New Value
58763          * @param {String} oldval Old Value
58764              */
58765         "beforepropertychange": true,
58766         /**
58767              * @event propertychange
58768              * Fires after a property changes
58769              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58770              * @param {String} id Record Id
58771              * @param {String} newval New Value
58772          * @param {String} oldval Old Value
58773              */
58774         "propertychange": true
58775     });
58776     this.customEditors = this.customEditors || {};
58777 };
58778 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58779     
58780      /**
58781      * @cfg {Object} customEditors map of colnames=> custom editors.
58782      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58783      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58784      * false disables editing of the field.
58785          */
58786     
58787       /**
58788      * @cfg {Object} propertyNames map of property Names to their displayed value
58789          */
58790     
58791     render : function(){
58792         Roo.grid.PropertyGrid.superclass.render.call(this);
58793         this.autoSize.defer(100, this);
58794     },
58795
58796     autoSize : function(){
58797         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58798         if(this.view){
58799             this.view.fitColumns();
58800         }
58801     },
58802
58803     onColumnResize : function(){
58804         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58805         this.autoSize();
58806     },
58807     /**
58808      * Sets the data for the Grid
58809      * accepts a Key => Value object of all the elements avaiable.
58810      * @param {Object} data  to appear in grid.
58811      */
58812     setSource : function(source){
58813         this.store.setSource(source);
58814         //this.autoSize();
58815     },
58816     /**
58817      * Gets all the data from the grid.
58818      * @return {Object} data  data stored in grid
58819      */
58820     getSource : function(){
58821         return this.store.getSource();
58822     }
58823 });/*
58824   
58825  * Licence LGPL
58826  
58827  */
58828  
58829 /**
58830  * @class Roo.grid.Calendar
58831  * @extends Roo.util.Grid
58832  * This class extends the Grid to provide a calendar widget
58833  * <br><br>Usage:<pre><code>
58834  var grid = new Roo.grid.Calendar("my-container-id", {
58835      ds: myDataStore,
58836      cm: myColModel,
58837      selModel: mySelectionModel,
58838      autoSizeColumns: true,
58839      monitorWindowResize: false,
58840      trackMouseOver: true
58841      eventstore : real data store..
58842  });
58843  // set any options
58844  grid.render();
58845   
58846   * @constructor
58847  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58848  * The container MUST have some type of size defined for the grid to fill. The container will be
58849  * automatically set to position relative if it isn't already.
58850  * @param {Object} config A config object that sets properties on this grid.
58851  */
58852 Roo.grid.Calendar = function(container, config){
58853         // initialize the container
58854         this.container = Roo.get(container);
58855         this.container.update("");
58856         this.container.setStyle("overflow", "hidden");
58857     this.container.addClass('x-grid-container');
58858
58859     this.id = this.container.id;
58860
58861     Roo.apply(this, config);
58862     // check and correct shorthanded configs
58863     
58864     var rows = [];
58865     var d =1;
58866     for (var r = 0;r < 6;r++) {
58867         
58868         rows[r]=[];
58869         for (var c =0;c < 7;c++) {
58870             rows[r][c]= '';
58871         }
58872     }
58873     if (this.eventStore) {
58874         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58875         this.eventStore.on('load',this.onLoad, this);
58876         this.eventStore.on('beforeload',this.clearEvents, this);
58877          
58878     }
58879     
58880     this.dataSource = new Roo.data.Store({
58881             proxy: new Roo.data.MemoryProxy(rows),
58882             reader: new Roo.data.ArrayReader({}, [
58883                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58884     });
58885
58886     this.dataSource.load();
58887     this.ds = this.dataSource;
58888     this.ds.xmodule = this.xmodule || false;
58889     
58890     
58891     var cellRender = function(v,x,r)
58892     {
58893         return String.format(
58894             '<div class="fc-day  fc-widget-content"><div>' +
58895                 '<div class="fc-event-container"></div>' +
58896                 '<div class="fc-day-number">{0}</div>'+
58897                 
58898                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58899             '</div></div>', v);
58900     
58901     }
58902     
58903     
58904     this.colModel = new Roo.grid.ColumnModel( [
58905         {
58906             xtype: 'ColumnModel',
58907             xns: Roo.grid,
58908             dataIndex : 'weekday0',
58909             header : 'Sunday',
58910             renderer : cellRender
58911         },
58912         {
58913             xtype: 'ColumnModel',
58914             xns: Roo.grid,
58915             dataIndex : 'weekday1',
58916             header : 'Monday',
58917             renderer : cellRender
58918         },
58919         {
58920             xtype: 'ColumnModel',
58921             xns: Roo.grid,
58922             dataIndex : 'weekday2',
58923             header : 'Tuesday',
58924             renderer : cellRender
58925         },
58926         {
58927             xtype: 'ColumnModel',
58928             xns: Roo.grid,
58929             dataIndex : 'weekday3',
58930             header : 'Wednesday',
58931             renderer : cellRender
58932         },
58933         {
58934             xtype: 'ColumnModel',
58935             xns: Roo.grid,
58936             dataIndex : 'weekday4',
58937             header : 'Thursday',
58938             renderer : cellRender
58939         },
58940         {
58941             xtype: 'ColumnModel',
58942             xns: Roo.grid,
58943             dataIndex : 'weekday5',
58944             header : 'Friday',
58945             renderer : cellRender
58946         },
58947         {
58948             xtype: 'ColumnModel',
58949             xns: Roo.grid,
58950             dataIndex : 'weekday6',
58951             header : 'Saturday',
58952             renderer : cellRender
58953         }
58954     ]);
58955     this.cm = this.colModel;
58956     this.cm.xmodule = this.xmodule || false;
58957  
58958         
58959           
58960     //this.selModel = new Roo.grid.CellSelectionModel();
58961     //this.sm = this.selModel;
58962     //this.selModel.init(this);
58963     
58964     
58965     if(this.width){
58966         this.container.setWidth(this.width);
58967     }
58968
58969     if(this.height){
58970         this.container.setHeight(this.height);
58971     }
58972     /** @private */
58973         this.addEvents({
58974         // raw events
58975         /**
58976          * @event click
58977          * The raw click event for the entire grid.
58978          * @param {Roo.EventObject} e
58979          */
58980         "click" : true,
58981         /**
58982          * @event dblclick
58983          * The raw dblclick event for the entire grid.
58984          * @param {Roo.EventObject} e
58985          */
58986         "dblclick" : true,
58987         /**
58988          * @event contextmenu
58989          * The raw contextmenu event for the entire grid.
58990          * @param {Roo.EventObject} e
58991          */
58992         "contextmenu" : true,
58993         /**
58994          * @event mousedown
58995          * The raw mousedown event for the entire grid.
58996          * @param {Roo.EventObject} e
58997          */
58998         "mousedown" : true,
58999         /**
59000          * @event mouseup
59001          * The raw mouseup event for the entire grid.
59002          * @param {Roo.EventObject} e
59003          */
59004         "mouseup" : true,
59005         /**
59006          * @event mouseover
59007          * The raw mouseover event for the entire grid.
59008          * @param {Roo.EventObject} e
59009          */
59010         "mouseover" : true,
59011         /**
59012          * @event mouseout
59013          * The raw mouseout event for the entire grid.
59014          * @param {Roo.EventObject} e
59015          */
59016         "mouseout" : true,
59017         /**
59018          * @event keypress
59019          * The raw keypress event for the entire grid.
59020          * @param {Roo.EventObject} e
59021          */
59022         "keypress" : true,
59023         /**
59024          * @event keydown
59025          * The raw keydown event for the entire grid.
59026          * @param {Roo.EventObject} e
59027          */
59028         "keydown" : true,
59029
59030         // custom events
59031
59032         /**
59033          * @event cellclick
59034          * Fires when a cell is clicked
59035          * @param {Grid} this
59036          * @param {Number} rowIndex
59037          * @param {Number} columnIndex
59038          * @param {Roo.EventObject} e
59039          */
59040         "cellclick" : true,
59041         /**
59042          * @event celldblclick
59043          * Fires when a cell is double clicked
59044          * @param {Grid} this
59045          * @param {Number} rowIndex
59046          * @param {Number} columnIndex
59047          * @param {Roo.EventObject} e
59048          */
59049         "celldblclick" : true,
59050         /**
59051          * @event rowclick
59052          * Fires when a row is clicked
59053          * @param {Grid} this
59054          * @param {Number} rowIndex
59055          * @param {Roo.EventObject} e
59056          */
59057         "rowclick" : true,
59058         /**
59059          * @event rowdblclick
59060          * Fires when a row is double clicked
59061          * @param {Grid} this
59062          * @param {Number} rowIndex
59063          * @param {Roo.EventObject} e
59064          */
59065         "rowdblclick" : true,
59066         /**
59067          * @event headerclick
59068          * Fires when a header is clicked
59069          * @param {Grid} this
59070          * @param {Number} columnIndex
59071          * @param {Roo.EventObject} e
59072          */
59073         "headerclick" : true,
59074         /**
59075          * @event headerdblclick
59076          * Fires when a header cell is double clicked
59077          * @param {Grid} this
59078          * @param {Number} columnIndex
59079          * @param {Roo.EventObject} e
59080          */
59081         "headerdblclick" : true,
59082         /**
59083          * @event rowcontextmenu
59084          * Fires when a row is right clicked
59085          * @param {Grid} this
59086          * @param {Number} rowIndex
59087          * @param {Roo.EventObject} e
59088          */
59089         "rowcontextmenu" : true,
59090         /**
59091          * @event cellcontextmenu
59092          * Fires when a cell is right clicked
59093          * @param {Grid} this
59094          * @param {Number} rowIndex
59095          * @param {Number} cellIndex
59096          * @param {Roo.EventObject} e
59097          */
59098          "cellcontextmenu" : true,
59099         /**
59100          * @event headercontextmenu
59101          * Fires when a header is right clicked
59102          * @param {Grid} this
59103          * @param {Number} columnIndex
59104          * @param {Roo.EventObject} e
59105          */
59106         "headercontextmenu" : true,
59107         /**
59108          * @event bodyscroll
59109          * Fires when the body element is scrolled
59110          * @param {Number} scrollLeft
59111          * @param {Number} scrollTop
59112          */
59113         "bodyscroll" : true,
59114         /**
59115          * @event columnresize
59116          * Fires when the user resizes a column
59117          * @param {Number} columnIndex
59118          * @param {Number} newSize
59119          */
59120         "columnresize" : true,
59121         /**
59122          * @event columnmove
59123          * Fires when the user moves a column
59124          * @param {Number} oldIndex
59125          * @param {Number} newIndex
59126          */
59127         "columnmove" : true,
59128         /**
59129          * @event startdrag
59130          * Fires when row(s) start being dragged
59131          * @param {Grid} this
59132          * @param {Roo.GridDD} dd The drag drop object
59133          * @param {event} e The raw browser event
59134          */
59135         "startdrag" : true,
59136         /**
59137          * @event enddrag
59138          * Fires when a drag operation is complete
59139          * @param {Grid} this
59140          * @param {Roo.GridDD} dd The drag drop object
59141          * @param {event} e The raw browser event
59142          */
59143         "enddrag" : true,
59144         /**
59145          * @event dragdrop
59146          * Fires when dragged row(s) are dropped on a valid DD target
59147          * @param {Grid} this
59148          * @param {Roo.GridDD} dd The drag drop object
59149          * @param {String} targetId The target drag drop object
59150          * @param {event} e The raw browser event
59151          */
59152         "dragdrop" : true,
59153         /**
59154          * @event dragover
59155          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59156          * @param {Grid} this
59157          * @param {Roo.GridDD} dd The drag drop object
59158          * @param {String} targetId The target drag drop object
59159          * @param {event} e The raw browser event
59160          */
59161         "dragover" : true,
59162         /**
59163          * @event dragenter
59164          *  Fires when the dragged row(s) first cross another DD target while being dragged
59165          * @param {Grid} this
59166          * @param {Roo.GridDD} dd The drag drop object
59167          * @param {String} targetId The target drag drop object
59168          * @param {event} e The raw browser event
59169          */
59170         "dragenter" : true,
59171         /**
59172          * @event dragout
59173          * Fires when the dragged row(s) leave another DD target while being dragged
59174          * @param {Grid} this
59175          * @param {Roo.GridDD} dd The drag drop object
59176          * @param {String} targetId The target drag drop object
59177          * @param {event} e The raw browser event
59178          */
59179         "dragout" : true,
59180         /**
59181          * @event rowclass
59182          * Fires when a row is rendered, so you can change add a style to it.
59183          * @param {GridView} gridview   The grid view
59184          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59185          */
59186         'rowclass' : true,
59187
59188         /**
59189          * @event render
59190          * Fires when the grid is rendered
59191          * @param {Grid} grid
59192          */
59193         'render' : true,
59194             /**
59195              * @event select
59196              * Fires when a date is selected
59197              * @param {DatePicker} this
59198              * @param {Date} date The selected date
59199              */
59200         'select': true,
59201         /**
59202              * @event monthchange
59203              * Fires when the displayed month changes 
59204              * @param {DatePicker} this
59205              * @param {Date} date The selected month
59206              */
59207         'monthchange': true,
59208         /**
59209              * @event evententer
59210              * Fires when mouse over an event
59211              * @param {Calendar} this
59212              * @param {event} Event
59213              */
59214         'evententer': true,
59215         /**
59216              * @event eventleave
59217              * Fires when the mouse leaves an
59218              * @param {Calendar} this
59219              * @param {event}
59220              */
59221         'eventleave': true,
59222         /**
59223              * @event eventclick
59224              * Fires when the mouse click an
59225              * @param {Calendar} this
59226              * @param {event}
59227              */
59228         'eventclick': true,
59229         /**
59230              * @event eventrender
59231              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59232              * @param {Calendar} this
59233              * @param {data} data to be modified
59234              */
59235         'eventrender': true
59236         
59237     });
59238
59239     Roo.grid.Grid.superclass.constructor.call(this);
59240     this.on('render', function() {
59241         this.view.el.addClass('x-grid-cal'); 
59242         
59243         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59244
59245     },this);
59246     
59247     if (!Roo.grid.Calendar.style) {
59248         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59249             
59250             
59251             '.x-grid-cal .x-grid-col' :  {
59252                 height: 'auto !important',
59253                 'vertical-align': 'top'
59254             },
59255             '.x-grid-cal  .fc-event-hori' : {
59256                 height: '14px'
59257             }
59258              
59259             
59260         }, Roo.id());
59261     }
59262
59263     
59264     
59265 };
59266 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59267     /**
59268      * @cfg {Store} eventStore The store that loads events.
59269      */
59270     eventStore : 25,
59271
59272      
59273     activeDate : false,
59274     startDay : 0,
59275     autoWidth : true,
59276     monitorWindowResize : false,
59277
59278     
59279     resizeColumns : function() {
59280         var col = (this.view.el.getWidth() / 7) - 3;
59281         // loop through cols, and setWidth
59282         for(var i =0 ; i < 7 ; i++){
59283             this.cm.setColumnWidth(i, col);
59284         }
59285     },
59286      setDate :function(date) {
59287         
59288         Roo.log('setDate?');
59289         
59290         this.resizeColumns();
59291         var vd = this.activeDate;
59292         this.activeDate = date;
59293 //        if(vd && this.el){
59294 //            var t = date.getTime();
59295 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59296 //                Roo.log('using add remove');
59297 //                
59298 //                this.fireEvent('monthchange', this, date);
59299 //                
59300 //                this.cells.removeClass("fc-state-highlight");
59301 //                this.cells.each(function(c){
59302 //                   if(c.dateValue == t){
59303 //                       c.addClass("fc-state-highlight");
59304 //                       setTimeout(function(){
59305 //                            try{c.dom.firstChild.focus();}catch(e){}
59306 //                       }, 50);
59307 //                       return false;
59308 //                   }
59309 //                   return true;
59310 //                });
59311 //                return;
59312 //            }
59313 //        }
59314         
59315         var days = date.getDaysInMonth();
59316         
59317         var firstOfMonth = date.getFirstDateOfMonth();
59318         var startingPos = firstOfMonth.getDay()-this.startDay;
59319         
59320         if(startingPos < this.startDay){
59321             startingPos += 7;
59322         }
59323         
59324         var pm = date.add(Date.MONTH, -1);
59325         var prevStart = pm.getDaysInMonth()-startingPos;
59326 //        
59327         
59328         
59329         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59330         
59331         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59332         //this.cells.addClassOnOver('fc-state-hover');
59333         
59334         var cells = this.cells.elements;
59335         var textEls = this.textNodes;
59336         
59337         //Roo.each(cells, function(cell){
59338         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59339         //});
59340         
59341         days += startingPos;
59342
59343         // convert everything to numbers so it's fast
59344         var day = 86400000;
59345         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59346         //Roo.log(d);
59347         //Roo.log(pm);
59348         //Roo.log(prevStart);
59349         
59350         var today = new Date().clearTime().getTime();
59351         var sel = date.clearTime().getTime();
59352         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59353         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59354         var ddMatch = this.disabledDatesRE;
59355         var ddText = this.disabledDatesText;
59356         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59357         var ddaysText = this.disabledDaysText;
59358         var format = this.format;
59359         
59360         var setCellClass = function(cal, cell){
59361             
59362             //Roo.log('set Cell Class');
59363             cell.title = "";
59364             var t = d.getTime();
59365             
59366             //Roo.log(d);
59367             
59368             
59369             cell.dateValue = t;
59370             if(t == today){
59371                 cell.className += " fc-today";
59372                 cell.className += " fc-state-highlight";
59373                 cell.title = cal.todayText;
59374             }
59375             if(t == sel){
59376                 // disable highlight in other month..
59377                 cell.className += " fc-state-highlight";
59378                 
59379             }
59380             // disabling
59381             if(t < min) {
59382                 //cell.className = " fc-state-disabled";
59383                 cell.title = cal.minText;
59384                 return;
59385             }
59386             if(t > max) {
59387                 //cell.className = " fc-state-disabled";
59388                 cell.title = cal.maxText;
59389                 return;
59390             }
59391             if(ddays){
59392                 if(ddays.indexOf(d.getDay()) != -1){
59393                     // cell.title = ddaysText;
59394                    // cell.className = " fc-state-disabled";
59395                 }
59396             }
59397             if(ddMatch && format){
59398                 var fvalue = d.dateFormat(format);
59399                 if(ddMatch.test(fvalue)){
59400                     cell.title = ddText.replace("%0", fvalue);
59401                    cell.className = " fc-state-disabled";
59402                 }
59403             }
59404             
59405             if (!cell.initialClassName) {
59406                 cell.initialClassName = cell.dom.className;
59407             }
59408             
59409             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59410         };
59411
59412         var i = 0;
59413         
59414         for(; i < startingPos; i++) {
59415             cells[i].dayName =  (++prevStart);
59416             Roo.log(textEls[i]);
59417             d.setDate(d.getDate()+1);
59418             
59419             //cells[i].className = "fc-past fc-other-month";
59420             setCellClass(this, cells[i]);
59421         }
59422         
59423         var intDay = 0;
59424         
59425         for(; i < days; i++){
59426             intDay = i - startingPos + 1;
59427             cells[i].dayName =  (intDay);
59428             d.setDate(d.getDate()+1);
59429             
59430             cells[i].className = ''; // "x-date-active";
59431             setCellClass(this, cells[i]);
59432         }
59433         var extraDays = 0;
59434         
59435         for(; i < 42; i++) {
59436             //textEls[i].innerHTML = (++extraDays);
59437             
59438             d.setDate(d.getDate()+1);
59439             cells[i].dayName = (++extraDays);
59440             cells[i].className = "fc-future fc-other-month";
59441             setCellClass(this, cells[i]);
59442         }
59443         
59444         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59445         
59446         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59447         
59448         // this will cause all the cells to mis
59449         var rows= [];
59450         var i =0;
59451         for (var r = 0;r < 6;r++) {
59452             for (var c =0;c < 7;c++) {
59453                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59454             }    
59455         }
59456         
59457         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59458         for(i=0;i<cells.length;i++) {
59459             
59460             this.cells.elements[i].dayName = cells[i].dayName ;
59461             this.cells.elements[i].className = cells[i].className;
59462             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59463             this.cells.elements[i].title = cells[i].title ;
59464             this.cells.elements[i].dateValue = cells[i].dateValue ;
59465         }
59466         
59467         
59468         
59469         
59470         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59471         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59472         
59473         ////if(totalRows != 6){
59474             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59475            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59476        // }
59477         
59478         this.fireEvent('monthchange', this, date);
59479         
59480         
59481     },
59482  /**
59483      * Returns the grid's SelectionModel.
59484      * @return {SelectionModel}
59485      */
59486     getSelectionModel : function(){
59487         if(!this.selModel){
59488             this.selModel = new Roo.grid.CellSelectionModel();
59489         }
59490         return this.selModel;
59491     },
59492
59493     load: function() {
59494         this.eventStore.load()
59495         
59496         
59497         
59498     },
59499     
59500     findCell : function(dt) {
59501         dt = dt.clearTime().getTime();
59502         var ret = false;
59503         this.cells.each(function(c){
59504             //Roo.log("check " +c.dateValue + '?=' + dt);
59505             if(c.dateValue == dt){
59506                 ret = c;
59507                 return false;
59508             }
59509             return true;
59510         });
59511         
59512         return ret;
59513     },
59514     
59515     findCells : function(rec) {
59516         var s = rec.data.start_dt.clone().clearTime().getTime();
59517        // Roo.log(s);
59518         var e= rec.data.end_dt.clone().clearTime().getTime();
59519        // Roo.log(e);
59520         var ret = [];
59521         this.cells.each(function(c){
59522              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59523             
59524             if(c.dateValue > e){
59525                 return ;
59526             }
59527             if(c.dateValue < s){
59528                 return ;
59529             }
59530             ret.push(c);
59531         });
59532         
59533         return ret;    
59534     },
59535     
59536     findBestRow: function(cells)
59537     {
59538         var ret = 0;
59539         
59540         for (var i =0 ; i < cells.length;i++) {
59541             ret  = Math.max(cells[i].rows || 0,ret);
59542         }
59543         return ret;
59544         
59545     },
59546     
59547     
59548     addItem : function(rec)
59549     {
59550         // look for vertical location slot in
59551         var cells = this.findCells(rec);
59552         
59553         rec.row = this.findBestRow(cells);
59554         
59555         // work out the location.
59556         
59557         var crow = false;
59558         var rows = [];
59559         for(var i =0; i < cells.length; i++) {
59560             if (!crow) {
59561                 crow = {
59562                     start : cells[i],
59563                     end :  cells[i]
59564                 };
59565                 continue;
59566             }
59567             if (crow.start.getY() == cells[i].getY()) {
59568                 // on same row.
59569                 crow.end = cells[i];
59570                 continue;
59571             }
59572             // different row.
59573             rows.push(crow);
59574             crow = {
59575                 start: cells[i],
59576                 end : cells[i]
59577             };
59578             
59579         }
59580         
59581         rows.push(crow);
59582         rec.els = [];
59583         rec.rows = rows;
59584         rec.cells = cells;
59585         for (var i = 0; i < cells.length;i++) {
59586             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59587             
59588         }
59589         
59590         
59591     },
59592     
59593     clearEvents: function() {
59594         
59595         if (!this.eventStore.getCount()) {
59596             return;
59597         }
59598         // reset number of rows in cells.
59599         Roo.each(this.cells.elements, function(c){
59600             c.rows = 0;
59601         });
59602         
59603         this.eventStore.each(function(e) {
59604             this.clearEvent(e);
59605         },this);
59606         
59607     },
59608     
59609     clearEvent : function(ev)
59610     {
59611         if (ev.els) {
59612             Roo.each(ev.els, function(el) {
59613                 el.un('mouseenter' ,this.onEventEnter, this);
59614                 el.un('mouseleave' ,this.onEventLeave, this);
59615                 el.remove();
59616             },this);
59617             ev.els = [];
59618         }
59619     },
59620     
59621     
59622     renderEvent : function(ev,ctr) {
59623         if (!ctr) {
59624              ctr = this.view.el.select('.fc-event-container',true).first();
59625         }
59626         
59627          
59628         this.clearEvent(ev);
59629             //code
59630        
59631         
59632         
59633         ev.els = [];
59634         var cells = ev.cells;
59635         var rows = ev.rows;
59636         this.fireEvent('eventrender', this, ev);
59637         
59638         for(var i =0; i < rows.length; i++) {
59639             
59640             cls = '';
59641             if (i == 0) {
59642                 cls += ' fc-event-start';
59643             }
59644             if ((i+1) == rows.length) {
59645                 cls += ' fc-event-end';
59646             }
59647             
59648             //Roo.log(ev.data);
59649             // how many rows should it span..
59650             var cg = this.eventTmpl.append(ctr,Roo.apply({
59651                 fccls : cls
59652                 
59653             }, ev.data) , true);
59654             
59655             
59656             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59657             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59658             cg.on('click', this.onEventClick, this, ev);
59659             
59660             ev.els.push(cg);
59661             
59662             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59663             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59664             //Roo.log(cg);
59665              
59666             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59667             cg.setWidth(ebox.right - sbox.x -2);
59668         }
59669     },
59670     
59671     renderEvents: function()
59672     {   
59673         // first make sure there is enough space..
59674         
59675         if (!this.eventTmpl) {
59676             this.eventTmpl = new Roo.Template(
59677                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59678                     '<div class="fc-event-inner">' +
59679                         '<span class="fc-event-time">{time}</span>' +
59680                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59681                     '</div>' +
59682                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59683                 '</div>'
59684             );
59685                 
59686         }
59687                
59688         
59689         
59690         this.cells.each(function(c) {
59691             //Roo.log(c.select('.fc-day-content div',true).first());
59692             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59693         });
59694         
59695         var ctr = this.view.el.select('.fc-event-container',true).first();
59696         
59697         var cls;
59698         this.eventStore.each(function(ev){
59699             
59700             this.renderEvent(ev);
59701              
59702              
59703         }, this);
59704         this.view.layout();
59705         
59706     },
59707     
59708     onEventEnter: function (e, el,event,d) {
59709         this.fireEvent('evententer', this, el, event);
59710     },
59711     
59712     onEventLeave: function (e, el,event,d) {
59713         this.fireEvent('eventleave', this, el, event);
59714     },
59715     
59716     onEventClick: function (e, el,event,d) {
59717         this.fireEvent('eventclick', this, el, event);
59718     },
59719     
59720     onMonthChange: function () {
59721         this.store.load();
59722     },
59723     
59724     onLoad: function () {
59725         
59726         //Roo.log('calendar onload');
59727 //         
59728         if(this.eventStore.getCount() > 0){
59729             
59730            
59731             
59732             this.eventStore.each(function(d){
59733                 
59734                 
59735                 // FIXME..
59736                 var add =   d.data;
59737                 if (typeof(add.end_dt) == 'undefined')  {
59738                     Roo.log("Missing End time in calendar data: ");
59739                     Roo.log(d);
59740                     return;
59741                 }
59742                 if (typeof(add.start_dt) == 'undefined')  {
59743                     Roo.log("Missing Start time in calendar data: ");
59744                     Roo.log(d);
59745                     return;
59746                 }
59747                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59748                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59749                 add.id = add.id || d.id;
59750                 add.title = add.title || '??';
59751                 
59752                 this.addItem(d);
59753                 
59754              
59755             },this);
59756         }
59757         
59758         this.renderEvents();
59759     }
59760     
59761
59762 });
59763 /*
59764  grid : {
59765                 xtype: 'Grid',
59766                 xns: Roo.grid,
59767                 listeners : {
59768                     render : function ()
59769                     {
59770                         _this.grid = this;
59771                         
59772                         if (!this.view.el.hasClass('course-timesheet')) {
59773                             this.view.el.addClass('course-timesheet');
59774                         }
59775                         if (this.tsStyle) {
59776                             this.ds.load({});
59777                             return; 
59778                         }
59779                         Roo.log('width');
59780                         Roo.log(_this.grid.view.el.getWidth());
59781                         
59782                         
59783                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59784                             '.course-timesheet .x-grid-row' : {
59785                                 height: '80px'
59786                             },
59787                             '.x-grid-row td' : {
59788                                 'vertical-align' : 0
59789                             },
59790                             '.course-edit-link' : {
59791                                 'color' : 'blue',
59792                                 'text-overflow' : 'ellipsis',
59793                                 'overflow' : 'hidden',
59794                                 'white-space' : 'nowrap',
59795                                 'cursor' : 'pointer'
59796                             },
59797                             '.sub-link' : {
59798                                 'color' : 'green'
59799                             },
59800                             '.de-act-sup-link' : {
59801                                 'color' : 'purple',
59802                                 'text-decoration' : 'line-through'
59803                             },
59804                             '.de-act-link' : {
59805                                 'color' : 'red',
59806                                 'text-decoration' : 'line-through'
59807                             },
59808                             '.course-timesheet .course-highlight' : {
59809                                 'border-top-style': 'dashed !important',
59810                                 'border-bottom-bottom': 'dashed !important'
59811                             },
59812                             '.course-timesheet .course-item' : {
59813                                 'font-family'   : 'tahoma, arial, helvetica',
59814                                 'font-size'     : '11px',
59815                                 'overflow'      : 'hidden',
59816                                 'padding-left'  : '10px',
59817                                 'padding-right' : '10px',
59818                                 'padding-top' : '10px' 
59819                             }
59820                             
59821                         }, Roo.id());
59822                                 this.ds.load({});
59823                     }
59824                 },
59825                 autoWidth : true,
59826                 monitorWindowResize : false,
59827                 cellrenderer : function(v,x,r)
59828                 {
59829                     return v;
59830                 },
59831                 sm : {
59832                     xtype: 'CellSelectionModel',
59833                     xns: Roo.grid
59834                 },
59835                 dataSource : {
59836                     xtype: 'Store',
59837                     xns: Roo.data,
59838                     listeners : {
59839                         beforeload : function (_self, options)
59840                         {
59841                             options.params = options.params || {};
59842                             options.params._month = _this.monthField.getValue();
59843                             options.params.limit = 9999;
59844                             options.params['sort'] = 'when_dt';    
59845                             options.params['dir'] = 'ASC';    
59846                             this.proxy.loadResponse = this.loadResponse;
59847                             Roo.log("load?");
59848                             //this.addColumns();
59849                         },
59850                         load : function (_self, records, options)
59851                         {
59852                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59853                                 // if you click on the translation.. you can edit it...
59854                                 var el = Roo.get(this);
59855                                 var id = el.dom.getAttribute('data-id');
59856                                 var d = el.dom.getAttribute('data-date');
59857                                 var t = el.dom.getAttribute('data-time');
59858                                 //var id = this.child('span').dom.textContent;
59859                                 
59860                                 //Roo.log(this);
59861                                 Pman.Dialog.CourseCalendar.show({
59862                                     id : id,
59863                                     when_d : d,
59864                                     when_t : t,
59865                                     productitem_active : id ? 1 : 0
59866                                 }, function() {
59867                                     _this.grid.ds.load({});
59868                                 });
59869                            
59870                            });
59871                            
59872                            _this.panel.fireEvent('resize', [ '', '' ]);
59873                         }
59874                     },
59875                     loadResponse : function(o, success, response){
59876                             // this is overridden on before load..
59877                             
59878                             Roo.log("our code?");       
59879                             //Roo.log(success);
59880                             //Roo.log(response)
59881                             delete this.activeRequest;
59882                             if(!success){
59883                                 this.fireEvent("loadexception", this, o, response);
59884                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59885                                 return;
59886                             }
59887                             var result;
59888                             try {
59889                                 result = o.reader.read(response);
59890                             }catch(e){
59891                                 Roo.log("load exception?");
59892                                 this.fireEvent("loadexception", this, o, response, e);
59893                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59894                                 return;
59895                             }
59896                             Roo.log("ready...");        
59897                             // loop through result.records;
59898                             // and set this.tdate[date] = [] << array of records..
59899                             _this.tdata  = {};
59900                             Roo.each(result.records, function(r){
59901                                 //Roo.log(r.data);
59902                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59903                                     _this.tdata[r.data.when_dt.format('j')] = [];
59904                                 }
59905                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59906                             });
59907                             
59908                             //Roo.log(_this.tdata);
59909                             
59910                             result.records = [];
59911                             result.totalRecords = 6;
59912                     
59913                             // let's generate some duumy records for the rows.
59914                             //var st = _this.dateField.getValue();
59915                             
59916                             // work out monday..
59917                             //st = st.add(Date.DAY, -1 * st.format('w'));
59918                             
59919                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59920                             
59921                             var firstOfMonth = date.getFirstDayOfMonth();
59922                             var days = date.getDaysInMonth();
59923                             var d = 1;
59924                             var firstAdded = false;
59925                             for (var i = 0; i < result.totalRecords ; i++) {
59926                                 //var d= st.add(Date.DAY, i);
59927                                 var row = {};
59928                                 var added = 0;
59929                                 for(var w = 0 ; w < 7 ; w++){
59930                                     if(!firstAdded && firstOfMonth != w){
59931                                         continue;
59932                                     }
59933                                     if(d > days){
59934                                         continue;
59935                                     }
59936                                     firstAdded = true;
59937                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59938                                     row['weekday'+w] = String.format(
59939                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59940                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59941                                                     d,
59942                                                     date.format('Y-m-')+dd
59943                                                 );
59944                                     added++;
59945                                     if(typeof(_this.tdata[d]) != 'undefined'){
59946                                         Roo.each(_this.tdata[d], function(r){
59947                                             var is_sub = '';
59948                                             var deactive = '';
59949                                             var id = r.id;
59950                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59951                                             if(r.parent_id*1>0){
59952                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59953                                                 id = r.parent_id;
59954                                             }
59955                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59956                                                 deactive = 'de-act-link';
59957                                             }
59958                                             
59959                                             row['weekday'+w] += String.format(
59960                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59961                                                     id, //0
59962                                                     r.product_id_name, //1
59963                                                     r.when_dt.format('h:ia'), //2
59964                                                     is_sub, //3
59965                                                     deactive, //4
59966                                                     desc // 5
59967                                             );
59968                                         });
59969                                     }
59970                                     d++;
59971                                 }
59972                                 
59973                                 // only do this if something added..
59974                                 if(added > 0){ 
59975                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59976                                 }
59977                                 
59978                                 
59979                                 // push it twice. (second one with an hour..
59980                                 
59981                             }
59982                             //Roo.log(result);
59983                             this.fireEvent("load", this, o, o.request.arg);
59984                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59985                         },
59986                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59987                     proxy : {
59988                         xtype: 'HttpProxy',
59989                         xns: Roo.data,
59990                         method : 'GET',
59991                         url : baseURL + '/Roo/Shop_course.php'
59992                     },
59993                     reader : {
59994                         xtype: 'JsonReader',
59995                         xns: Roo.data,
59996                         id : 'id',
59997                         fields : [
59998                             {
59999                                 'name': 'id',
60000                                 'type': 'int'
60001                             },
60002                             {
60003                                 'name': 'when_dt',
60004                                 'type': 'string'
60005                             },
60006                             {
60007                                 'name': 'end_dt',
60008                                 'type': 'string'
60009                             },
60010                             {
60011                                 'name': 'parent_id',
60012                                 'type': 'int'
60013                             },
60014                             {
60015                                 'name': 'product_id',
60016                                 'type': 'int'
60017                             },
60018                             {
60019                                 'name': 'productitem_id',
60020                                 'type': 'int'
60021                             },
60022                             {
60023                                 'name': 'guid',
60024                                 'type': 'int'
60025                             }
60026                         ]
60027                     }
60028                 },
60029                 toolbar : {
60030                     xtype: 'Toolbar',
60031                     xns: Roo,
60032                     items : [
60033                         {
60034                             xtype: 'Button',
60035                             xns: Roo.Toolbar,
60036                             listeners : {
60037                                 click : function (_self, e)
60038                                 {
60039                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60040                                     sd.setMonth(sd.getMonth()-1);
60041                                     _this.monthField.setValue(sd.format('Y-m-d'));
60042                                     _this.grid.ds.load({});
60043                                 }
60044                             },
60045                             text : "Back"
60046                         },
60047                         {
60048                             xtype: 'Separator',
60049                             xns: Roo.Toolbar
60050                         },
60051                         {
60052                             xtype: 'MonthField',
60053                             xns: Roo.form,
60054                             listeners : {
60055                                 render : function (_self)
60056                                 {
60057                                     _this.monthField = _self;
60058                                    // _this.monthField.set  today
60059                                 },
60060                                 select : function (combo, date)
60061                                 {
60062                                     _this.grid.ds.load({});
60063                                 }
60064                             },
60065                             value : (function() { return new Date(); })()
60066                         },
60067                         {
60068                             xtype: 'Separator',
60069                             xns: Roo.Toolbar
60070                         },
60071                         {
60072                             xtype: 'TextItem',
60073                             xns: Roo.Toolbar,
60074                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60075                         },
60076                         {
60077                             xtype: 'Fill',
60078                             xns: Roo.Toolbar
60079                         },
60080                         {
60081                             xtype: 'Button',
60082                             xns: Roo.Toolbar,
60083                             listeners : {
60084                                 click : function (_self, e)
60085                                 {
60086                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60087                                     sd.setMonth(sd.getMonth()+1);
60088                                     _this.monthField.setValue(sd.format('Y-m-d'));
60089                                     _this.grid.ds.load({});
60090                                 }
60091                             },
60092                             text : "Next"
60093                         }
60094                     ]
60095                 },
60096                  
60097             }
60098         };
60099         
60100         *//*
60101  * Based on:
60102  * Ext JS Library 1.1.1
60103  * Copyright(c) 2006-2007, Ext JS, LLC.
60104  *
60105  * Originally Released Under LGPL - original licence link has changed is not relivant.
60106  *
60107  * Fork - LGPL
60108  * <script type="text/javascript">
60109  */
60110  
60111 /**
60112  * @class Roo.LoadMask
60113  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60114  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60115  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60116  * element's UpdateManager load indicator and will be destroyed after the initial load.
60117  * @constructor
60118  * Create a new LoadMask
60119  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60120  * @param {Object} config The config object
60121  */
60122 Roo.LoadMask = function(el, config){
60123     this.el = Roo.get(el);
60124     Roo.apply(this, config);
60125     if(this.store){
60126         this.store.on('beforeload', this.onBeforeLoad, this);
60127         this.store.on('load', this.onLoad, this);
60128         this.store.on('loadexception', this.onLoadException, this);
60129         this.removeMask = false;
60130     }else{
60131         var um = this.el.getUpdateManager();
60132         um.showLoadIndicator = false; // disable the default indicator
60133         um.on('beforeupdate', this.onBeforeLoad, this);
60134         um.on('update', this.onLoad, this);
60135         um.on('failure', this.onLoad, this);
60136         this.removeMask = true;
60137     }
60138 };
60139
60140 Roo.LoadMask.prototype = {
60141     /**
60142      * @cfg {Boolean} removeMask
60143      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60144      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60145      */
60146     /**
60147      * @cfg {String} msg
60148      * The text to display in a centered loading message box (defaults to 'Loading...')
60149      */
60150     msg : 'Loading...',
60151     /**
60152      * @cfg {String} msgCls
60153      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60154      */
60155     msgCls : 'x-mask-loading',
60156
60157     /**
60158      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60159      * @type Boolean
60160      */
60161     disabled: false,
60162
60163     /**
60164      * Disables the mask to prevent it from being displayed
60165      */
60166     disable : function(){
60167        this.disabled = true;
60168     },
60169
60170     /**
60171      * Enables the mask so that it can be displayed
60172      */
60173     enable : function(){
60174         this.disabled = false;
60175     },
60176     
60177     onLoadException : function()
60178     {
60179         Roo.log(arguments);
60180         
60181         if (typeof(arguments[3]) != 'undefined') {
60182             Roo.MessageBox.alert("Error loading",arguments[3]);
60183         } 
60184         /*
60185         try {
60186             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60187                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60188             }   
60189         } catch(e) {
60190             
60191         }
60192         */
60193     
60194         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60195     },
60196     // private
60197     onLoad : function()
60198     {
60199         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60200     },
60201
60202     // private
60203     onBeforeLoad : function(){
60204         if(!this.disabled){
60205             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60206         }
60207     },
60208
60209     // private
60210     destroy : function(){
60211         if(this.store){
60212             this.store.un('beforeload', this.onBeforeLoad, this);
60213             this.store.un('load', this.onLoad, this);
60214             this.store.un('loadexception', this.onLoadException, this);
60215         }else{
60216             var um = this.el.getUpdateManager();
60217             um.un('beforeupdate', this.onBeforeLoad, this);
60218             um.un('update', this.onLoad, this);
60219             um.un('failure', this.onLoad, this);
60220         }
60221     }
60222 };/*
60223  * Based on:
60224  * Ext JS Library 1.1.1
60225  * Copyright(c) 2006-2007, Ext JS, LLC.
60226  *
60227  * Originally Released Under LGPL - original licence link has changed is not relivant.
60228  *
60229  * Fork - LGPL
60230  * <script type="text/javascript">
60231  */
60232
60233
60234 /**
60235  * @class Roo.XTemplate
60236  * @extends Roo.Template
60237  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60238 <pre><code>
60239 var t = new Roo.XTemplate(
60240         '&lt;select name="{name}"&gt;',
60241                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60242         '&lt;/select&gt;'
60243 );
60244  
60245 // then append, applying the master template values
60246  </code></pre>
60247  *
60248  * Supported features:
60249  *
60250  *  Tags:
60251
60252 <pre><code>
60253       {a_variable} - output encoded.
60254       {a_variable.format:("Y-m-d")} - call a method on the variable
60255       {a_variable:raw} - unencoded output
60256       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60257       {a_variable:this.method_on_template(...)} - call a method on the template object.
60258  
60259 </code></pre>
60260  *  The tpl tag:
60261 <pre><code>
60262         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60263         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60264         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60265         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60266   
60267         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60268         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60269 </code></pre>
60270  *      
60271  */
60272 Roo.XTemplate = function()
60273 {
60274     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60275     if (this.html) {
60276         this.compile();
60277     }
60278 };
60279
60280
60281 Roo.extend(Roo.XTemplate, Roo.Template, {
60282
60283     /**
60284      * The various sub templates
60285      */
60286     tpls : false,
60287     /**
60288      *
60289      * basic tag replacing syntax
60290      * WORD:WORD()
60291      *
60292      * // you can fake an object call by doing this
60293      *  x.t:(test,tesT) 
60294      * 
60295      */
60296     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60297
60298     /**
60299      * compile the template
60300      *
60301      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60302      *
60303      */
60304     compile: function()
60305     {
60306         var s = this.html;
60307      
60308         s = ['<tpl>', s, '</tpl>'].join('');
60309     
60310         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60311             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60312             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60313             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60314             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60315             m,
60316             id     = 0,
60317             tpls   = [];
60318     
60319         while(true == !!(m = s.match(re))){
60320             var forMatch   = m[0].match(nameRe),
60321                 ifMatch   = m[0].match(ifRe),
60322                 execMatch   = m[0].match(execRe),
60323                 namedMatch   = m[0].match(namedRe),
60324                 
60325                 exp  = null, 
60326                 fn   = null,
60327                 exec = null,
60328                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60329                 
60330             if (ifMatch) {
60331                 // if - puts fn into test..
60332                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60333                 if(exp){
60334                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60335                 }
60336             }
60337             
60338             if (execMatch) {
60339                 // exec - calls a function... returns empty if true is  returned.
60340                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60341                 if(exp){
60342                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60343                 }
60344             }
60345             
60346             
60347             if (name) {
60348                 // for = 
60349                 switch(name){
60350                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60351                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60352                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60353                 }
60354             }
60355             var uid = namedMatch ? namedMatch[1] : id;
60356             
60357             
60358             tpls.push({
60359                 id:     namedMatch ? namedMatch[1] : id,
60360                 target: name,
60361                 exec:   exec,
60362                 test:   fn,
60363                 body:   m[1] || ''
60364             });
60365             if (namedMatch) {
60366                 s = s.replace(m[0], '');
60367             } else { 
60368                 s = s.replace(m[0], '{xtpl'+ id + '}');
60369             }
60370             ++id;
60371         }
60372         this.tpls = [];
60373         for(var i = tpls.length-1; i >= 0; --i){
60374             this.compileTpl(tpls[i]);
60375             this.tpls[tpls[i].id] = tpls[i];
60376         }
60377         this.master = tpls[tpls.length-1];
60378         return this;
60379     },
60380     /**
60381      * same as applyTemplate, except it's done to one of the subTemplates
60382      * when using named templates, you can do:
60383      *
60384      * var str = pl.applySubTemplate('your-name', values);
60385      *
60386      * 
60387      * @param {Number} id of the template
60388      * @param {Object} values to apply to template
60389      * @param {Object} parent (normaly the instance of this object)
60390      */
60391     applySubTemplate : function(id, values, parent)
60392     {
60393         
60394         
60395         var t = this.tpls[id];
60396         
60397         
60398         try { 
60399             if(t.test && !t.test.call(this, values, parent)){
60400                 return '';
60401             }
60402         } catch(e) {
60403             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60404             Roo.log(e.toString());
60405             Roo.log(t.test);
60406             return ''
60407         }
60408         try { 
60409             
60410             if(t.exec && t.exec.call(this, values, parent)){
60411                 return '';
60412             }
60413         } catch(e) {
60414             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60415             Roo.log(e.toString());
60416             Roo.log(t.exec);
60417             return ''
60418         }
60419         try {
60420             var vs = t.target ? t.target.call(this, values, parent) : values;
60421             parent = t.target ? values : parent;
60422             if(t.target && vs instanceof Array){
60423                 var buf = [];
60424                 for(var i = 0, len = vs.length; i < len; i++){
60425                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60426                 }
60427                 return buf.join('');
60428             }
60429             return t.compiled.call(this, vs, parent);
60430         } catch (e) {
60431             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60432             Roo.log(e.toString());
60433             Roo.log(t.compiled);
60434             return '';
60435         }
60436     },
60437
60438     compileTpl : function(tpl)
60439     {
60440         var fm = Roo.util.Format;
60441         var useF = this.disableFormats !== true;
60442         var sep = Roo.isGecko ? "+" : ",";
60443         var undef = function(str) {
60444             Roo.log("Property not found :"  + str);
60445             return '';
60446         };
60447         
60448         var fn = function(m, name, format, args)
60449         {
60450             //Roo.log(arguments);
60451             args = args ? args.replace(/\\'/g,"'") : args;
60452             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60453             if (typeof(format) == 'undefined') {
60454                 format= 'htmlEncode';
60455             }
60456             if (format == 'raw' ) {
60457                 format = false;
60458             }
60459             
60460             if(name.substr(0, 4) == 'xtpl'){
60461                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60462             }
60463             
60464             // build an array of options to determine if value is undefined..
60465             
60466             // basically get 'xxxx.yyyy' then do
60467             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60468             //    (function () { Roo.log("Property not found"); return ''; })() :
60469             //    ......
60470             
60471             var udef_ar = [];
60472             var lookfor = '';
60473             Roo.each(name.split('.'), function(st) {
60474                 lookfor += (lookfor.length ? '.': '') + st;
60475                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60476             });
60477             
60478             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60479             
60480             
60481             if(format && useF){
60482                 
60483                 args = args ? ',' + args : "";
60484                  
60485                 if(format.substr(0, 5) != "this."){
60486                     format = "fm." + format + '(';
60487                 }else{
60488                     format = 'this.call("'+ format.substr(5) + '", ';
60489                     args = ", values";
60490                 }
60491                 
60492                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60493             }
60494              
60495             if (args.length) {
60496                 // called with xxyx.yuu:(test,test)
60497                 // change to ()
60498                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60499             }
60500             // raw.. - :raw modifier..
60501             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60502             
60503         };
60504         var body;
60505         // branched to use + in gecko and [].join() in others
60506         if(Roo.isGecko){
60507             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60508                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60509                     "';};};";
60510         }else{
60511             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60512             body.push(tpl.body.replace(/(\r\n|\n)/g,
60513                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60514             body.push("'].join('');};};");
60515             body = body.join('');
60516         }
60517         
60518         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60519        
60520         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60521         eval(body);
60522         
60523         return this;
60524     },
60525
60526     applyTemplate : function(values){
60527         return this.master.compiled.call(this, values, {});
60528         //var s = this.subs;
60529     },
60530
60531     apply : function(){
60532         return this.applyTemplate.apply(this, arguments);
60533     }
60534
60535  });
60536
60537 Roo.XTemplate.from = function(el){
60538     el = Roo.getDom(el);
60539     return new Roo.XTemplate(el.value || el.innerHTML);
60540 };